Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FullingPorter v0.1.3
FullingPorter.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FullingPorter.Contract; using FullingPorter.Core; using FullingPorter.Localization; using FullingPorter.Network; using FullingPorter.Porter; using FullingPorter.Storage; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("FullingPorter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.3.0")] [assembly: AssemblyInformationalVersion("0.1.3")] [assembly: AssemblyProduct("FullingPorter")] [assembly: AssemblyTitle("FullingPorter")] [assembly: AssemblyVersion("0.1.3.0")] namespace FullingPorter { [BepInPlugin("Likhtenvald.FullingPorter", "FullingPorter", "0.1.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "Likhtenvald.FullingPorter"; public const string PluginName = "FullingPorter"; public const string PluginVersion = "0.1.3"; public const string QuickStackPlusGuid = "Goneryx.QuickStackPlus"; internal static ManualLogSource Log; internal static ConfigEntry<int> ContractPrice; internal static ConfigEntry<float> WorkRadius; internal static ConfigEntry<int> MaxStacksPerTrip; internal static ConfigEntry<KeyCode> DismissKey; internal static ConfigEntry<KeyCode> RenameKey; internal static ConfigEntry<KeyCode> SourceChestKey; private Harmony _harmony; private void Awake() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_006f: 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_007c: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00ab: 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_00b8: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; ContractPrice = ((BaseUnityPlugin)this).Config.Bind<int>("Contract", "Price", 1500, new ConfigDescription("Haldor contract price in coins. Synchronized from the server.", (AcceptableValueBase)null, new object[1] { val })); WorkRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Porter", "WorkRadius", 30f, new ConfigDescription("Maximum work radius in metres. Synchronized from the server.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); MaxStacksPerTrip = ((BaseUnityPlugin)this).Config.Bind<int>("Porter", "MaxStacksPerTrip", 10, new ConfigDescription("Maximum distinct stacks carried per trip. Synchronized from the server.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); DismissKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Porter", "DismissKey", (KeyCode)127, "Key used while looking at a porter to dismiss it. Press twice to confirm."); RenameKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Porter", "RenameKey", (KeyCode)279, "Key used while looking at a porter to rename it."); SourceChestKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Porter", "SourceChestKey", (KeyCode)278, "Key used while looking at a container to toggle it as a porter source."); PorterRpc.Register(); _harmony = new Harmony("Likhtenvald.FullingPorter"); _harmony.PatchAll(); LocalizationRegistry.Register(); PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable; ((BaseUnityPlugin)this).Logger.LogInfo((object)"FullingPorter 0.1.3 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void OnVanillaPrefabsAvailable() { ContractRegistry.Register(); PorterPrefabRegistry.Register(); PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable; } } } namespace FullingPorter.Storage { internal static class ContainerUseGuard { internal const string PorterLockKey = "FullingPorter.TransferLock"; private static readonly HashSet<ZDOID> HeldLocks = new HashSet<ZDOID>(); internal static bool IsPlayerBusy(Container container) { if (!Object.op_Implicit((Object)(object)container)) { return true; } ZNetView component = ((Component)container).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return true; } if (container.IsInUse()) { return true; } ZDO zDO = component.GetZDO(); if (zDO != null) { return zDO.GetInt(ZDOVars.s_inUse, 0) == 1; } return true; } internal static bool IsPorterLocked(Container container) { ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null) { return val2.GetBool("FullingPorter.TransferLock", false); } return false; } internal static bool TryAcquire(Container container, out bool newlyAcquired) { //IL_0044: 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_006b: Unknown result type (might be due to invalid IL or missing references) newlyAcquired = false; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || IsPlayerBusy(container)) { return false; } ZNetView component = ((Component)container).GetComponent<ZNetView>(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val == null) { return false; } if (HeldLocks.Contains(val.m_uid)) { return true; } if (val.GetBool("FullingPorter.TransferLock", false)) { Plugin.Log.LogWarning((object)$"Clearing orphaned porter lock on container {val.m_uid}."); val.Set("FullingPorter.TransferLock", false); } val.Set("FullingPorter.TransferLock", true); HeldLocks.Add(val.m_uid); newlyAcquired = true; return true; } internal static string DescribeState(Container container) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 == null) { return "unavailable"; } return $"id={val2.m_uid}, localInUse={container.IsInUse()}, " + $"networkInUse={val2.GetInt(ZDOVars.s_inUse, 0)}, " + string.Format("porterLocked={0}, ", val2.GetBool("FullingPorter.TransferLock", false)) + $"serverHeld={HeldLocks.Contains(val2.m_uid)}"; } internal static void Release(Container container) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null && HeldLocks.Remove(val2.m_uid) && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { val2.Set("FullingPorter.TransferLock", false); } } internal static void ClearStaleLock(Container container) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null) { HeldLocks.Remove(val2.m_uid); if (val2.GetBool("FullingPorter.TransferLock", false)) { val2.Set("FullingPorter.TransferLock", false); } } } } internal static class QuickStackPlusBridge { internal const string StorageFilterZdoKey = "Goneryx.QuickStackPlus.StorageFilter"; internal const char Separator = '\u001f'; internal static HashSet<string> GetAcceptedItemIds(Container container) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if ((Object)(object)container == (Object)null) { return hashSet; } ZNetView component = ((Component)container).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return hashSet; } ZDO zDO = component.GetZDO(); if (zDO == null) { return hashSet; } string text = zDO.GetString("Goneryx.QuickStackPlus.StorageFilter", string.Empty); if (string.IsNullOrEmpty(text)) { return hashSet; } string[] array = text.Split(new char[1] { '\u001f' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in array) { hashSet.Add(item); } return hashSet; } internal static bool Accepts(Container container, ItemData item) { if ((Object)(object)item?.m_dropPrefab == (Object)null) { return false; } return GetAcceptedItemIds(container).Contains(((Object)item.m_dropPrefab).name); } } internal static class SourceContainerMarker { internal const string SourceKey = "FullingPorter.Source"; internal static bool IsSource(Container container) { ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null); if ((Object)(object)val != (Object)null && val.IsValid()) { ZDO zDO = val.GetZDO(); if (zDO == null) { return false; } return zDO.GetBool("FullingPorter.Source", false); } return false; } internal static bool TryToggleServer(Container container, out bool enabled) { enabled = false; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } ZDO zDO = val.GetZDO(); if (zDO == null) { return false; } enabled = !zDO.GetBool("FullingPorter.Source", false); zDO.Set("FullingPorter.Source", enabled); return true; } } internal static class TransferService { internal enum TransferResult { Success, WaitingForOwnership, SourceUnavailable, DestinationRejected, DestinationFull, ContainerBusy, Failed } private sealed class InventorySnapshot : IDisposable { private readonly Inventory _inventory; private readonly List<ItemData> _items; private bool _committed; internal InventorySnapshot(Inventory inventory) { //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) _inventory = inventory; _items = new List<ItemData>(); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null) { ItemData val = allItem.Clone(); val.m_stack = allItem.m_stack; val.m_gridPos = allItem.m_gridPos; _items.Add(val); } } } internal void Commit() { _committed = true; } public void Dispose() { //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) if (_committed) { return; } foreach (ItemData item in new List<ItemData>(_inventory.GetAllItems())) { _inventory.RemoveItem(item); } foreach (ItemData item2 in _items) { ItemData val = item2.Clone(); val.m_stack = item2.m_stack; val.m_gridPos = item2.m_gridPos; _inventory.AddItem(val); } } } internal static TransferResult TryMoveWholeStack(Container source, Container destination, ItemData item) { if (!Object.op_Implicit((Object)(object)source) || !Object.op_Implicit((Object)(object)destination) || item == null || (Object)(object)source == (Object)(object)destination) { return TransferResult.Failed; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return TransferResult.Failed; } if (ContainerUseGuard.IsPlayerBusy(source) || ContainerUseGuard.IsPlayerBusy(destination)) { ContainerUseGuard.Release(destination); ContainerUseGuard.Release(source); return TransferResult.ContainerBusy; } ZNetView component = ((Component)source).GetComponent<ZNetView>(); ZNetView component2 = ((Component)destination).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component.IsValid() || !component2.IsValid()) { return TransferResult.Failed; } bool newlyAcquired = false; bool newlyAcquired2 = false; if (!ContainerUseGuard.TryAcquire(source, out newlyAcquired)) { ContainerUseGuard.Release(destination); ContainerUseGuard.Release(source); return TransferResult.ContainerBusy; } if (!ContainerUseGuard.TryAcquire(destination, out newlyAcquired2)) { ContainerUseGuard.Release(destination); ContainerUseGuard.Release(source); return TransferResult.ContainerBusy; } if (newlyAcquired || newlyAcquired2) { return TransferResult.WaitingForOwnership; } try { if (ContainerUseGuard.IsPlayerBusy(source) || ContainerUseGuard.IsPlayerBusy(destination)) { return TransferResult.ContainerBusy; } if (!component.IsOwner()) { component.ClaimOwnership(); } if (!component2.IsOwner()) { component2.ClaimOwnership(); } if (!component.IsOwner() || !component2.IsOwner()) { return TransferResult.WaitingForOwnership; } if (ContainerUseGuard.IsPlayerBusy(source) || ContainerUseGuard.IsPlayerBusy(destination)) { return TransferResult.ContainerBusy; } Inventory inventory = source.GetInventory(); Inventory inventory2 = destination.GetInventory(); if (inventory == null || inventory2 == null) { return TransferResult.Failed; } ItemData val = FindMatchingSourceStack(inventory, item); if (val == null) { return TransferResult.SourceUnavailable; } if (!QuickStackPlusBridge.Accepts(destination, val)) { return TransferResult.DestinationRejected; } if (!inventory2.CanAddItem(val, -1)) { return TransferResult.DestinationFull; } using InventorySnapshot inventorySnapshot = new InventorySnapshot(inventory2); ItemData val2 = val.Clone(); val2.m_stack = val.m_stack; val2.m_equipped = false; if (!inventory2.AddItem(val2)) { return TransferResult.Failed; } if (!inventory.RemoveItem(val)) { return TransferResult.SourceUnavailable; } inventorySnapshot.Commit(); return TransferResult.Success; } finally { ContainerUseGuard.Release(destination); ContainerUseGuard.Release(source); } } internal static ItemData FindMatchingSourceStack(Inventory inventory, ItemData planned) { if (inventory == null || (Object)(object)planned?.m_dropPrefab == (Object)null) { return null; } foreach (ItemData allItem in inventory.GetAllItems()) { if ((Object)(object)allItem?.m_dropPrefab == (Object)null || allItem.m_gridPos.x != planned.m_gridPos.x || allItem.m_gridPos.y != planned.m_gridPos.y || ((Object)allItem.m_dropPrefab).name != ((Object)planned.m_dropPrefab).name || allItem.m_stack != planned.m_stack || allItem.m_quality != planned.m_quality || allItem.m_variant != planned.m_variant) { continue; } Dictionary<string, string> customData = allItem.m_customData; Dictionary<string, string> customData2 = planned.m_customData; if (customData == null || customData2 == null) { if (customData == customData2) { return allItem; } } else { if (customData.Count != customData2.Count) { continue; } bool flag = true; foreach (KeyValuePair<string, string> item in customData2) { if (!customData.TryGetValue(item.Key, out var value) || value != item.Value) { flag = false; break; } } if (flag) { return allItem; } } } return null; } } } namespace FullingPorter.Porter { internal sealed class PorterInteraction : MonoBehaviour, Interactable, TextReceiver { private const float DismissConfirmSeconds = 3f; private PorterState _state; private ZNetView _view; private PorterPersonality _personality; private float _dismissConfirmUntil; private void Awake() { _state = ((Component)this).GetComponent<PorterState>(); _view = ((Component)this).GetComponent<ZNetView>(); _personality = ((Component)this).GetComponent<PorterPersonality>(); } private void Update() { //IL_001c: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } bool num = Plugin.DismissKey != null && Input.GetKeyDown(Plugin.DismissKey.Value); bool flag = Plugin.RenameKey != null && Input.GetKeyDown(Plugin.RenameKey.Value); if (!num && !flag) { return; } GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); if (!((Object)(object)hoverObject == (Object)null) && !((Object)(object)hoverObject.GetComponentInParent<PorterInteraction>() != (Object)(object)this)) { if (flag) { RequestRename(); } else if (Time.time <= _dismissConfirmUntil) { PorterRpc.RequestDismiss(_view); _dismissConfirmUntil = 0f; } else { _dismissConfirmUntil = Time.time + 3f; ((Character)localPlayer).Message((MessageType)2, "$fullingporter_dismiss_confirm", 0, (Sprite)null, false); } } } public bool Interact(Humanoid character, bool hold, bool alt) { if (hold || (Object)(object)character != (Object)(object)Player.m_localPlayer) { return false; } if ((Object)(object)_personality != (Object)null) { return _personality.React(Player.m_localPlayer); } return false; } private void RequestRename() { if ((Object)(object)TextInput.instance != (Object)null) { TextInput.instance.RequestText((TextReceiver)(object)this, "$fullingporter_rename_title", 24); } } public bool UseItem(Humanoid user, ItemData item) { return false; } public string GetText() { return _state?.PorterName ?? string.Empty; } public void SetText(string text) { PorterRpc.RequestRename(_view, text); } } internal sealed class PorterMovementAI : BaseAI { internal void WalkTo(Vector3 point, float stopDistance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ((BaseAI)this).MoveTo(Time.deltaTime, point, stopDistance, false); } internal void Halt() { ((BaseAI)this).StopMoving(); } } internal sealed class PorterPersonality : MonoBehaviour { private static readonly string[] IdleLines = new string[6] { "$fullingporter_talk_idle_01", "$fullingporter_talk_idle_02", "$fullingporter_talk_idle_03", "$fullingporter_talk_idle_04", "$fullingporter_talk_idle_05", "$fullingporter_talk_idle_06" }; private static readonly string[] WorkingLines = new string[7] { "$fullingporter_talk_work_01", "$fullingporter_talk_work_02", "$fullingporter_talk_work_03", "$fullingporter_talk_work_05", "$fullingporter_talk_work_06", "$fullingporter_talk_work_07", "$fullingporter_talk_work_04" }; private static readonly string[] ReturningLines = new string[4] { "$fullingporter_talk_return_01", "$fullingporter_talk_return_02", "$fullingporter_talk_return_03", "$fullingporter_talk_idle_040" }; private static readonly string[] BlockedLines = new string[4] { "$fullingporter_talk_blocked_01", "$fullingporter_talk_blocked_02", "$fullingporter_talk_blocked_03", "$fullingporter_talk_idle_041" }; private static readonly string[] VoicePrefabs = new string[4] { "sfx_goblin_idle", "sfx_goblin_idle", "sfx_goblin_idle", "sfx_goblin_alerted" }; private PorterWorker _worker; private PorterState _state; private ZNetView _view; private float _nextReactionTime; private float _nextAmbientReactionTime; private const float ReactionCooldown = 2.5f; internal const float AmbientHearDistance = 18f; private const float AmbientMinInterval = 18f; private const float AmbientMaxInterval = 42f; private void Awake() { _worker = ((Component)this).GetComponent<PorterWorker>(); _state = ((Component)this).GetComponent<PorterState>(); _view = ((Component)this).GetComponent<ZNetView>(); ScheduleNextAmbient(); } private void Update() { //IL_002a: 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) //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) if (Time.time < _nextAmbientReactionTime) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { ScheduleNextAmbient(); return; } Vector3 val = ((Component)localPlayer).transform.position - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude > 324f) { _nextAmbientReactionTime = Time.time + 5f; return; } PorterRpc.RequestSpeak(_view, ambient: true); ScheduleNextAmbient(); } internal bool React(Player player) { if ((Object)(object)player == (Object)null || Time.time < _nextReactionTime) { return false; } if (!PorterRpc.RequestSpeak(_view, ambient: false)) { return false; } _nextReactionTime = Time.time + 2.5f; if (_nextAmbientReactionTime < Time.time + 8f) { _nextAmbientReactionTime = Time.time + 8f; } return true; } internal bool TrySpeakServer(bool ambient) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)_view == (Object)null || !_view.IsValid() || Time.time < _nextReactionTime || (ambient && Time.time < _nextAmbientReactionTime)) { return false; } _nextReactionTime = Time.time + 2.5f; ScheduleNextAmbient(); string line = PickLine(); int voiceIndex = Random.Range(0, VoicePrefabs.Length); PlaySpeech(line, voiceIndex); PorterRpc.BroadcastSpeech(_view, line, voiceIndex); return true; } internal void PlaySpeech(string line, int voiceIndex) { //IL_002e: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(line) && voiceIndex >= 0 && voiceIndex < VoicePrefabs.Length && !((Object)(object)Player.m_localPlayer == (Object)null)) { Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)this).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude > 400f)) { string text = LocalizationManager.Instance.TryTranslate(line); ShowSpeechBubble(Player.m_localPlayer, text); PlayVoice(voiceIndex); } } } private void ScheduleNextAmbient() { _nextAmbientReactionTime = Time.time + Random.Range(18f, 42f); } private void ShowSpeechBubble(Player player, string text) { //IL_0051: 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) if ((Object)(object)Chat.instance == (Object)null) { ((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false); return; } string text2 = (((Object)(object)_state != (Object)null) ? _state.PorterName : "$fullingporter_name"); text2 = LocalizationManager.Instance.TryTranslate(text2); Chat.instance.SetNpcText(((Component)this).gameObject, Vector3.up * 1.8f, 20f, 4f, text2, text, false); } private string PickLine() { string[] array = (((Object)(object)_worker != (Object)null) ? _worker.GetDialogueContext() : PorterDialogueContext.Idle) switch { PorterDialogueContext.Working => WorkingLines, PorterDialogueContext.Returning => ReturningLines, PorterDialogueContext.Blocked => BlockedLines, _ => IdleLines, }; return array[Random.Range(0, array.Length)]; } private void PlayVoice(int voiceIndex) { //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_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_0053: Unknown result type (might be due to invalid IL or missing references) string text = VoicePrefabs[voiceIndex]; GameObject prefab = PrefabManager.Instance.GetPrefab(text); if ((Object)(object)prefab == (Object)null) { Plugin.Log.LogDebug((object)("Porter voice prefab not found: " + text)); } else { Object.Instantiate<GameObject>(prefab, ((Component)this).transform.position + Vector3.up * 1.2f, Quaternion.identity); } } } internal static class PorterPrefabRegistry { internal const string PrefabName = "FullingPorter_Fuling"; internal static void Register() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = PrefabManager.Instance.GetPrefab("Goblin"); if ((Object)(object)prefab == (Object)null) { Plugin.Log.LogError((object)"Vanilla Goblin prefab was not found."); return; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("FullingPorter_Fuling", prefab); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"Could not clone the porter prefab from vanilla Goblin."); return; } MonsterAI component = val.GetComponent<MonsterAI>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } PorterMovementAI porterMovementAI = val.GetComponent<PorterMovementAI>(); if ((Object)(object)porterMovementAI == (Object)null) { porterMovementAI = val.AddComponent<PorterMovementAI>(); } ((BaseAI)porterMovementAI).m_pathAgentType = (AgentType)1; Character component2 = val.GetComponent<Character>(); if ((Object)(object)component2 != (Object)null) { component2.m_faction = (Faction)0; } CustomPrefab val2 = new CustomPrefab(val, true); val.AddComponent<PorterState>(); val.AddComponent<PorterWorker>(); val.AddComponent<PorterPersonality>(); val.AddComponent<PorterInteraction>(); PrefabManager.Instance.AddPrefab(val2); } } internal sealed class PorterState : MonoBehaviour, Hoverable { internal const string WorldPresenceKey = "FullingPorter.ActivePorter"; private const string WorldPresencePrefix = "FullingPorter.ActivePorter:"; private const string NameKey = "FullingPorter.Name"; private const string HomeKey = "FullingPorter.Home"; private const string HasHomeKey = "FullingPorter.HasHome"; private const string DefaultName = "$fullingporter_name"; private static PorterState _serverActive; private ZNetView _view; private Character _character; private PorterWorker _worker; private float _nextNameRefresh; private const float NameRefreshInterval = 0.5f; internal string PorterName { get { if ((Object)(object)_view == (Object)null || !_view.IsValid()) { return "$fullingporter_name"; } return _view.GetZDO().GetString("FullingPorter.Name", "$fullingporter_name"); } set { SetNameServer(value); } } private void Awake() { _view = ((Component)this).GetComponent<ZNetView>(); _character = ((Component)this).GetComponent<Character>(); _worker = ((Component)this).GetComponent<PorterWorker>(); ApplyDisplayName(); } private void Update() { if (!(Time.time < _nextNameRefresh)) { _nextNameRefresh = Time.time + 0.5f; if ((Object)(object)_character != (Object)null && _character.m_name != PorterName) { ApplyDisplayName(); } } } private void Start() { ApplyDisplayName(); if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if ((Object)(object)_serverActive != (Object)null && (Object)(object)_serverActive != (Object)(object)this) { Plugin.Log.LogWarning((object)"Duplicate porter detected on server; removing the newer instance."); if ((Object)(object)_view != (Object)null && _view.IsValid() && !_view.IsOwner()) { _view.ClaimOwnership(); } if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(((Component)this).gameObject); } } else { _serverActive = this; MarkWorldOccupied(_view); } } private void OnDestroy() { if ((Object)(object)_serverActive == (Object)(object)this) { _serverActive = null; } } internal static bool WorldHasPorter() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_serverActive != (Object)null) { return true; } PorterState[] array = Object.FindObjectsByType<PorterState>((FindObjectsSortMode)0); foreach (PorterState porterState in array) { if (!((Object)(object)porterState == (Object)null)) { _serverActive = porterState; MarkWorldOccupied(porterState._view); return true; } } ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null) { return false; } bool flag = false; List<string> list = new List<string>(); foreach (string globalKey in instance.GetGlobalKeys()) { if (globalKey == "FullingPorter.ActivePorter") { flag = true; } else if (globalKey.StartsWith("FullingPorter.ActivePorter:")) { if (TryParseWorldKey(globalKey, out var id) && ZDOMan.instance != null && ZDOMan.instance.GetZDO(id) != null) { return true; } list.Add(globalKey); } } foreach (string item in list) { instance.RemoveGlobalKey(item); } if (flag) { instance.RemoveGlobalKey("FullingPorter.ActivePorter"); } return false; } internal static void MarkWorldOccupied(ZNetView view) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null); if (!((Object)(object)instance == (Object)null) && val != null) { if (instance.GetGlobalKey("FullingPorter.ActivePorter")) { instance.RemoveGlobalKey("FullingPorter.ActivePorter"); } string text = MakeWorldKey(val.m_uid); if (!instance.GetGlobalKey(text)) { instance.SetGlobalKey(text); } } } internal static void ClearWorldOccupied() { ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null) { return; } foreach (string item in new List<string>(instance.GetGlobalKeys())) { if (item == "FullingPorter.ActivePorter" || item.StartsWith("FullingPorter.ActivePorter:")) { instance.RemoveGlobalKey(item); } } _serverActive = null; } private static string MakeWorldKey(ZDOID id) { return "FullingPorter.ActivePorter:" + ((ZDOID)(ref id)).UserID.ToString(CultureInfo.InvariantCulture) + ":" + ((ZDOID)(ref id)).ID.ToString(CultureInfo.InvariantCulture); } private static bool TryParseWorldKey(string key, out ZDOID id) { //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_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) id = ZDOID.None; if (string.IsNullOrEmpty(key) || !key.StartsWith("FullingPorter.ActivePorter:")) { return false; } string[] array = key.Substring("FullingPorter.ActivePorter:".Length).Split(new char[1] { ':' }); if (array.Length != 2) { return false; } if (!long.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !uint.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return false; } id = new ZDOID(result, result2); return true; } internal Vector3 GetOrCreateHome(Vector3 fallback) { //IL_001b: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0088: 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) if ((Object)(object)_view == (Object)null || !_view.IsValid()) { return fallback; } ZDO zDO = _view.GetZDO(); if (zDO == null) { return fallback; } if (zDO.GetBool("FullingPorter.HasHome", false)) { return zDO.GetVec3("FullingPorter.Home", fallback); } if (!_view.IsOwner()) { _view.ClaimOwnership(); } if (!_view.IsOwner()) { return fallback; } zDO.Set("FullingPorter.Home", fallback); zDO.Set("FullingPorter.HasHome", true); return fallback; } internal bool SetNameServer(string value) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)_view == (Object)null || !_view.IsValid()) { return false; } ZDO zDO = _view.GetZDO(); if (zDO == null) { return false; } zDO.Set("FullingPorter.Name", string.IsNullOrWhiteSpace(value) ? "$fullingporter_name" : value.Trim()); ApplyDisplayName(); return true; } private void ApplyDisplayName() { if ((Object)(object)_character != (Object)null) { _character.m_name = PorterName; } } internal string BuildHoverText() { string value = (((Object)(object)_worker != (Object)null) ? _worker.GetStatusText() : "$fullingporter_status_idle"); string text = TranslateTokenWithSuffix(PorterName); string text2 = TranslateTokenWithSuffix(value); return text + "\n" + text2; } private static string TranslateTokenWithSuffix(string value) { if (string.IsNullOrEmpty(value) || value[0] != '$') { return value; } int num = value.IndexOf(" ("); if (num < 0) { return LocalizationManager.Instance.TryTranslate(value); } string text = value.Substring(0, num); return LocalizationManager.Instance.TryTranslate(text) + value.Substring(num); } public string GetHoverName() { return PorterName; } public float GetHoverOffset() { return 1.5f; } public string GetHoverText() { return BuildHoverText(); } } internal sealed class PorterWorker : MonoBehaviour { private sealed class CargoEntry { internal ItemData Item; internal Container Destination; internal int TransferFailures; } private Character _character; private PorterMovementAI _ai; private ZNetView _view; private PorterWorkState _state; private Container _source; private readonly List<CargoEntry> _cargo = new List<CargoEntry>(); private readonly Dictionary<Container, Dictionary<string, float>> _destinationCooldowns = new Dictionary<Container, Dictionary<string, float>>(); private Vector3 _home; private float _nextScan; private Vector3 _moveTarget; private Vector3 _lastProgressPosition; private float _lastProgressTime; private bool _trackingMove; private float _nextTransferTime; private bool _homeInitialized; private int _activeTripCapacity; private ItemData _ownershipWaitItem; private float _ownershipWaitSince; private float _containerBusySince = -1f; private float _playerBusySince = -1f; private Container _activeDestination; private bool _returningFromWork; private float _blockedStatusUntil; private float _blockedDialogueUntil; private const string StatusKey = "FullingPorter.Status"; private const string DialogueContextKey = "FullingPorter.DialogueContext"; private const float InteractionDistance = 2.5f; private const float HomeDistance = 0.35f; private const float ScanInterval = 2f; private const float StuckTimeout = 8f; private const float ProgressDistance = 0.25f; private const float TransferInterval = 0.12f; private const float DestinationRetryCooldown = 8f; private const float BlockedDialogueDuration = 20f; private const float OwnershipWaitTimeout = 3f; private const float ContainerBusyTimeout = 5f; private const float PlayerBusyTimeout = 30f; private const int MaxTransferFailures = 3; private const float TransferFailureRetryDelay = 0.3f; private void Awake() { //IL_002b: 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_004a: Unknown result type (might be due to invalid IL or missing references) _character = ((Component)this).GetComponent<Character>(); _ai = ((Component)this).GetComponent<PorterMovementAI>(); _view = ((Component)this).GetComponent<ZNetView>(); _home = ((Component)this).transform.position; if ((Object)(object)_character != (Object)null) { _character.m_faction = (Faction)0; } } private void Update() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_0093: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_view == (Object)null || !_view.IsValid() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if (!_view.IsOwner()) { _view.ClaimOwnership(); } if (_view.IsOwner()) { if (!_homeInitialized) { PorterState component = ((Component)this).GetComponent<PorterState>(); _home = (((Object)(object)component != (Object)null) ? component.GetOrCreateHome(((Component)this).transform.position) : ((Component)this).transform.position); _homeInitialized = true; } switch (_state) { case PorterWorkState.Idle: TickIdle(); break; case PorterWorkState.ToSource: TickToSource(); break; case PorterWorkState.ToDestination: TickToDestination(); break; case PorterWorkState.ReturningHome: TickReturningHome(); break; } PublishPresentation(); } } private void TickIdle() { //IL_0035: 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_0040: 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) if (Time.time < _nextScan) { return; } _nextScan = Time.time + 2f; if (TryFindBatch()) { _state = PorterWorkState.ToSource; return; } Vector3 val = ((Component)this).transform.position - _home; if (((Vector3)(ref val)).sqrMagnitude > 0.122499995f) { _returningFromWork = false; _state = PorterWorkState.ReturningHome; } } private void TickToSource() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!IsUsable(_source)) { AbortBatch(); return; } if (ContainerUseGuard.IsPlayerBusy(_source)) { _nextTransferTime = Time.time + 0.3f; return; } if (!MoveTowards(((Component)_source).transform.position, 2.5f, out var stuck)) { if (stuck) { AbortBatch(); } return; } Inventory inventory = _source.GetInventory(); if (inventory == null) { AbortBatch(); return; } for (int num = _cargo.Count - 1; num >= 0; num--) { CargoEntry cargoEntry = _cargo[num]; Inventory val = (IsUsable(cargoEntry.Destination) ? cargoEntry.Destination.GetInventory() : null); if (cargoEntry.Item == null || TransferService.FindMatchingSourceStack(inventory, cargoEntry.Item) == null || val == null || !QuickStackPlusBridge.Accepts(cargoEntry.Destination, cargoEntry.Item) || !val.CanAddItem(cargoEntry.Item, -1)) { Plugin.Log.LogDebug((object)("Porter skipped unavailable cargo during source revalidation: " + DescribeItem(cargoEntry.Item) + ".")); _cargo.RemoveAt(num); } } if (_cargo.Count == 0) { Plugin.Log.LogDebug((object)"Porter trip has no valid cargo after source revalidation."); FinishBatch(); } else { Plugin.Log.LogDebug((object)$"Porter collected {_cargo.Count} stack(s) from source and is heading to destination."); _state = PorterWorkState.ToDestination; } } private void TickToDestination() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if (!IsUsable(_source) || _cargo.Count == 0) { FinishBatch(); return; } if (!HasCargoForDestination(_activeDestination)) { _activeDestination = FindNearestDestination(); } int num = FindCargoIndexForDestination(_activeDestination); if (num < 0) { FinishBatch(); return; } CargoEntry cargoEntry = _cargo[num]; if (!IsUsable(cargoEntry.Destination) || cargoEntry.Item == null) { RemoveCargoEntryAndContinue(num); return; } if (ContainerUseGuard.IsPlayerBusy(_source) || ContainerUseGuard.IsPlayerBusy(cargoEntry.Destination)) { WaitForPlayerContainer(cargoEntry); return; } ResetPlayerBusyWait(); if (!MoveTowards(((Component)cargoEntry.Destination).transform.position, 2.5f, out var stuck)) { if (stuck) { Plugin.Log.LogDebug((object)"Porter could not reach a destination; skipping this stack."); RemoveCargoEntryAndContinue(num); } } else { if (Time.time < _nextTransferTime) { return; } _nextTransferTime = Time.time + 0.12f; TransferService.TransferResult transferResult = TransferService.TryMoveWholeStack(_source, cargoEntry.Destination, cargoEntry.Item); switch (transferResult) { case TransferService.TransferResult.ContainerBusy: if (ContainerUseGuard.IsPlayerBusy(_source) || ContainerUseGuard.IsPlayerBusy(cargoEntry.Destination)) { WaitForPlayerContainer(cargoEntry); } else { WaitForBusyContainer(cargoEntry); } return; case TransferService.TransferResult.WaitingForOwnership: if (_containerBusySince >= 0f && Time.time - _containerBusySince >= 5f) { Plugin.Log.LogWarning((object)("Porter container access did not stabilize for " + DescribeItem(cargoEntry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(cargoEntry.Destination) + "]. Returning home.")); AbortBatch(); } else if (_ownershipWaitItem != cargoEntry.Item) { _ownershipWaitItem = cargoEntry.Item; _ownershipWaitSince = Time.time; Plugin.Log.LogDebug((object)("Porter waiting for container ownership for " + DescribeItem(cargoEntry.Item) + ".")); } else if (!(Time.time - _ownershipWaitSince < 3f)) { Plugin.Log.LogWarning((object)("Porter ownership wait timed out for " + DescribeItem(cargoEntry.Item) + "; skipping this stack.")); ResetOwnershipWait(); RemoveCargoEntryAndContinue(num); } return; } ResetOwnershipWait(); ResetContainerBusyWait(); ResetPlayerBusyWait(); switch (transferResult) { case TransferService.TransferResult.Success: ClearDestinationCooldown(cargoEntry.Destination, cargoEntry.Item); Plugin.Log.LogDebug((object)("Porter moved " + DescribeItem(cargoEntry.Item) + " successfully.")); break; case TransferService.TransferResult.DestinationFull: SetDestinationCooldown(cargoEntry.Destination, cargoEntry.Item); Plugin.Log.LogDebug((object)("Porter destination is full for " + DescribeItem(cargoEntry.Item) + "; cooldown started.")); break; case TransferService.TransferResult.DestinationRejected: Plugin.Log.LogDebug((object)("Porter destination no longer accepts " + DescribeItem(cargoEntry.Item) + ".")); break; case TransferService.TransferResult.SourceUnavailable: Plugin.Log.LogDebug((object)("Porter source no longer contains " + DescribeItem(cargoEntry.Item) + ".")); break; default: cargoEntry.TransferFailures++; if (PorterRules.ShouldRetryTransfer(cargoEntry.TransferFailures, 3)) { _nextTransferTime = Time.time + 0.3f; Plugin.Log.LogDebug((object)("Porter transfer failed transiently for " + DescribeItem(cargoEntry.Item) + "; retry " + $"{cargoEntry.TransferFailures}/{2} at the same destination.")); return; } Plugin.Log.LogWarning((object)$"Porter transfer failed {3} times for {DescribeItem(cargoEntry.Item)}; skipping this stack."); break; } RemoveCargoEntryAndContinue(num); } } private void TickReturningHome() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) if (MoveTowards(_home, 0.35f, out var stuck) || stuck) { _ai?.Halt(); ResetMoveTracking(); _state = PorterWorkState.Idle; _returningFromWork = false; _nextScan = 0f; } } private bool TryFindBatch() { //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) //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_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) ClearBatch(); CleanupDestinationCooldowns(); float value = Plugin.WorkRadius.Value; float num = value * value; int num2 = (_activeTripCapacity = Mathf.Max(1, Plugin.MaxStacksPerTrip.Value)); Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); Dictionary<Container, HashSet<string>> acceptedIds = new Dictionary<Container, HashSet<string>>(); Dictionary<Container, Inventory> planningInventories = new Dictionary<Container, Inventory>(); List<Container> list = new List<Container>(); Container[] array2 = array; foreach (Container val in array2) { if (IsUsable(val) && !ContainerUseGuard.IsPlayerBusy(val) && SourceContainerMarker.IsSource(val)) { Vector3 val2 = ((Component)val).transform.position - _home; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { list.Add(val); } } } list.Sort(delegate(Container a, Container b) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_001b: 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_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_0041: Unknown result type (might be due to invalid IL or missing references) Vector3 val5 = ((Component)a).transform.position - ((Component)this).transform.position; float sqrMagnitude = ((Vector3)(ref val5)).sqrMagnitude; val5 = ((Component)b).transform.position - ((Component)this).transform.position; return sqrMagnitude.CompareTo(((Vector3)(ref val5)).sqrMagnitude); }); foreach (Container item in list) { Inventory inventory = item.GetInventory(); List<ItemData> list2 = ((inventory != null) ? inventory.GetAllItems() : null); if (list2 == null || list2.Count == 0) { continue; } foreach (ItemData item2 in list2) { Container val3 = FindDestination(item2, item, num, array, acceptedIds, planningInventories); if (!((Object)(object)val3 == (Object)null)) { ItemData val4 = item2.Clone(); val4.m_gridPos = item2.m_gridPos; val4.m_stack = item2.m_stack; _cargo.Add(new CargoEntry { Item = val4, Destination = val3 }); if (_cargo.Count >= num2) { break; } } } if (_cargo.Count != 0) { _source = item; Plugin.Log.LogDebug((object)$"Porter planned trip with {_cargo.Count}/{num2} stack(s) from nearest source {((Object)item).name}."); return true; } } return false; } private Container FindDestination(ItemData item, Container source, float radiusSqr, Container[] containers, Dictionary<Container, HashSet<string>> acceptedIds, Dictionary<Container, Inventory> planningInventories) { //IL_0075: 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_0080: 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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item?.m_dropPrefab == (Object)null) { return null; } Container val = null; float num = float.MaxValue; string name = ((Object)item.m_dropPrefab).name; foreach (Container val2 in containers) { if (!IsUsable(val2) || ContainerUseGuard.IsPlayerBusy(val2) || (Object)(object)val2 == (Object)(object)source || SourceContainerMarker.IsSource(val2)) { continue; } Vector3 val3 = ((Component)val2).transform.position - _home; if (((Vector3)(ref val3)).sqrMagnitude > radiusSqr) { continue; } if (!acceptedIds.TryGetValue(val2, out var value)) { value = (acceptedIds[val2] = QuickStackPlusBridge.GetAcceptedItemIds(val2)); } if (!value.Contains(name) || IsDestinationCoolingDown(val2, name)) { continue; } Inventory planningInventory = GetPlanningInventory(val2, planningInventories); if (planningInventory == null) { continue; } if (!planningInventory.CanAddItem(item, -1)) { SetDestinationCooldown(val2, item); continue; } val3 = ((Component)source).transform.position - ((Component)val2).transform.position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { val = val2; num = sqrMagnitude; } } if ((Object)(object)val == (Object)null) { return null; } Inventory planningInventory2 = GetPlanningInventory(val, planningInventories); if (planningInventory2 == null) { return null; } ItemData val4 = item.Clone(); val4.m_stack = item.m_stack; val4.m_equipped = false; if (!planningInventory2.AddItem(val4)) { Plugin.Log.LogDebug((object)("Porter could not reserve planned capacity for " + DescribeItem(item) + " in " + ((Object)val).name + ".")); return null; } return val; } private static Inventory GetPlanningInventory(Container container, Dictionary<Container, Inventory> planningInventories) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown if ((Object)(object)container == (Object)null) { return null; } if (planningInventories.TryGetValue(container, out var value)) { return value; } Inventory inventory = container.GetInventory(); if (inventory == null) { return null; } Inventory val = new Inventory("FullingPorter_Planning", (Sprite)null, inventory.GetWidth(), inventory.GetHeight()); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null) { ItemData val2 = allItem.Clone(); val2.m_stack = allItem.m_stack; val2.m_equipped = false; if (!val.AddItem(val2)) { Plugin.Log.LogDebug((object)("Porter could not mirror inventory for planning: " + ((Object)container).name + ".")); return null; } } } planningInventories[container] = val; return val; } internal PorterDialogueContext GetDialogueContext() { if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { ZDO val = (((Object)(object)_view != (Object)null && _view.IsValid()) ? _view.GetZDO() : null); int num = ((val != null) ? val.GetInt("FullingPorter.DialogueContext", 0) : 0); if (num < 0 || num > 3) { return PorterDialogueContext.Idle; } return (PorterDialogueContext)num; } return PorterRules.GetDialogueContext(_state, _returningFromWork, Time.time < _blockedDialogueUntil); } internal string GetStatusText() { if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { ZDO obj = (((Object)(object)_view != (Object)null && _view.IsValid()) ? _view.GetZDO() : null); return ((obj != null) ? obj.GetString("FullingPorter.Status", "$fullingporter_status_idle") : null) ?? "$fullingporter_status_idle"; } int capacity = ((_activeTripCapacity > 0) ? _activeTripCapacity : Mathf.Max(1, Plugin.MaxStacksPerTrip.Value)); return PorterRules.GetStatusText(_state, _returningFromWork, Time.time < _blockedStatusUntil, _cargo.Count, capacity); } private void PublishPresentation() { ZDO zDO = _view.GetZDO(); if (zDO != null) { string statusText = GetStatusText(); if (zDO.GetString("FullingPorter.Status", string.Empty) != statusText) { zDO.Set("FullingPorter.Status", statusText); } int dialogueContext = (int)GetDialogueContext(); if (zDO.GetInt("FullingPorter.DialogueContext", -1) != dialogueContext) { zDO.Set("FullingPorter.DialogueContext", dialogueContext); } } } private bool IsDestinationCoolingDown(Container container, string itemId) { if ((Object)(object)container == (Object)null || string.IsNullOrEmpty(itemId)) { return false; } if (!_destinationCooldowns.TryGetValue(container, out var value)) { return false; } if (!value.TryGetValue(itemId, out var value2)) { return false; } if (Time.time < value2) { return true; } value.Remove(itemId); if (value.Count == 0) { _destinationCooldowns.Remove(container); } return false; } private void SetDestinationCooldown(Container container, ItemData item) { object obj; if (item == null) { obj = null; } else { GameObject dropPrefab = item.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } string text = (string)obj; if (!((Object)(object)container == (Object)null) && !string.IsNullOrEmpty(text)) { if (!_destinationCooldowns.TryGetValue(container, out var value)) { value = new Dictionary<string, float>(); _destinationCooldowns[container] = value; } float num = (value[text] = Time.time + 8f); if (num > _blockedStatusUntil) { _blockedStatusUntil = num; } float num3 = Time.time + 20f; if (num3 > _blockedDialogueUntil) { _blockedDialogueUntil = num3; } } } private void ClearDestinationCooldown(Container container, ItemData item) { object obj; if (item == null) { obj = null; } else { GameObject dropPrefab = item.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } string text = (string)obj; if (!((Object)(object)container == (Object)null) && !string.IsNullOrEmpty(text) && _destinationCooldowns.TryGetValue(container, out var value)) { value.Remove(text); if (value.Count == 0) { _destinationCooldowns.Remove(container); } } } private void RemoveCargoEntryAndContinue(int index) { if (index >= 0 && index < _cargo.Count) { ContainerUseGuard.Release(_cargo[index].Destination); _cargo.RemoveAt(index); } ContainerUseGuard.Release(_source); _activeDestination = null; ResetOwnershipWait(); ResetContainerBusyWait(); ResetPlayerBusyWait(); if (_cargo.Count == 0) { FinishBatch(); } } private Container FindNearestDestination() { //IL_0031: 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_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) Container result = null; float num = float.MaxValue; foreach (CargoEntry item in _cargo) { Container destination = item.Destination; if (IsUsable(destination)) { Vector3 val = ((Component)this).transform.position - ((Component)destination).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { result = destination; num = sqrMagnitude; } } } return result; } private bool HasCargoForDestination(Container destination) { if ((Object)(object)destination == (Object)null) { return false; } for (int i = 0; i < _cargo.Count; i++) { if ((Object)(object)_cargo[i].Destination == (Object)(object)destination) { return true; } } return false; } private int FindCargoIndexForDestination(Container destination) { if ((Object)(object)destination == (Object)null) { return -1; } for (int i = 0; i < _cargo.Count; i++) { if ((Object)(object)_cargo[i].Destination == (Object)(object)destination) { return i; } } return -1; } private void CleanupDestinationCooldowns() { if (_destinationCooldowns.Count == 0) { return; } List<Container> list = new List<Container>(); foreach (KeyValuePair<Container, Dictionary<string, float>> destinationCooldown in _destinationCooldowns) { Container key = destinationCooldown.Key; Dictionary<string, float> value = destinationCooldown.Value; if (!Object.op_Implicit((Object)(object)key) || value == null) { list.Add(key); continue; } List<string> list2 = new List<string>(); foreach (KeyValuePair<string, float> item in value) { if (Time.time >= item.Value) { list2.Add(item.Key); } } foreach (string item2 in list2) { value.Remove(item2); } if (value.Count == 0) { list.Add(key); } } foreach (Container item3 in list) { _destinationCooldowns.Remove(item3); } } private bool MoveTowards(Vector3 point, float stopDistance, out bool stuck) { //IL_0009: 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_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_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) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_00a5: 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_00c2: Unknown result type (might be due to invalid IL or missing references) stuck = false; if (DistanceXZ(((Component)this).transform.position, point) <= stopDistance) { _ai?.Halt(); ResetMoveTracking(); return true; } if ((Object)(object)_ai == (Object)null) { Plugin.Log.LogWarning((object)"Porter movement AI is missing."); stuck = true; return false; } if (!_trackingMove || DistanceXZ(_moveTarget, point) > 0.5f) { _trackingMove = true; _moveTarget = point; _lastProgressPosition = ((Component)this).transform.position; _lastProgressTime = Time.time; } else if (DistanceXZ(_lastProgressPosition, ((Component)this).transform.position) >= 0.25f) { _lastProgressPosition = ((Component)this).transform.position; _lastProgressTime = Time.time; } else if (Time.time - _lastProgressTime >= 8f) { _ai.Halt(); ResetMoveTracking(); stuck = true; return false; } _ai.WalkTo(point, stopDistance); return false; } private static float DistanceXZ(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } private void ResetMoveTracking() { //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_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) _trackingMove = false; _moveTarget = Vector3.zero; _lastProgressPosition = Vector3.zero; _lastProgressTime = 0f; } private static bool IsUsable(Container c) { if (!Object.op_Implicit((Object)(object)c)) { return false; } ZNetView component = ((Component)c).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null) { return component.IsValid(); } return false; } private void FinishBatch() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogDebug((object)"Porter trip complete; returning home."); ClearBatch(); Vector3 val = ((Component)this).transform.position - _home; if (((Vector3)(ref val)).sqrMagnitude > 0.122499995f) { _returningFromWork = true; _state = PorterWorkState.ReturningHome; } else { _state = PorterWorkState.Idle; _nextScan = Time.time + 2f; } } private void AbortBatch() { ClearBatch(); _returningFromWork = true; _state = PorterWorkState.ReturningHome; } private static string DescribeItem(ItemData item) { if (item == null) { return "<null item>"; } string arg = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : (item.m_shared?.m_name ?? "<unknown>")); return $"{arg} x{item.m_stack}"; } private void WaitForPlayerContainer(CargoEntry entry) { if (_playerBusySince < 0f) { _playerBusySince = Time.time; Plugin.Log.LogDebug((object)("Porter waiting for player to close a container for " + DescribeItem(entry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(entry.Destination) + "].")); } ContainerUseGuard.Release(entry.Destination); ContainerUseGuard.Release(_source); ResetOwnershipWait(); ResetContainerBusyWait(); if (Time.time - _playerBusySince >= 30f) { Plugin.Log.LogWarning((object)("Porter player container wait timed out for " + DescribeItem(entry.Item) + "; returning home.")); AbortBatch(); } else { _nextTransferTime = Time.time + 0.3f; } } private void WaitForBusyContainer(CargoEntry entry) { if (_containerBusySince < 0f) { _containerBusySince = Time.time; Plugin.Log.LogWarning((object)("Porter waiting for container access for " + DescribeItem(entry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(entry.Destination) + "].")); } ContainerUseGuard.Release(entry.Destination); ContainerUseGuard.Release(_source); ResetOwnershipWait(); if (Time.time - _containerBusySince >= 5f) { Plugin.Log.LogWarning((object)("Porter container wait timed out for " + DescribeItem(entry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(entry.Destination) + "]. Returning home.")); AbortBatch(); } else { _nextTransferTime = Time.time + 0.3f; } } private void ResetContainerBusyWait() { _containerBusySince = -1f; } private void ResetPlayerBusyWait() { _playerBusySince = -1f; } private void ResetOwnershipWait() { _ownershipWaitItem = null; _ownershipWaitSince = 0f; } private void ClearBatch() { _ai?.Halt(); ResetMoveTracking(); ContainerUseGuard.Release(_source); foreach (CargoEntry item in _cargo) { ContainerUseGuard.Release(item?.Destination); } _source = null; _cargo.Clear(); _nextTransferTime = 0f; _activeTripCapacity = 0; _activeDestination = null; ResetOwnershipWait(); ResetContainerBusyWait(); ResetPlayerBusyWait(); } } } namespace FullingPorter.Patches { [HarmonyPatch(typeof(Player), "Update")] internal static class ContainerInteractionPatch { private static void Postfix(Player __instance) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Plugin.SourceChestKey != null && Input.GetKeyDown(Plugin.SourceChestKey.Value)) { GameObject hoverObject = ((Humanoid)__instance).GetHoverObject(); Container val = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<Container>() : null); if (!((Object)(object)val == (Object)null)) { PorterRpc.RequestToggleSource(val); } } } } [HarmonyPatch(typeof(Container), "GetHoverText")] internal static class ContainerHoverTextPatch { private static void Postfix(Container __instance, ref string __result) { if (SourceContainerMarker.IsSource(__instance)) { string text = LocalizationManager.Instance.TryTranslate("$fullingporter_source_hover"); __result = __result + "\n<color=yellow>" + text + "</color>"; } } } [HarmonyPatch(typeof(Character), "GetHoverText")] internal static class PorterCharacterHoverTextPatch { private static void Postfix(Character __instance, ref string __result) { PorterState porterState = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent<PorterState>() : null); if (!((Object)(object)porterState == (Object)null)) { __result = porterState.BuildHoverText(); } } } [HarmonyPatch(typeof(Container), "Interact")] internal static class PorterContainerTransferLockPatch { private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result) { if (hold || (Object)(object)character != (Object)(object)Player.m_localPlayer || !ContainerUseGuard.IsPorterLocked(__instance)) { return true; } Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "$msg_inuse", 0, (Sprite)null, false); } __result = false; return false; } } [HarmonyPatch(typeof(Container), "Awake")] internal static class PorterContainerStaleLockPatch { private static void Postfix(Container __instance) { ContainerUseGuard.ClearStaleLock(__instance); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class PorterDamagePatch { private static bool Prefix(Character __instance) { if (!((Object)(object)__instance == (Object)null)) { return (Object)(object)((Component)__instance).GetComponent<PorterState>() == (Object)null; } return true; } } [HarmonyPatch(typeof(EnemyHud), "ShowHud")] internal static class PorterEnemyHudPatch { private static bool Prefix(Character c) { if (!((Object)(object)c == (Object)null)) { return (Object)(object)((Component)c).GetComponent<PorterState>() == (Object)null; } return true; } } [HarmonyPatch(typeof(ZRoutedRpc), "RPC_RoutedRPC")] internal static class PorterRoutedRpcGuardPatch { private static readonly int PorterActionsHash = StringExtensionMethods.GetStableHashCode("Likhtenvald.FullingPorter!PorterActions"); [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(ZRpc rpc, ZPackage pkg) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || rpc == null || pkg == null) { return true; } int pos = pkg.GetPos(); try { pkg.ReadLong(); long num = pkg.ReadLong(); pkg.ReadLong(); pkg.ReadZDOID(); if (pkg.ReadInt() != PorterActionsHash) { pkg.SetPos(pos); return true; } ZNetPeer val = FindPeerForRpc(rpc); if (val == null || !val.IsReady()) { Plugin.Log.LogWarning((object)"Dropped FullingPorter RPC from unresolved or unready connection."); return false; } if (!PorterServerRequestRules.IsAuthenticatedSender(num, val.m_uid)) { Plugin.Log.LogWarning((object)$"Dropped forged FullingPorter RPC: claimed peer {num}, actual peer {val.m_uid}."); return false; } pkg.SetPos(pos); return true; } catch { pkg.SetPos(pos); return true; } } private static ZNetPeer FindPeerForRpc(ZRpc rpc) { if (rpc == null || (Object)(object)ZNet.instance == (Object)null) { return null; } List<ZNetPeer> peers = ZNet.instance.GetPeers(); if (peers == null) { return null; } foreach (ZNetPeer item in peers) { if (item != null && item.m_rpc == rpc) { return item; } } return null; } } } namespace FullingPorter.Network { internal static class PorterRpc { private enum ActionCode { Spawn = 1, ToggleSource, Dismiss, Rename, SpeakRequest, SpeechEvent } private static CustomRPC _rpc; private static bool _spawnPending; private static Inventory _pendingContractInventory; private static ItemData _pendingContractItem; private static readonly MethodInfo GetServerPeerIdMethod = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null); private static readonly FieldInfo RoutedRpcIdField = AccessTools.Field(typeof(ZRoutedRpc), "m_id"); private static readonly Dictionary<long, float> LastActionAt = new Dictionary<long, float>(); internal static void Register() { //IL_0011: 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_0027: Expected O, but got Unknown //IL_0027: Expected O, but got Unknown _rpc = NetworkManager.Instance.AddRPC("PorterActions", new CoroutineHandler(ServerReceive), new CoroutineHandler(ClientReceive)); } internal static void RequestSpawn(Vector3 position, Vector3 forward, Inventory inventory, ItemData contractItem) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (_spawnPending || inventory == null || contractItem == null) { return; } _pendingContractInventory = inventory; _pendingContractItem = contractItem; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { CompleteLocalSpawn(TrySpawnServer(position, forward)); return; } if (_rpc == null || ZRoutedRpc.instance == null) { ClearPendingSpawn(); return; } long serverPeerId = GetServerPeerId(); if (serverPeerId == 0L) { Plugin.Log.LogWarning((object)"Porter spawn request skipped because the server peer ID could not be resolved."); ClearPendingSpawn(); return; } _spawnPending = true; ZPackage val = new ZPackage(); val.Write(1); val.Write(forward); _rpc.SendPackage(serverPeerId, val); } internal static void RequestToggleSource(Container container) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (_rpc == null || ZRoutedRpc.instance == null || val2 == null) { return; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { if (SourceContainerMarker.TryToggleServer(container, out var enabled)) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, enabled ? "$fullingporter_source_enabled" : "$fullingporter_source_disabled", 0, (Sprite)null, false); } } return; } long serverPeerId = GetServerPeerId(); if (serverPeerId != 0L) { ZPackage val3 = new ZPackage(); val3.Write(2); val3.Write(val2.m_uid); _rpc.SendPackage(serverPeerId, val3); } } internal static void RequestDismiss(ZNetView view) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null); if (_rpc == null || ZRoutedRpc.instance == null || val == null) { return; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { if (TryDismissServer(((Component)view).gameObject)) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "$fullingporter_dismissed", 0, (Sprite)null, false); } } return; } long serverPeerId = GetServerPeerId(); if (serverPeerId != 0L) { ZPackage val2 = new ZPackage(); val2.Write(3); val2.Write(val.m_uid); _rpc.SendPackage(serverPeerId, val2); } } internal static void RequestRename(ZNetView view, string name) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null); if (_rpc == null || ZRoutedRpc.instance == null || val == null) { return; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { ((Component)view).GetComponent<PorterState>()?.SetNameServer(name); return; } long serverPeerId = GetServerPeerId(); if (serverPeerId != 0L) { ZPackage val2 = new ZPackage(); val2.Write(4); val2.Write(val.m_uid); val2.Write(name ?? string.Empty); _rpc.SendPackage(serverPeerId, val2); } } internal static bool RequestSpeak(ZNetView view, bool ambient) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null); if (_rpc == null || ZRoutedRpc.instance == null || val == null) { return false; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return ((Component)view).GetComponent<PorterPersonality>()?.TrySpeakServer(ambient) ?? false; } long serverPeerId = GetServerPeerId(); if (serverPeerId == 0L) { return false; } ZPackage val2 = new ZPackage(); val2.Write(5); val2.Write(val.m_uid); val2.Write(ambient); _rpc.SendPackage(serverPeerId, val2); return true; } internal static void BroadcastSpeech(ZNetView view, string line, int voiceIndex) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_rpc != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)view == (Object)null) && view.IsValid()) { ZPackage val = NewResult(ActionCode.SpeechEvent, success: true); val.Write(view.GetZDO().m_uid); val.Write(line); val.Write(voiceIndex); _rpc.SendPackage(ZNet.instance.GetPeers(), val); } } private static long GetServerPeerId() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null) { return 0L; } try { if (GetServerPeerIdMethod != null && GetServerPeerIdMethod.Invoke(instance, null) is long num && num != 0L) { return num; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && RoutedRpcIdField != null && RoutedRpcIdField.GetValue(instance) is long num2 && num2 != 0L) { return num2; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not resolve Valheim server peer ID: " + ex.GetBaseException().Message)); return 0L; } Plugin.Log.LogWarning((object)"Valheim server peer ID is unavailable on this build."); return 0L; } private static IEnumerator ServerReceive(long sender, ZPackage pkg) { switch ((ActionCode)pkg.ReadInt()) { case ActionCode.Spawn: HandleSpawn(sender, pkg); break; case ActionCode.ToggleSource: HandleToggleSource(sender, pkg); break; case ActionCode.Dismiss: yield return HandleDismiss(sender, pkg); yield break; case ActionCode.Rename: HandleRename(sender, pkg); break; case ActionCode.SpeakRequest: HandleSpeak(sender, pkg); break; } yield return null; } private static IEnumerator ClientReceive(long sender, ZPackage pkg) { ActionCode actionCode = (ActionCode)pkg.ReadInt(); bool flag = pkg.ReadBool(); switch (actionCode) { case ActionCode.Spawn: CompleteLocalSpawn(flag); break; case ActionCode.ToggleSource: if (flag) { bool flag2 = pkg.ReadBool(); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, flag2 ? "$fullingporter_source_enabled" : "$fullingporter_source_disabled", 0, (Sprite)null, false); } } break; case ActionCode.Dismiss: if (flag) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "$fullingporter_dismissed", 0, (Sprite)null, false); } } break; case ActionCode.SpeechEvent: if (flag && sender == GetServerPeerId()) { GameObject obj = FindObject(pkg.ReadZDOID()); string line = pkg.ReadString(); int voiceIndex = pkg.ReadInt(); if (obj != null) { obj.GetComponent<PorterPersonality>()?.PlaySpeech(line, voiceIndex); } } break; } yield return null; } private static void HandleSpawn(long sender, ZPackage pkg) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0027: Unknown result type (might be due to invalid IL or missing references) if (!TryResolveRemoteSender(sender, out var _, out var position) || !TryConsumeActionBudget(sender)) { SendResult(sender, ActionCode.Spawn, success: false); return; } Vector3 forward = pkg.ReadVector3(); SendResult(sender, ActionCode.Spawn, TrySpawnServer(position, forward)); } private static bool TrySpawnServer(Vector3 position, Vector3 forward) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //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_009a: 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_0074: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } if (PorterState.WorldHasPorter()) { return false; } ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab("FullingPorter_Fuling") : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"Porter prefab is not registered in ZNetScene."); return false; } forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { forward = Vector3.forward; } GameObject val2 = Object.Instantiate<GameObject>(val, position + ((Vector3)(ref forward)).normalized * 2f, Quaternion.LookRotation(-((Vector3)(ref forward)).normalized)); ZNetView val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<ZNetView>() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { return false; } PorterState.MarkWorldOccupied(val3); return true; } private static void HandleToggleSource(long sender, ZPackage pkg) { //IL_0001: 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) GameObject val = FindObject(pkg.ReadZDOID()); Container val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Container>() : null); if ((Object)(object)val2 == (Object)null || !ValidateRemoteTargetAction(sender, val.transform.position) || !SourceContainerMarker.TryToggleServer(val2, out var enabled)) { SendResult(sender, ActionCode.ToggleSource, success: false); return; } ZPackage val3 = NewResult(ActionCode.ToggleSource, success: true); val3.Write(enabled); _rpc.SendPackage(sender, val3); } private static IEnumerator HandleDismiss(long sender, ZPackage pkg) { GameObject target = FindObject(pkg.ReadZDOID()); ZNetView view = (((Object)(object)target != (Object)null) ? target.GetComponent<ZNetView>() : null); PorterState porterState = (((Object)(object)target != (Object)null) ? target.GetComponent<PorterState>() : null); if ((Object)(object)view == (Object)null || (Object)(object)porterState == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || !ValidateRemoteTargetAction(sender, target.transform.position)) { SendResult(sender, ActionCode.Dismiss, success: false); yield break; } float deadline = Time.time + 3f; while (!view.IsOwner() && Time.time < deadline) { view.ClaimOwnership(); yield return null; } if (!view.IsOwner()) { SendResult(sender, ActionCode.Dismiss, success: false); } else { SendResult(sender, ActionCode.Dismiss, TryDismissServer(target)); } } private static bool TryDismissServer(GameObject target) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)target == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return false; } if ((Object)(object)target.GetComponent<PorterState>() == (Object)null) { return false; } PorterState.ClearWorldOccupied(); ZNetScene.instance.Destroy(target); return true; } private static void HandleRename(long sender, ZPackage pkg) { //IL_0001: 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) GameObject val = FindObject(pkg.ReadZDOID()); PorterState porterState = (((Object)(object)val != (Object)null) ? val.GetComponent<PorterState>() : null); string text = pkg.ReadString(); if ((Object)(object)porterState == (Object)null || !PorterServerRequestRules.IsRenamePayloadValid(text) || !ValidateRemoteTargetAction(sender, val.transform.position)) { SendResult(sender, ActionCode.Rename, success: false); return; } bool success = porterState.SetNameServer(text); SendResult(sender, ActionCode.Rename, success); } private static void HandleSpeak(long sender, ZPackage pkg) { //IL_0001: 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_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_0066: Unknown result type (might be due to invalid IL or missing references) GameObject val = FindObject(pkg.ReadZDOID()); PorterPersonality porterPersonality = (((Object)(object)val != (Object)null) ? val.GetComponent<PorterPersonality>() : null); bool flag = pkg.ReadBool(); if (!((Object)(object)porterPersonality == (Object)null) && TryResolveRemoteSender(sender, out var _, out var position) && TryConsumeActionBudget(sender)) { float num = (flag ? 18f : 5f); Vector3 val2 = position - val.transform.position; if (((Vector3)(ref val2)).sqrMagnitude > num * num) { Plugin.Log.LogWarning((object)$"Rejected porter speech request from peer {sender}: target is out of range."); } else { porterPersonality.TrySpeakServer(flag); } } } private static bool ValidateRemoteTargetAction(long sender, Vector3 targetPosition) { //IL_0018: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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) if (!TryResolveRemoteSender(sender, out var _, out var position)) { return false; } if (!TryConsumeActionBudget(sender)) { return false; } bool num = PorterServerRequestRules.IsWithinInteractionDistance(position.x, position.y, position.z, targetPosition.x, targetPosition.y, targetPosition.z); if (!num) { Plugin.Log.LogWarning((object)$"Rejected porter RPC from peer {sender}: target is out of interaction range."); } return num; } private static bool TryResolveRemoteSender(long sender, out ZNetPeer peer, out Vector3 position) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_0094: 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_0099: Unknown result type (might be due to invalid IL or missing references) peer = null; position = Vector3.zero; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } peer = ZNet.instance.GetPeer(sender); if (peer == null || !peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone()) { Plugin.Log.LogWarning((object)$"Rejected porter RPC from unknown or unready peer {sender}."); return false; } ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(peer.m_characterID) : null); position = ((val != null) ? val.GetPosition() : peer.m_refPos); return true; } private static bool TryConsumeActionBudget(long sender) { float time = Time.time; float value; float lastActionAt = (LastActionAt.TryGetValue(sender, out value) ? value : (-1f)); if (!PorterServerRequestRules.IsActionRateAllowed(time, lastActionAt)) { Plugin.Log.LogWarning((object)$"Rejected porter RPC from peer {sender}: action rate limit exceeded."); return false; } LastActionAt[sender] = time; return true; } private static GameObject FindObject(ZDOID id) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ZNetScene.instance != (Object)null)) { return null; } return ZNetScene.instance.FindInstance(id); } private static void SendResult(long target, ActionCode action, bool success) { _rpc.SendPackage(target, NewResult(action, success)); } private static ZPackage NewResult(ActionCode action, bool success) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0014: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write((int)action); val.Write(success); return val; } private static void CompleteLocalSpawn(bool success) { _spawnPending = false; if (success) { ConsumePendingContract(); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "$fullingporter_spawned", 0, (Sprite)null, false); } } else { Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, "$fullingporter_already_exists", 0, (Sprite)null, false); } } ClearPendingSpawn(); } private static void ConsumePendingContract() { object obj = _pendingContractInventory; if (obj == null) { Player localPlayer = Player.m_localPlayer; obj = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); } Inventory val = (Inventory)obj; if (val == null) { return; } if (_pendingContractItem != null && val.ContainsItem(_pendingContractItem)) { val.RemoveOneItem(_pendingContractItem); return; } List<ItemData> allItems = val.GetAllItems(); if (allItems == null) { return; } foreach (ItemData item in allItems) { object obj2; if (item == null) { obj2 = null; } else { GameObject dropPrefab = item.m_dropPrefab; obj2 = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } if (!((string?)obj2 != "FullingPorterContract")) { val.RemoveOneItem(item); break; } } } private static void ClearPendingSpawn() { _spawnPending = false; _pendingContractInventory = null; _pendingContractItem = null; } } } namespace FullingPorter.Localization { internal static class LocalizationRegistry { private static readonly Dictionary<string, string> English = new Dictionary<string, string> { ["fullingporter_name"] = "Fuling Porter", ["fullingporter_contract"] = "Fuling Porter Contract", ["fullingporter_contract_desc"] = "Hire one friendly Fuling porter to move source-chest items into QuickStackPlus Smart Storage.", ["fullingporter_source_enabled"] = "Porter source chest assigned", ["fullingporter_source_disabled"] = "Porter source chest unassigned", ["fullingporter_source_hover"] = "Porter source", ["fullingporter_status_idle"] = "Waiting for work", ["fullingporter_status_collecting"] = "Going to source", ["fullingporter_status_delivering"] = "Delivering", ["fullingporter_status_returning"] = "Returning home", ["fullingporter_status_blocked"] = "Waiting for storage space", ["fullingporter_spawned"] = "Your Fuling porter has arrived", ["fullingporter_already_exists"] = "Only one porter can be contracted in this world", ["fullingporter_rename_title"] = "Name your porter", ["fullingporter_dismissed"] = "Porter contract terminated", ["fullingporter_dismiss_confirm"] = "Press the dismissal key again within 3 seconds to terminate this contract", ["fullingporter_talk_idle_01"] = "Finally, no work!", ["fullingporter_talk_idle_02"] = "Porter carries. Viking watches.", ["fullingporter_talk_idle_03"] = "Coins to Haldor. Work to me. Very fair.", ["fullingporter_talk_idle_04"] = "I have seen berserkers carry less.", ["fullingporter_talk_idle_05"] = "Work exists. Vacation does not.", ["fullingporter_talk_idle_06"] = "That's how it goes, doggy.", ["fullingporter_talk_work_01"] = "Work again!?", ["fullingporter_talk_work_02"] = "Ten stacks! I have two hands!", ["fullingporter_talk_work_03"] = "I am porter, not portal!", ["fullingporter_talk_work_04"] = "Help me...", ["fullingporter_talk_work_05"] = "More wood? Did the forest happen to run out?", ["fullingporter_talk_work_06"] = "You put it in chest. I put it in another.", ["fullingporter_talk_work_07"] = "Walk there. Carry this. Walk back...", ["fullingporter_talk_return_01"] = "Done!", ["fullingporter_talk_return_02"] = "Cargo delivered. Please leave five stars.", ["fullingporter_talk_return_03"] = "I survived another chest.", ["fullingporter_talk_idle_040"] = "Finished! Pretending to be busy now.", ["fullingporter_talk_blocked_01"] = "Chest full! I am not magic!", ["fullingporter_talk_blocked_02"] = "Where put it!? On your head?", ["fullingporter_talk_blocked_03"] = "No room. Build chest. Big chest.", ["fullingporter_talk_idle_041"] = "What do you want? Can't you see? Five-minute break!" }; private static readonly Dictionary<string, string> Russian = new Dictionary<string, string> { ["fullingporter_name"] = "Фулинг-грузчик", ["fullingporter_contract"] = "Контракт фулинга-грузчика", ["fullingporter_contract_desc"] = "Наймите одного дружелюбного фулинга-грузчика для переноса вещей из источников в QuickStackPlus Smart Storage.", ["fullingporter_source_enabled"] = "Сундук назначен источником грузчика", ["fullingporter_source_disabled"] = "Сундук больше не является источником грузчика", ["fullingporter_source_hover"] = "Источник грузчика", ["fullingporter_status_idle"] = "Ожидает работу", ["fullingporter_status_collecting"] = "Идёт к источнику", ["fullingporter_status_delivering"] = "Разносит груз", ["fullingporter_status_returning"] = "Возвращается домой", ["fullingporter_status_blocked"] = "Ждёт освобождения склада", ["fullingporter_spawned"] = "Ваш фулинг-грузчик прибыл", ["fullingporter_already_exists"] = "В этом мире уже есть фулинг-грузчик", ["fullingporter_rename_title"] = "Имя грузчика", ["fullingporter_dismissed"] = "Контракт с грузчиком расторгнут", ["fullingporter_dismiss_confirm"] = "Нажмите клавишу увольнения ещё раз в течение 3 секунд, чтобы расторгнуть контракт", ["fullingporter_talk_idle_01"] = "Наконец-то нет работы!", ["fullingporter_talk_idle_02"] = "Грузчик носит. Викинг смотрит.", ["fullingporter_talk_idle_03"] = "Монеты Хальдору. Работа мне. Очень честно.", ["fullingporter_talk_idle_04"] = "Я видел берсерков. Они меньше таскают.", ["fullingporter_talk_idle_05"] = "Работа есть. Отпуска нет.", ["fullingporter_talk_idle_06"] = "Вот такие дела, собачка.", ["fullingporter_talk_work_01"] = "Опять работать!?", ["fullingporter_talk_work_02"] = "Десять стопок! У меня две руки!", ["fullingporter_talk_work_03"] = "Я грузчик, не портал!", ["fullingporter_talk_work_04"] = "Помогите...", ["fullingporter_talk_work_05"] = "Ещё дерево? У вас лес случайно не закончился?", ["fullingporter_talk_work_06"] = "Ты кладёшь в сундук. Я кладу в другой.", ["fullingporter_talk_work_07"] = "Иди туда. Неси это. Иди назад...", ["fullingporter_talk_return_01"] = "Готово!", ["fullingporter_talk_return_02"] = "Груз доставлен. Поставьте пять звезд.", ["fullingporter_talk_return_03"] = "Я пережил ещё один сундук.", ["fullingporter_talk_idle_040"] = "Закончил! Теперь делаю вид, что занят.", ["fullingporter_talk_blocked_01"] = "Сундук полный! Я не маг!", ["fullingporter_talk_blocked_02"] = "Куда класть!? На голову тебе?", ["fullingporter_talk_blocked_03"] = "Места нет. Строй сундук. Большой сундук.", ["fullingporter_talk_idle_041"] = "Чего тебе? Не видишь? Перерыв пять минут!" }; internal static void Register() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); st