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 WiresBalancedCraftFromNearbyChests v0.1.1
WiresBalancedCraftFromNearbyChests.dll
Decompiled 13 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("WiresBalancedCraftFromNearbyChests")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WiresBalancedCraftFromNearbyChests")] [assembly: AssemblyTitle("WiresBalancedCraftFromNearbyChests")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace WiresBalancedCraftFromNearbyChests { [HarmonyPatch(typeof(ZDO), "Set", new Type[] { typeof(int), typeof(string) })] internal static class ChestTitleZdoSetPatch { [HarmonyPostfix] private static void Postfix(ZDO __instance, int hash) { if (__instance != null && hash == ZDOVars.s_text) { ContainerRegistry.NotifyTitleChanged(__instance); } } } [HarmonyPatch(typeof(ZNetView), "OnDestroy")] internal static class RegistryObjectDestroyPatch { [HarmonyPrefix] private static void Prefix(ZNetView __instance) { if ((Object)(object)__instance == (Object)null) { return; } Container[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Container>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { ContainerRegistry.Unregister(componentsInChildren[i]); } } Smelter componentInChildren = ((Component)__instance).GetComponentInChildren<Smelter>(true); if ((Object)(object)componentInChildren != (Object)null) { RefillTargetRegistry.Unregister((Component)(object)componentInChildren); } Fireplace componentInChildren2 = ((Component)__instance).GetComponentInChildren<Fireplace>(true); if ((Object)(object)componentInChildren2 != (Object)null) { RefillTargetRegistry.Unregister((Component)(object)componentInChildren2); } CookingStation componentInChildren3 = ((Component)__instance).GetComponentInChildren<CookingStation>(true); if ((Object)(object)componentInChildren3 != (Object)null) { RefillTargetRegistry.Unregister((Component)(object)componentInChildren3); } Fermenter componentInChildren4 = ((Component)__instance).GetComponentInChildren<Fermenter>(true); if ((Object)(object)componentInChildren4 != (Object)null) { RefillTargetRegistry.Unregister((Component)(object)componentInChildren4); } Turret componentInChildren5 = ((Component)__instance).GetComponentInChildren<Turret>(true); if ((Object)(object)componentInChildren5 != (Object)null) { RefillTargetRegistry.Unregister((Component)(object)componentInChildren5); } ShieldGenerator componentInChildren6 = ((Component)__instance).GetComponentInChildren<ShieldGenerator>(true); if ((Object)(object)componentInChildren6 != (Object)null) { RefillTargetRegistry.Unregister((Component)(object)componentInChildren6); } Catapult componentInChildren7 = ((Component)__instance).GetComponentInChildren<Catapult>(true); if ((Object)(object)componentInChildren7 != (Object)null) { RefillTargetRegistry.Unregister((Component)(object)componentInChildren7); } } } [HarmonyPatch(typeof(ZDO), "Set", new Type[] { typeof(string), typeof(string) })] internal static class ChestTitleZdoStringSetPatch { [HarmonyPostfix] private static void Postfix(ZDO __instance, string name) { if (__instance != null && string.Equals(name, "text", StringComparison.Ordinal)) { ContainerRegistry.NotifyTitleChanged(__instance); } } } [HarmonyPatch(typeof(CraftingStation), "Start")] internal static class CraftingStationStartGraphPatch { [HarmonyPostfix] private static void Postfix() { ConnectionVisuals.NotifyGraphChanged(); } } [HarmonyPatch(typeof(CraftingStation), "OnDestroy")] internal static class CraftingStationDestroyGraphPatch { [HarmonyPrefix] private static void Prefix() { ConnectionVisuals.NotifyGraphChanged(); } } internal static class ConnectionVisuals { private enum VisualMode { None, Station, RefillTarget, ChestStation, ChestRefill, Tool } private struct BeamKey : IEquatable<BeamKey> { internal int ContainerId; internal int TargetId; internal bool FromPlayer; public bool Equals(BeamKey other) { if (ContainerId == other.ContainerId && TargetId == other.TargetId) { return FromPlayer == other.FromPlayer; } return false; } public override bool Equals(object obj) { if (obj is BeamKey) { return Equals((BeamKey)obj); } return false; } public override int GetHashCode() { return (((ContainerId * 397) ^ TargetId) * 397) ^ (FromPlayer ? 1 : 0); } } private sealed class Beam { internal BeamKey Key; internal Container Container; internal Component Target; internal bool FromPlayer; internal GameObject Prefab; internal GameObject Instance; } private static readonly Dictionary<BeamKey, Beam> Beams = new Dictionary<BeamKey, Beam>(); private static readonly HashSet<BeamKey> Desired = new HashSet<BeamKey>(); private static readonly List<BeamKey> RemoveKeys = new List<BeamKey>(); private static readonly Dictionary<int, Stack<GameObject>> Pool = new Dictionary<int, Stack<GameObject>>(); private static readonly FieldInfo AllExtensionsField = AccessTools.Field(typeof(StationExtension), "m_allExtensions"); private static readonly string[] KnownExtensionPrefabs = new string[10] { "piece_workbench_ext1", "piece_workbench_ext2", "piece_workbench_ext3", "piece_workbench_ext4", "forge_ext1", "forge_ext2", "forge_ext3", "forge_ext4", "forge_ext5", "forge_ext6" }; private const float ContextPollSeconds = 0.08f; private const float LazyGraphDelay = 0.12f; private const float MovingGraphRefreshSeconds = 0.25f; private const int BeamCreatesPerFrame = 1; private static GameObject _genericConnectionPrefab; private static VisualMode _mode; private static int _focusId; private static string _routeId; private static CraftingStation _station; private static Component _refillTarget; private static Container _chest; private static ItemData _tool; private static bool _dirty = true; private static float _rebuildNotBefore; private static float _nextContextPoll; private static float _nextMovingGraphRefresh; internal static void Update() { ContainerRegistry.Tick(); if (!VisualsEnabled()) { CleanupActive(); ResetContext(); return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { CleanupActive(); ResetContext(); return; } if (Time.time >= _nextContextPoll) { _nextContextPoll = Time.time + 0.08f; PollContext(localPlayer); } if (Time.time >= _nextMovingGraphRefresh && ContextUsesMovingContainers()) { _nextMovingGraphRefresh = Time.time + 0.25f; _dirty = true; _rebuildNotBefore = 0f; } if (_dirty && Time.time >= _rebuildNotBefore) { Rebuild(localPlayer); _dirty = false; } ProcessBeamCreationBudget(localPlayer); if (Beams.Count > 0 && (_mode == VisualMode.Tool || HasMovingBeam())) { UpdateBeamTransforms(localPlayer); } } private static bool VisualsEnabled() { if (ModConfig.Enabled != null && ModConfig.Enabled.Value && ModConfig.ShowConnections != null) { return ModConfig.ShowConnections.Value; } return false; } private static bool ContextUsesMovingContainers() { if ((Object)(object)_chest != (Object)null && ContainerRegistry.IsMoving(_chest)) { return true; } string text = _routeId; if (string.IsNullOrEmpty(text) && _mode == VisualMode.Station && (Object)(object)_station != (Object)null) { text = RouteRegistry.EnsureStation(_station)?.Id; } return ContainerRegistry.HasMovingForRoute(text); } private static bool HasMovingBeam() { foreach (Beam value in Beams.Values) { if (value != null && ContainerRegistry.IsMoving(value.Container)) { return true; } } return false; } private static void PollContext(Player player) { GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject != (Object)null) { CraftingStation componentInParent = hoverObject.GetComponentInParent<CraftingStation>(); if ((Object)(object)componentInParent != (Object)null) { SetContext(VisualMode.Station, ((Object)componentInParent).GetInstanceID(), null, componentInParent, null, null, null); return; } Component val = RefillTargetRegistry.FindHoveredTarget(hoverObject); if ((Object)(object)val != (Object)null) { RouteDefinition routeDefinition = RefillRouting.Resolve(val); if (routeDefinition != null) { SetContext(VisualMode.RefillTarget, ((Object)val).GetInstanceID(), routeDefinition.Id, null, val, null, null); return; } } Container componentInParent2 = hoverObject.GetComponentInParent<Container>(); if ((Object)(object)componentInParent2 != (Object)null) { RouteDefinition routeDefinition2 = RouteRegistry.Get(ContainerRegistry.GetAssignedRouteId(componentInParent2)); if (routeDefinition2 != null && routeDefinition2.Kind == RouteKind.Station) { SetContext(VisualMode.ChestStation, ((Object)componentInParent2).GetInstanceID(), routeDefinition2.Id, null, null, componentInParent2, null); return; } if (routeDefinition2 != null && routeDefinition2.Kind == RouteKind.Refill) { SetContext(VisualMode.ChestRefill, ((Object)componentInParent2).GetInstanceID(), routeDefinition2.Id, null, null, componentInParent2, null); return; } } } ItemData rightItem = ((Humanoid)player).RightItem; if (Hud.InBuildUi() && rightItem != null && rightItem.m_shared != null && (Object)(object)rightItem.m_shared.m_buildPieces != (Object)null) { RouteDefinition routeDefinition3 = RouteRegistry.EnsureTool(rightItem); if (routeDefinition3 != null) { int focusId = (((Object)(object)rightItem.m_dropPrefab != (Object)null) ? ((Object)rightItem.m_dropPrefab).GetInstanceID() : ((object)rightItem).GetHashCode()); SetContext(VisualMode.Tool, focusId, routeDefinition3.Id, null, null, null, rightItem); return; } } SetContext(VisualMode.None, 0, null, null, null, null, null); } private static void SetContext(VisualMode mode, int focusId, string routeId, CraftingStation station, Component refillTarget, Container chest, ItemData tool) { bool num = _mode != mode || _focusId != focusId || !string.Equals(_routeId, routeId, StringComparison.Ordinal); _mode = mode; _focusId = focusId; _routeId = routeId; _station = station; _refillTarget = refillTarget; _chest = chest; _tool = tool; if (num) { _dirty = true; _rebuildNotBefore = 0f; } } private static void Rebuild(Player player) { Desired.Clear(); switch (_mode) { case VisualMode.Station: BuildStation(_station); break; case VisualMode.RefillTarget: BuildRefillTarget(_refillTarget); break; case VisualMode.ChestStation: BuildChestStation(_chest); break; case VisualMode.ChestRefill: BuildChestRefill(_chest); break; case VisualMode.Tool: BuildTool(player, _tool); break; } RemoveUndesired(); UpdateBeamTransforms(player); } private static void BuildStation(CraftingStation station) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)station == (Object)null) { return; } RouteDefinition routeDefinition = RouteRegistry.EnsureStation(station); if (routeDefinition != null) { float stationBuildRange = station.GetStationBuildRange(); List<Container> matching = ContainerRegistry.GetMatching(new RouteContext { Definition = routeDefinition, Origin = ((Component)station).transform.position, Range = stationBuildRange, Station = station }); GameObject prefab = FindConnectionPrefab(station); for (int i = 0; i < matching.Count; i++) { WantBeam(matching[i], (Component)(object)station, fromPlayer: false, prefab); } } } private static void BuildRefillTarget(Component target) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return; } RouteDefinition routeDefinition = RefillRouting.Resolve(target); if (routeDefinition != null) { float sharedStationRadius = RangeProvider.GetSharedStationRadius(); List<Container> matching = ContainerRegistry.GetMatching(new RouteContext { Definition = routeDefinition, Origin = target.transform.position, Range = sharedStationRadius }); GameObject prefab = FindGenericConnectionPrefab(); for (int i = 0; i < matching.Count; i++) { WantBeam(matching[i], target, fromPlayer: false, prefab); } } } private static void BuildChestStation(Container chest) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null || !ContainerRegistry.IsUsable(chest)) { return; } RouteDefinition routeDefinition = RouteRegistry.Get(ContainerRegistry.GetAssignedRouteId(chest)); if (routeDefinition == null) { return; } foreach (CraftingStation instance in CraftingStation.Instances) { CraftingStation val = instance; if ((Object)(object)val == (Object)null) { continue; } RouteDefinition routeDefinition2 = RouteRegistry.EnsureStation(val); if (routeDefinition2 != null && !(routeDefinition2.Id != routeDefinition.Id)) { float stationBuildRange = val.GetStationBuildRange(); if (WithinHorizontalRange(((Component)chest).transform.position, ((Component)val).transform.position, stationBuildRange)) { WantBeam(chest, (Component)(object)val, fromPlayer: false, FindConnectionPrefab(val)); } } } } private static void BuildChestRefill(Container chest) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null || !ContainerRegistry.IsUsable(chest)) { return; } RouteDefinition routeDefinition = RouteRegistry.Get(ContainerRegistry.GetAssignedRouteId(chest)); if (routeDefinition == null) { return; } float sharedStationRadius = RangeProvider.GetSharedStationRadius(); foreach (Component target in RefillTargetRegistry.GetTargets(routeDefinition)) { if (!((Object)(object)target == (Object)null) && WithinHorizontalRange(((Component)chest).transform.position, target.transform.position, sharedStationRadius)) { WantBeam(chest, target, fromPlayer: false, FindGenericConnectionPrefab()); } } } private static void BuildTool(Player player, ItemData tool) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || tool == null) { return; } RouteDefinition routeDefinition = RouteRegistry.EnsureTool(tool); if (routeDefinition != null) { float sharedStationRadius = RangeProvider.GetSharedStationRadius(); List<Container> matching = ContainerRegistry.GetMatching(new RouteContext { Definition = routeDefinition, Origin = ((Component)player).transform.position, Range = sharedStationRadius, Tool = tool }); GameObject prefab = FindGenericConnectionPrefab(); for (int i = 0; i < matching.Count; i++) { WantBeam(matching[i], null, fromPlayer: true, prefab); } } } private static bool WithinHorizontalRange(Vector3 a, Vector3 b, float range) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Vector3 val = a - b; val.y = 0f; return ((Vector3)(ref val)).sqrMagnitude <= range * range; } private static void WantBeam(Container chest, Component target, bool fromPlayer, GameObject prefab) { if (!((Object)(object)chest == (Object)null) && !((Object)(object)prefab == (Object)null)) { BeamKey beamKey = new BeamKey { ContainerId = ((Object)chest).GetInstanceID(), TargetId = (((Object)(object)target != (Object)null) ? ((Object)target).GetInstanceID() : 0), FromPlayer = fromPlayer }; Desired.Add(beamKey); EnsureBeam(beamKey, chest, target, fromPlayer, prefab); } } private static void EnsureBeam(BeamKey key, Container chest, Component target, bool fromPlayer, GameObject prefab) { if (Beams.TryGetValue(key, out var value)) { if (value != null && (Object)(object)value.Prefab == (Object)(object)prefab) { value.Container = chest; value.Target = target; value.FromPlayer = fromPlayer; return; } ReleaseBeam(value); Beams.Remove(key); } Beams.Add(key, new Beam { Key = key, Container = chest, Target = target, FromPlayer = fromPlayer, Prefab = prefab, Instance = null }); } private static GameObject AcquireInstance(GameObject prefab) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)prefab).GetInstanceID(); GameObject val = null; if (Pool.TryGetValue(instanceID, out var value)) { while (value.Count > 0 && (Object)(object)val == (Object)null) { val = value.Pop(); } } if ((Object)(object)val == (Object)null) { val = Object.Instantiate<GameObject>(prefab, Vector3.zero, Quaternion.identity); } if ((Object)(object)val == (Object)null) { return null; } val.SetActive(true); return val; } private static void ReleaseBeam(Beam beam) { if (beam == null || (Object)(object)beam.Instance == (Object)null) { return; } beam.Instance.SetActive(false); if ((Object)(object)beam.Prefab == (Object)null) { Object.Destroy((Object)(object)beam.Instance); return; } int instanceID = ((Object)beam.Prefab).GetInstanceID(); if (!Pool.TryGetValue(instanceID, out var value)) { value = new Stack<GameObject>(); Pool.Add(instanceID, value); } if (value.Count < 64) { value.Push(beam.Instance); } else { Object.Destroy((Object)(object)beam.Instance); } } private static void RemoveUndesired() { RemoveKeys.Clear(); foreach (KeyValuePair<BeamKey, Beam> beam in Beams) { if (!Desired.Contains(beam.Key) || beam.Value == null || (Object)(object)beam.Value.Container == (Object)null) { RemoveKeys.Add(beam.Key); } } for (int i = 0; i < RemoveKeys.Count; i++) { if (Beams.TryGetValue(RemoveKeys[i], out var value)) { ReleaseBeam(value); } Beams.Remove(RemoveKeys[i]); } } private static void ProcessBeamCreationBudget(Player player) { int num = 0; foreach (Beam value in Beams.Values) { if (num >= 1) { break; } if (value != null && !((Object)(object)value.Instance != (Object)null) && !((Object)(object)value.Prefab == (Object)null) && !((Object)(object)value.Container == (Object)null)) { value.Instance = AcquireInstance(value.Prefab); if (!((Object)(object)value.Instance == (Object)null)) { UpdateBeamTransform(value, player); num++; } } } } private static void UpdateBeamTransforms(Player player) { foreach (Beam value in Beams.Values) { UpdateBeamTransform(value, player); } } private static void UpdateBeamTransform(Beam beam, Player player) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (beam == null || (Object)(object)beam.Container == (Object)null || (Object)(object)beam.Instance == (Object)null) { return; } Vector3 val; Vector3 val2; if (beam.FromPlayer) { if ((Object)(object)player == (Object)null) { return; } val = ((Component)player).transform.position + Vector3.up; val2 = GetContainerPoint(beam.Container); } else { if ((Object)(object)beam.Target == (Object)null) { return; } val = GetContainerPoint(beam.Container); val2 = GetTargetPoint(beam.Target); } Vector3 val3 = val2 - val; if (!(((Vector3)(ref val3)).sqrMagnitude < 0.0001f)) { Transform transform = beam.Instance.transform; transform.position = val; transform.rotation = Quaternion.LookRotation(((Vector3)(ref val3)).normalized); transform.localScale = new Vector3(1f, 1f, ((Vector3)(ref val3)).magnitude); } } private static Vector3 GetTargetPoint(Component target) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) CraftingStation val = (CraftingStation)(object)((target is CraftingStation) ? target : null); if ((Object)(object)val != (Object)null) { return val.GetConnectionEffectPoint(); } Collider componentInChildren = target.GetComponentInChildren<Collider>(); Bounds bounds; if ((Object)(object)componentInChildren != (Object)null) { bounds = componentInChildren.bounds; return ((Bounds)(ref bounds)).center; } Piece componentInParent = target.GetComponentInParent<Piece>(); if ((Object)(object)componentInParent != (Object)null) { componentInChildren = ((Component)componentInParent).GetComponentInChildren<Collider>(); if ((Object)(object)componentInChildren != (Object)null) { bounds = componentInChildren.bounds; return ((Bounds)(ref bounds)).center; } } return target.transform.position + Vector3.up * 0.5f; } private static Vector3 GetContainerPoint(Container container) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_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_0019: Unknown result type (might be due to invalid IL or missing references) Collider componentInChildren = ((Component)container).GetComponentInChildren<Collider>(); if ((Object)(object)componentInChildren != (Object)null) { Bounds bounds = componentInChildren.bounds; return ((Bounds)(ref bounds)).center; } return ((Component)container).transform.position + Vector3.up * 0.5f; } private static GameObject FindConnectionPrefab(CraftingStation station) { if ((Object)(object)station != (Object)null) { List<StationExtension> loadedExtensions = GetLoadedExtensions(); if (loadedExtensions != null) { for (int i = 0; i < loadedExtensions.Count; i++) { StationExtension val = loadedExtensions[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.m_connectionPrefab == (Object)null) && !((Object)(object)val.m_craftingStation == (Object)null) && val.m_craftingStation.m_name == station.m_name) { return val.m_connectionPrefab; } } } } return FindGenericConnectionPrefab(); } private static List<StationExtension> GetLoadedExtensions() { if (!(AllExtensionsField != null)) { return null; } return AllExtensionsField.GetValue(null) as List<StationExtension>; } private static GameObject FindGenericConnectionPrefab() { if ((Object)(object)_genericConnectionPrefab != (Object)null) { return _genericConnectionPrefab; } List<StationExtension> loadedExtensions = GetLoadedExtensions(); if (loadedExtensions != null) { for (int i = 0; i < loadedExtensions.Count; i++) { StationExtension val = loadedExtensions[i]; if ((Object)(object)val != (Object)null && (Object)(object)val.m_connectionPrefab != (Object)null) { _genericConnectionPrefab = val.m_connectionPrefab; return _genericConnectionPrefab; } } } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return null; } for (int j = 0; j < KnownExtensionPrefabs.Length; j++) { GameObject prefab = instance.GetPrefab(KnownExtensionPrefabs[j]); if (!((Object)(object)prefab == (Object)null)) { StationExtension componentInChildren = prefab.GetComponentInChildren<StationExtension>(true); if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)componentInChildren.m_connectionPrefab == (Object)null)) { _genericConnectionPrefab = componentInChildren.m_connectionPrefab; return _genericConnectionPrefab; } } } return null; } internal static void NotifyGraphChanged() { _dirty = true; _rebuildNotBefore = Mathf.Max(_rebuildNotBefore, Time.time + 0.12f); } internal static void NotifyChestChanged(Container chest) { NotifyGraphChanged(); if (!((Object)(object)chest == (Object)null) && !string.IsNullOrEmpty(ContainerRegistry.GetAssignedRouteId(chest))) { SpawnRenameBurst(chest); } } private static void SpawnRenameBurst(Container chest) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) GameObject val = FindGenericConnectionPrefab(); if (!((Object)(object)val == (Object)null)) { GameObject val2 = Object.Instantiate<GameObject>(val, GetContainerPoint(chest), Quaternion.identity); if (!((Object)(object)val2 == (Object)null)) { val2.transform.localScale = Vector3.one * 0.3f; val2.SetActive(true); Object.Destroy((Object)(object)val2, 0.55f); } } } private static void CleanupActive() { foreach (Beam value in Beams.Values) { ReleaseBeam(value); } Beams.Clear(); Desired.Clear(); RemoveKeys.Clear(); } private static void ResetContext() { _mode = VisualMode.None; _focusId = 0; _routeId = null; _station = null; _refillTarget = null; _chest = null; _tool = null; _dirty = true; _rebuildNotBefore = 0f; _nextMovingGraphRefresh = 0f; } internal static void Cleanup() { CleanupActive(); foreach (Stack<GameObject> value in Pool.Values) { while (value.Count > 0) { GameObject val = value.Pop(); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } Pool.Clear(); _genericConnectionPrefab = null; ResetContext(); } } internal static class ContainerRegistry { private const float CacheSeconds = 0.25f; private const float ReconcileIntervalSeconds = 0.05f; private static readonly HashSet<Container> Known = new HashSet<Container>(); private static readonly List<Container> Reconcile = new List<Container>(); private static readonly Queue<Container> PendingIndex = new Queue<Container>(); private static readonly Dictionary<string, HashSet<Container>> ByRoute = new Dictionary<string, HashSet<Container>>(StringComparer.Ordinal); private static readonly Dictionary<int, string> RouteByContainer = new Dictionary<int, string>(); private static readonly Dictionary<int, string> TitleByContainer = new Dictionary<int, string>(); private static readonly Dictionary<ZDOID, Container> ByZdo = new Dictionary<ZDOID, Container>(); private static readonly List<Container> Matching = new List<Container>(); private static readonly MethodInfo CheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); private static float _nextRefresh; private static float _nextReconcile; private static int _reconcileCursor; private static bool _fullReindexPending; private static int _fullReindexCursor; private static string _cachedRouteId; private static Vector3 _cachedOrigin; private static float _cachedRange; internal static void Register(Container container) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)container == (Object)null) && Known.Add(container)) { Reconcile.Add(container); PendingIndex.Enqueue(container); ZNetView netView = GetNetView(container); if ((Object)(object)netView != (Object)null && netView.IsValid() && netView.GetZDO() != null) { ByZdo[netView.GetZDO().m_uid] = container; } } } internal static void Unregister(Container container) { if (!((Object)(object)container == (Object)null)) { Known.Remove(container); Reconcile.Remove(container); RemoveIndex(container); Invalidate(); ConnectionVisuals.NotifyGraphChanged(); } } internal static void Clear() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) Known.Clear(); Reconcile.Clear(); PendingIndex.Clear(); ByRoute.Clear(); RouteByContainer.Clear(); TitleByContainer.Clear(); ByZdo.Clear(); Matching.Clear(); _nextRefresh = 0f; _nextReconcile = 0f; _reconcileCursor = 0; _fullReindexPending = false; _fullReindexCursor = 0; _cachedRouteId = null; _cachedOrigin = Vector3.zero; _cachedRange = 0f; } internal static void Invalidate() { _nextRefresh = 0f; _cachedRouteId = null; } internal static void ReindexAll() { ByRoute.Clear(); RouteByContainer.Clear(); TitleByContainer.Clear(); _fullReindexPending = Reconcile.Count > 0; _fullReindexCursor = 0; Invalidate(); ConnectionVisuals.NotifyGraphChanged(); } internal static void Tick() { while (PendingIndex.Count > 0) { Container val = PendingIndex.Dequeue(); if (!((Object)(object)val == (Object)null)) { Reindex(val, force: false); return; } } if (_fullReindexPending) { while (_fullReindexCursor < Reconcile.Count) { Container val2 = Reconcile[_fullReindexCursor++]; if (!((Object)(object)val2 == (Object)null)) { Reindex(val2, force: true); return; } } _fullReindexPending = false; _fullReindexCursor = 0; } else { if (Reconcile.Count == 0 || Time.time < _nextReconcile) { return; } _nextReconcile = Time.time + 0.05f; if (_reconcileCursor >= Reconcile.Count) { _reconcileCursor = 0; } int reconcileCursor = _reconcileCursor; Container val3 = Reconcile[reconcileCursor]; if ((Object)(object)val3 == (Object)null) { Reconcile.RemoveAt(reconcileCursor); if (_reconcileCursor >= Reconcile.Count) { _reconcileCursor = 0; } } else { _reconcileCursor++; Reindex(val3, force: false); } } } internal static void NotifyTitleChanged(Container container) { if (!((Object)(object)container == (Object)null)) { int instanceID = ((Object)container).GetInstanceID(); TitleByContainer.TryGetValue(instanceID, out var value); string b = GetCustomTitle(container) ?? ""; if (!string.Equals(value, b, StringComparison.Ordinal)) { Reindex(container, force: true); Invalidate(); ConnectionVisuals.NotifyChestChanged(container); } } } internal static void NotifyTitleChanged(ZDO zdo) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (zdo != null && ByZdo.TryGetValue(zdo.m_uid, out var value) && (Object)(object)value != (Object)null) { NotifyTitleChanged(value); } } internal static string GetAssignedRouteId(Container container) { if ((Object)(object)container == (Object)null) { return null; } int instanceID = ((Object)container).GetInstanceID(); if (!TitleByContainer.ContainsKey(instanceID)) { Reindex(container, force: true); } else { Reindex(container, force: false); } if (!RouteByContainer.TryGetValue(((Object)container).GetInstanceID(), out var value)) { return null; } return value; } internal static List<Container> GetMatching(RouteContext context) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if (context == null || context.Definition == null) { Matching.Clear(); return Matching; } if (_cachedRouteId == context.Definition.Id && Mathf.Abs(_cachedRange - context.Range) < 0.01f) { Vector3 val = _cachedOrigin - context.Origin; if (((Vector3)(ref val)).sqrMagnitude < 0.0625f && Time.time < _nextRefresh) { return Matching; } } _cachedRouteId = context.Definition.Id; _cachedOrigin = context.Origin; _cachedRange = context.Range; _nextRefresh = Time.time + 0.25f; Refresh(context); return Matching; } internal static bool IsAssignedTo(Container container, RouteDefinition route) { if ((Object)(object)container == (Object)null || route == null) { return false; } ZNetView netView = GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid() || netView.GetZDO() == null) { return false; } return GetAssignedRouteId(container) == route.Id; } internal static bool IsUsable(Container container) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)container == (Object)null || (Object)(object)localPlayer == (Object)null || (Object)(object)Game.instance == (Object)null || container.GetInventory() == null) { return false; } ZNetView netView = GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return false; } long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); if (CheckAccessMethod != null) { object obj = CheckAccessMethod.Invoke(container, new object[1] { playerID }); if (!(obj is bool) || !(bool)obj) { return false; } } if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)) { return false; } ZDO zDO = netView.GetZDO(); if (!MultiUserChestCompat.Active && zDO != null && !netView.IsOwner() && zDO.GetInt(ZDOVars.s_inUse, 0) == 1) { return false; } return true; } internal static bool IsMoving(Container container) { if ((Object)(object)container != (Object)null) { return (Object)(object)container.m_rootObjectOverride != (Object)null; } return false; } internal static bool HasMovingForRoute(string routeId) { if (string.IsNullOrEmpty(routeId)) { return false; } if (!ByRoute.TryGetValue(routeId, out var value)) { return false; } foreach (Container item in value) { if (IsMoving(item)) { return true; } } return false; } internal static ZNetView GetNetView(Container container) { if ((Object)(object)container == (Object)null) { return null; } if (!((Object)(object)container.m_rootObjectOverride != (Object)null)) { return ((Component)container).GetComponent<ZNetView>(); } return container.m_rootObjectOverride; } internal static bool TryTakeOwnership(Container container) { ZNetView netView = GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return false; } if (!netView.IsOwner()) { netView.ClaimOwnership(); } return netView.IsOwner(); } private static void Reindex(Container container, bool force) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null) { return; } ZNetView netView = GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid() || netView.GetZDO() == null) { return; } ZDO zDO = netView.GetZDO(); ByZdo[zDO.m_uid] = container; string text = zDO.GetString(ZDOVars.s_text, "") ?? ""; int instanceID = ((Object)container).GetInstanceID(); if (!force && TitleByContainer.TryGetValue(instanceID, out var value) && string.Equals(value, text, StringComparison.Ordinal)) { return; } RouteByContainer.TryGetValue(instanceID, out var value2); string text2 = RouteRegistry.ClassifyTitle(text); if (!string.IsNullOrEmpty(value2) && !string.Equals(value2, text2, StringComparison.Ordinal) && ByRoute.TryGetValue(value2, out var value3)) { value3.Remove(container); if (value3.Count == 0) { ByRoute.Remove(value2); } } if (!string.IsNullOrEmpty(text2)) { if (!ByRoute.TryGetValue(text2, out var value4)) { value4 = new HashSet<Container>(); ByRoute.Add(text2, value4); } value4.Add(container); RouteByContainer[instanceID] = text2; } else { RouteByContainer.Remove(instanceID); } TitleByContainer[instanceID] = text; if (!string.Equals(value2, text2, StringComparison.Ordinal)) { Invalidate(); ConnectionVisuals.NotifyGraphChanged(); } } private static void RemoveIndex(Container container) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null) { return; } int instanceID = ((Object)container).GetInstanceID(); if (RouteByContainer.TryGetValue(instanceID, out var value) && ByRoute.TryGetValue(value, out var value2)) { value2.Remove(container); if (value2.Count == 0) { ByRoute.Remove(value); } } RouteByContainer.Remove(instanceID); TitleByContainer.Remove(instanceID); ZNetView netView = GetNetView(container); if ((Object)(object)netView != (Object)null && netView.GetZDO() != null) { ByZdo.Remove(netView.GetZDO().m_uid); } } private static void Refresh(RouteContext context) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) Matching.Clear(); float num = context.Range * context.Range; if (!ByRoute.TryGetValue(context.Definition.Id, out var value)) { return; } foreach (Container item in value) { if (!((Object)(object)item == (Object)null)) { Vector3 val = ((Component)item).transform.position - context.Origin; val.y = 0f; if (!(((Vector3)(ref val)).sqrMagnitude > num) && IsUsable(item)) { Matching.Add(item); } } } Matching.Sort(delegate(Container a, Container b) { //IL_001f: 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_002f: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) string customTitle = GetCustomTitle(a); string customTitle2 = GetCustomTitle(b); int num2 = WindowsLogicalCompare(customTitle, customTitle2); if (num2 != 0) { return num2; } Vector3 val2 = ((Component)a).transform.position - context.Origin; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; val2 = ((Component)b).transform.position - context.Origin; float sqrMagnitude2 = ((Vector3)(ref val2)).sqrMagnitude; return sqrMagnitude.CompareTo(sqrMagnitude2); }); } internal static string GetCustomTitle(Container container) { ZNetView netView = GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid() || netView.GetZDO() == null) { return ""; } return netView.GetZDO().GetString(ZDOVars.s_text, ""); } private static int WindowsLogicalCompare(string left, string right) { left = left ?? ""; right = right ?? ""; if (left.Length != right.Length) { if (left.StartsWith(right, StringComparison.OrdinalIgnoreCase)) { return -1; } if (right.StartsWith(left, StringComparison.OrdinalIgnoreCase)) { return 1; } } int i = 0; int j = 0; while (i < left.Length && j < right.Length) { char c = left[i]; char c2 = right[j]; if (char.IsDigit(c) && char.IsDigit(c2)) { int num = i; int num2 = j; for (; i < left.Length && left[i] == '0'; i++) { } for (; j < right.Length && right[j] == '0'; j++) { } int num3 = i; int num4 = j; for (; i < left.Length && char.IsDigit(left[i]); i++) { } for (; j < right.Length && char.IsDigit(right[j]); j++) { } int num5 = i - num3; int num6 = j - num4; if (num5 != num6) { return num5.CompareTo(num6); } for (int k = 0; k < num5; k++) { int num7 = left[num3 + k].CompareTo(right[num4 + k]); if (num7 != 0) { return num7; } } int num8 = i - num; int num9 = j - num2; if (num8 != num9) { return num8.CompareTo(num9); } } else { char c3 = char.ToUpperInvariant(c); char c4 = char.ToUpperInvariant(c2); if (c3 != c4) { return c3.CompareTo(c4); } i++; j++; } } return (left.Length - i).CompareTo(right.Length - j); } } internal static class MethodSwaps { internal static readonly MethodInfo CountItemsFrom = AccessTools.Method(typeof(Inventory), "CountItems", new Type[3] { typeof(string), typeof(int), typeof(bool) }, (Type[])null); internal static readonly MethodInfo CountItemsTo = AccessTools.Method(typeof(InventoryBridge), "CountItems", (Type[])null, (Type[])null); internal static readonly MethodInfo HaveItemFrom = AccessTools.Method(typeof(Inventory), "HaveItem", new Type[2] { typeof(string), typeof(bool) }, (Type[])null); internal static readonly MethodInfo HaveItemTo = AccessTools.Method(typeof(InventoryBridge), "HaveItem", (Type[])null, (Type[])null); internal static readonly MethodInfo GetItemFrom = AccessTools.Method(typeof(Inventory), "GetItem", new Type[3] { typeof(string), typeof(int), typeof(bool) }, (Type[])null); internal static readonly MethodInfo GetItemTo = AccessTools.Method(typeof(InventoryBridge), "GetItem", (Type[])null, (Type[])null); internal static readonly MethodInfo RemoveItemFrom = AccessTools.Method(typeof(Inventory), "RemoveItem", new Type[4] { typeof(string), typeof(int), typeof(int), typeof(bool) }, (Type[])null); internal static readonly MethodInfo RemoveItemTo = AccessTools.Method(typeof(InventoryBridge), "RemoveItem", (Type[])null, (Type[])null); } internal static class Redirect { internal static IEnumerable<CodeInstruction> Calls(IEnumerable<CodeInstruction> instructions, params KeyValuePair<MethodInfo, MethodInfo>[] swaps) { foreach (CodeInstruction instruction in instructions) { for (int i = 0; i < swaps.Length; i++) { if (CodeInstructionExtensions.Calls(instruction, swaps[i].Key)) { instruction.opcode = OpCodes.Call; instruction.operand = swaps[i].Value; break; } } yield return instruction; } } internal static KeyValuePair<MethodInfo, MethodInfo> Swap(MethodInfo from, MethodInfo to) { return new KeyValuePair<MethodInfo, MethodInfo>(from, to); } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { [HarmonyPostfix] private static void Postfix(Container __instance) { ContainerRegistry.Register(__instance); } } [HarmonyPatch(typeof(Player), "HaveRequirementItems", new Type[] { typeof(Recipe), typeof(bool), typeof(int), typeof(int) })] internal static class HaveRequirementItemsPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo)); } } [HarmonyPatch] internal static class GetFirstRequiredItemPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo), Redirect.Swap(MethodSwaps.GetItemFrom, MethodSwaps.GetItemTo)); } } [HarmonyPatch(typeof(Player), "ConsumeResources")] internal static class ConsumeResourcesPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.RemoveItemFrom, MethodSwaps.RemoveItemTo)); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })] internal static class DoCraftingPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.RemoveItemFrom, MethodSwaps.RemoveItemTo)); } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] internal static class SetupRequirementPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo)); } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] internal static class HavePieceRequirementsPatch { [HarmonyPrefix] private static void Prefix() { RoutingContext.EnterBuild(); } [HarmonyFinalizer] private static void Finalizer() { RoutingContext.ExitBuild(); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.HaveItemFrom, MethodSwaps.HaveItemTo), Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo)); } } [HarmonyPatch(typeof(Player), "UpdatePlacement", new Type[] { typeof(bool), typeof(float) })] internal static class UpdatePlacementPatch { [HarmonyPrefix] private static void Prefix() { RoutingContext.EnterBuild(); } [HarmonyFinalizer] private static void Finalizer() { RoutingContext.ExitBuild(); } } [HarmonyPatch(typeof(Hud), "SetupPieceInfo", new Type[] { typeof(Piece) })] internal static class SetupPieceInfoPatch { [HarmonyPrefix] private static void Prefix() { RoutingContext.EnterBuild(); } [HarmonyFinalizer] private static void Finalizer() { RoutingContext.ExitBuild(); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo)); } } [HarmonyPatch(typeof(ZNet), "Shutdown")] internal static class ZNetShutdownPatch { [HarmonyPostfix] private static void Postfix() { RefillTargetRegistry.Clear(); ContainerRegistry.Clear(); RoutingContext.Reset(); ConnectionVisuals.Cleanup(); KeywordSync.Reset(); MultiUserChestCompat.Reset(); RangeProvider.Invalidate(); } } internal static class InventoryBridge { private static bool TryGetContext(Inventory inventory, out RouteContext context) { context = null; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || inventory == null || inventory != ((Humanoid)localPlayer).GetInventory()) { return false; } return RoutingContext.TryGet(out context); } public static int CountItems(Inventory inventory, string name, int quality, bool matchWorldLevel) { int num = inventory.CountItems(name, quality, matchWorldLevel); if (!TryGetContext(inventory, out var context)) { return num; } List<Container> matching = ContainerRegistry.GetMatching(context); for (int i = 0; i < matching.Count; i++) { Inventory inventory2 = matching[i].GetInventory(); if (inventory2 != null) { num += inventory2.CountItems(name, quality, matchWorldLevel); } } return num; } public static bool HaveItem(Inventory inventory, string name, bool matchWorldLevel) { if (inventory.HaveItem(name, matchWorldLevel)) { return true; } if (!TryGetContext(inventory, out var context)) { return false; } List<Container> matching = ContainerRegistry.GetMatching(context); for (int i = 0; i < matching.Count; i++) { Inventory inventory2 = matching[i].GetInventory(); if (inventory2 != null && inventory2.HaveItem(name, matchWorldLevel)) { return true; } } return false; } public static ItemData GetItem(Inventory inventory, string name, int quality, bool isPrefabName) { ItemData item = inventory.GetItem(name, quality, isPrefabName); if (item != null) { return item; } if (!TryGetContext(inventory, out var context)) { return null; } List<Container> matching = ContainerRegistry.GetMatching(context); for (int i = 0; i < matching.Count; i++) { Inventory inventory2 = matching[i].GetInventory(); if (inventory2 != null) { item = inventory2.GetItem(name, quality, isPrefabName); if (item != null) { return item; } } } return null; } public static void RemoveItem(Inventory inventory, string name, int amount, int itemQuality, bool worldLevelBased) { if (!TryGetContext(inventory, out var context)) { inventory.RemoveItem(name, amount, itemQuality, worldLevelBased); return; } if (context.Definition != null && context.Definition.Kind == RouteKind.Refill && MultiUserChestCompat.Active) { MultiUserChestCompat.PendingAction pendingAction = MultiUserChestCompat.PrepareName(context, name, amount, itemQuality, worldLevelBased); if (pendingAction != null) { throw new MucPendingException(pendingAction); } } int num = amount; int num2 = inventory.CountItems(name, itemQuality, worldLevelBased); int num3 = Mathf.Min(num, num2); if (num3 > 0) { inventory.RemoveItem(name, num3, itemQuality, worldLevelBased); num -= num3; } if (num <= 0) { return; } List<Container> matching = ContainerRegistry.GetMatching(context); for (int i = 0; i < matching.Count; i++) { if (num <= 0) { break; } Container val = matching[i]; Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(val); if (effectiveInventory == null) { continue; } int num4 = effectiveInventory.CountItems(name, itemQuality, worldLevelBased); if (num4 > 0) { Inventory inventory2 = val.GetInventory(); if (effectiveInventory != inventory2 || ContainerRegistry.TryTakeOwnership(val)) { int num5 = Mathf.Min(num4, num); effectiveInventory.RemoveItem(name, num5, itemQuality, worldLevelBased); num -= num5; } } } if (num > 0 && Plugin.Log != null) { Plugin.Log.LogWarning((object)("Could not consume " + num + "x " + name + " after checking the player and " + matching.Count + " matching chest(s).")); } } public static int CountItemsByName(Inventory inventory, string[] names, int quality, bool matchWorldLevel, bool stacksOnly) { int num = inventory.CountItemsByName(names, quality, matchWorldLevel, stacksOnly); if (!TryGetContext(inventory, out var context)) { return num; } List<Container> matching = ContainerRegistry.GetMatching(context); for (int i = 0; i < matching.Count; i++) { Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(matching[i]); if (effectiveInventory != null) { num += effectiveInventory.CountItemsByName(names, quality, matchWorldLevel, stacksOnly); } } return num; } public static ItemData GetAmmoItem(Inventory inventory, string ammoName, string matchPrefabName) { ItemData ammoItem = inventory.GetAmmoItem(ammoName, matchPrefabName); if (ammoItem != null) { return ammoItem; } if (!TryGetContext(inventory, out var context)) { return null; } List<Container> matching = ContainerRegistry.GetMatching(context); for (int i = 0; i < matching.Count; i++) { Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(matching[i]); if (effectiveInventory != null) { ammoItem = effectiveInventory.GetAmmoItem(ammoName, matchPrefabName); if (ammoItem != null) { return ammoItem; } } } return null; } public static bool RemoveItemData(Inventory inventory, ItemData item, int amount) { if (inventory == null || item == null) { return false; } if (inventory.ContainsItem(item)) { return inventory.RemoveItem(item, amount); } if (!TryGetContext(inventory, out var context)) { return inventory.RemoveItem(item, amount); } if (context.Definition != null && context.Definition.Kind == RouteKind.Refill && MultiUserChestCompat.Active) { MultiUserChestCompat.PendingAction pendingAction = MultiUserChestCompat.PrepareExactItem(context, item, amount); if (pendingAction != null) { throw new MucPendingException(pendingAction); } } List<Container> matching = ContainerRegistry.GetMatching(context); for (int i = 0; i < matching.Count; i++) { Container val = matching[i]; Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(val); if (effectiveInventory != null && effectiveInventory.ContainsItem(item)) { Inventory inventory2 = val.GetInventory(); if (effectiveInventory == inventory2 && !ContainerRegistry.TryTakeOwnership(val)) { return false; } return effectiveInventory.RemoveItem(item, amount); } } return false; } public static bool RemoveOneItem(Inventory inventory, ItemData item) { return RemoveItemData(inventory, item, 1); } } internal static class KeywordSync { private const string RequestRpc = "WBCFNC_KeywordRequest"; private const string ConfigRpc = "WBCFNC_Keywords"; private static readonly Dictionary<string, string[]> ServerKeywords = new Dictionary<string, string[]>(StringComparer.Ordinal); private static bool _registered; internal static bool ServerEnforced { get; private set; } internal static string[] GetKeywords(string routeId, string[] localValues) { if (ServerEnforced && routeId != null && ServerKeywords.TryGetValue(routeId, out var value)) { return value; } return localValues ?? new string[0]; } internal static void Register() { if (!_registered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("WBCFNC_KeywordRequest", (Action<long>)OnConfigRequested); ZRoutedRpc.instance.Register<ZPackage>("WBCFNC_Keywords", (Action<long, ZPackage>)OnConfigReceived); _registered = true; } } internal static void RequestFromServer() { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC("WBCFNC_KeywordRequest", Array.Empty<object>()); } } internal static void NotifyLocalKeywordChanged() { ContainerRegistry.Invalidate(); if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZPackage val = BuildPackage(); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "WBCFNC_Keywords", new object[1] { val }); } } internal static void Reset() { _registered = false; ServerEnforced = false; ServerKeywords.Clear(); } private static void OnConfigRequested(long sender) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(sender, "WBCFNC_Keywords", new object[1] { BuildPackage() }); } } private static ZPackage BuildPackage() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); List<RouteDefinition> list = new List<RouteDefinition>(RouteRegistry.All); val.Write(list.Count); for (int i = 0; i < list.Count; i++) { RouteDefinition routeDefinition = list[i]; val.Write(routeDefinition.Id ?? ""); for (int j = 0; j < 3; j++) { string text = ""; if (routeDefinition.KeywordEntries != null && j < routeDefinition.KeywordEntries.Length && routeDefinition.KeywordEntries[j] != null) { text = routeDefinition.KeywordEntries[j].Value ?? ""; } val.Write(text); } } return val; } private static void OnConfigReceived(long sender, ZPackage package) { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || package == null) { return; } ServerKeywords.Clear(); int num = package.ReadInt(); for (int i = 0; i < num; i++) { string text = package.ReadString(); string[] array = new string[3]; for (int j = 0; j < array.Length; j++) { array[j] = package.ReadString() ?? ""; } if (!string.IsNullOrEmpty(text)) { ServerKeywords[text] = array; } } ServerEnforced = true; ContainerRegistry.ReindexAll(); if (Plugin.Log != null) { Plugin.Log.LogInfo((object)("Using " + ServerKeywords.Count + " station/tool chest keyword(s) from the server.")); } } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class ZNetAwakeKeywordSyncPatch { [HarmonyPostfix] private static void Postfix() { KeywordSync.Reset(); KeywordSync.Register(); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class ZNetPeerInfoKeywordSyncPatch { [HarmonyPostfix] private static void Postfix() { KeywordSync.Register(); KeywordSync.RequestFromServer(); } } internal enum ChestNameHover { Vanilla, CustomName, TypeThenCustomName, CustomNameThenType } internal static class ModConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> ShowConnections; internal static ConfigEntry<bool> EnableCustomNames; internal static ConfigEntry<ChestNameHover> HoverNameFormat; internal static ConfigEntry<bool> ShowRenameHint; internal static ConfigFile File; internal static void Bind(ConfigFile config) { File = config; Enabled = config.Bind<bool>("General", "Enabled", true, "Enable crafting and building from correctly named nearby chests."); ShowConnections = config.Bind<bool>("Visuals", "Show connections", true, "Show vanilla-style connection effects for linked chests."); EnableCustomNames = config.Bind<bool>("Chest Renaming", "Enable custom names", true, "Enable the standalone chest renaming fallback when another compatible chest renamer is not installed."); HoverNameFormat = config.Bind<ChestNameHover>("Chest Renaming", "Hover name format", ChestNameHover.TypeThenCustomName, "Chest name format shown in hover when the standalone rename fallback is active."); ShowRenameHint = config.Bind<bool>("Chest Renaming", "Show rename hint in hover", true, "Show the Alt+Use rename hint when the standalone rename fallback is active."); } internal static ConfigEntry<string>[] BindStationKeywords(string key, params string[] defaults) { return BindKeywordSlots("Station Keywords", key, defaults, "Chest titles containing this alternative are assigned to this crafting station. Matching is case-insensitive; spaces are literal. Leave blank to disable this alternative. If another route already uses the same alias, this field is automatically cleared."); } internal static ConfigEntry<string>[] BindToolKeywords(string key, params string[] defaults) { return BindKeywordSlots("Build Tool Keywords", key, defaults, "Chest titles containing this alternative are assigned to this build tool. Matching is case-insensitive; spaces are literal. Leave blank to disable this alternative. If another route already uses the same alias, this field is automatically cleared."); } internal static ConfigEntry<string>[] BindRefillKeywords(string key, params string[] defaults) { return BindKeywordSlots("Refill Keywords", key, defaults, "Chest titles containing this alternative are assigned to this refill/feed route. Matching is case-insensitive; spaces are literal. Leave blank to disable this alternative. If another route already uses the same alias, this field is automatically cleared."); } private static ConfigEntry<string>[] BindKeywordSlots(string section, string key, string[] defaults, string description) { ConfigEntry<string>[] array = new ConfigEntry<string>[3]; for (int i = 0; i < array.Length; i++) { string text = ((defaults != null && i < defaults.Length) ? (defaults[i] ?? "") : ""); array[i] = File.Bind<string>(section, key + " " + (i + 1), text, description); } return array; } } internal static class MucActionPreflight { internal static readonly FieldInfo CraftRecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe"); internal static readonly FieldInfo CraftUpgradeItemField = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem"); internal static readonly FieldInfo CraftVariantField = AccessTools.Field(typeof(InventoryGui), "m_craftVariant"); internal static readonly FieldInfo MultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting"); internal static readonly FieldInfo MultiCraftAmountField = AccessTools.Field(typeof(InventoryGui), "m_multiCraftAmount"); internal static List<MultiUserChestCompat.Demand> RequirementDemands(Requirement[] requirements, int quality, int multiplier, CraftingStation station) { List<MultiUserChestCompat.Demand> list = new List<MultiUserChestCompat.Demand>(); if (requirements == null) { return list; } Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); foreach (Requirement val in requirements) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && (!((Object)(object)station != (Object)null) || station.m_upgrader == val.m_upgraderResource) && (!((Object)(object)station == (Object)null) || !val.m_upgraderResource)) { int num = val.GetAmount(quality) * multiplier; if (num > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; dictionary.TryGetValue(name, out var value); dictionary[name] = value + num; } } } foreach (KeyValuePair<string, int> item in dictionary) { list.Add(new MultiUserChestCompat.Demand { Name = item.Key, Amount = item.Value, Quality = -1, MatchWorldLevel = true }); } return list; } internal static void Missing(Player player) { if ((Object)(object)player != (Object)null) { ((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false); } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })] internal static class MucCraftActionPatch { [HarmonyPrefix] private static bool Prefix(InventoryGui __instance, Player player, MethodBase __originalMethod) { if (!MultiUserChestCompat.Active || MultiUserChestCompat.Executing) { return true; } if (MultiUserChestCompat.Busy) { return false; } if ((Object)(object)__instance == (Object)null || (Object)(object)player == (Object)null || player.NoCostCheat() || (Object)(object)ZoneSystem.instance == (Object)null || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)) { return true; } Recipe recipe = default(Recipe); ref Recipe reference = ref recipe; object? value = MucActionPreflight.CraftRecipeField.GetValue(__instance); reference = (Recipe)((value is Recipe) ? value : null); if ((Object)(object)recipe == (Object)null) { return true; } ItemData upgrade = default(ItemData); ref ItemData reference2 = ref upgrade; object? value2 = MucActionPreflight.CraftUpgradeItemField.GetValue(__instance); reference2 = (ItemData)((value2 is ItemData) ? value2 : null); int num = ((upgrade == null) ? 1 : (upgrade.m_quality + 1)); bool multi = (bool)MucActionPreflight.MultiCraftingField.GetValue(__instance); int multiplier = ((!multi) ? 1 : ((int)MucActionPreflight.MultiCraftAmountField.GetValue(__instance))); int variant = (int)MucActionPreflight.CraftVariantField.GetValue(__instance); CraftingStation station = player.GetCurrentCraftingStation(); if (!RoutingContext.TryGet(out var context)) { return true; } MultiUserChestCompat.PendingAction pendingAction; if (recipe.m_requireOnlyOneIngredient) { int num2 = default(int); ItemData val = default(ItemData); recipe.GetAmount(num, ref num2, ref val, multiplier); pendingAction = ((val != null && num2 > 0) ? MultiUserChestCompat.PrepareExactItem(context, val, num2) : null); } else { List<MultiUserChestCompat.Demand> demands = MucActionPreflight.RequirementDemands(recipe.m_resources, num, multiplier, station); pendingAction = MultiUserChestCompat.Prepare(context, demands); } if (pendingAction == null) { return true; } Func<bool> stillValid = delegate { if ((Object)(object)__instance == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || !InventoryGui.IsVisible()) { return false; } if (MucActionPreflight.CraftRecipeField.GetValue(__instance) != recipe) { return false; } if (MucActionPreflight.CraftUpgradeItemField.GetValue(__instance) != upgrade) { return false; } if ((int)MucActionPreflight.CraftVariantField.GetValue(__instance) != variant) { return false; } if ((bool)MucActionPreflight.MultiCraftingField.GetValue(__instance) != multi) { return false; } if (multi && (int)MucActionPreflight.MultiCraftAmountField.GetValue(__instance) != multiplier) { return false; } if ((Object)(object)player.GetCurrentCraftingStation() != (Object)(object)station) { return false; } return (Object)(object)station == (Object)null || station.CheckUsable(player, false); }; Action ready = delegate { __originalMethod.Invoke(__instance, new object[1] { player }); }; MultiUserChestCompat.Attach(pendingAction, stillValid, ready, delegate { MucActionPreflight.Missing(player); }); return false; } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class MucBuildActionPatch { private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost"); private static readonly FieldInfo LastToolUseField = AccessTools.Field(typeof(Player), "m_lastToolUseTime"); private static readonly FieldInfo BuildRemoveDebtField = AccessTools.Field(typeof(Player), "m_buildRemoveDebt"); private static readonly MethodInfo GetBuildStaminaMethod = AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null); private static readonly MethodInfo GetPlaceDurabilityMethod = AccessTools.Method(typeof(Player), "GetPlaceDurability", new Type[1] { typeof(ItemData) }, (Type[])null); [HarmonyPrefix] private static bool Prefix(Player __instance, Piece piece, ref bool __result) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) if (!MultiUserChestCompat.Active || MultiUserChestCompat.Executing) { return true; } if (MultiUserChestCompat.Busy) { __result = false; return false; } if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)piece == (Object)null || __instance.NoCostCheat() || (Object)(object)ZoneSystem.instance == (Object)null || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { return true; } GameObject val = (GameObject)((PlacementGhostField != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { return true; } ItemData tool = ((Humanoid)__instance).RightItem; if (tool == null || tool.m_shared == null || (Object)(object)tool.m_shared.m_buildPieces == (Object)null) { return true; } Vector3 position = val.transform.position; Quaternion rotation = val.transform.rotation; RoutingContext.EnterBuild(); RouteContext context; try { if (!RoutingContext.TryGet(out context)) { return true; } } finally { RoutingContext.ExitBuild(); } List<MultiUserChestCompat.Demand> demands = MucActionPreflight.RequirementDemands(piece.m_resources, 0, 1, null); MultiUserChestCompat.PendingAction pendingAction = MultiUserChestCompat.Prepare(context, demands); if (pendingAction == null) { return true; } Func<bool> stillValid = delegate { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead() || !((Character)__instance).InPlaceMode() || (Object)(object)__instance.GetSelectedPiece() != (Object)(object)piece || ((Humanoid)__instance).RightItem != tool) { return false; } GameObject val2 = (GameObject)((PlacementGhostField != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val2 == (Object)null) { return false; } Vector3 val3 = val2.transform.position - position; if (((Vector3)(ref val3)).sqrMagnitude >= 0.01f || Quaternion.Angle(val2.transform.rotation, rotation) >= 1f) { return false; } float buildStamina = GetBuildStamina(__instance); return ((Character)__instance).HaveStamina(buildStamina); }; Action ready = delegate { CompleteBuild(__instance, piece, tool); }; MultiUserChestCompat.Attach(pendingAction, stillValid, ready, delegate { MucActionPreflight.Missing(__instance); }); __result = false; return false; } private static void CompleteBuild(Player player, Piece piece, ItemData tool) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Invalid comparison between Unknown and I4 //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Invalid comparison between Unknown and I4 if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || tool == null) { return; } RoutingContext.EnterBuild(); try { if (!player.HaveRequirements(piece, (RequirementMode)0)) { MucActionPreflight.Missing(player); } else { if (!player.TryPlacePiece(piece)) { return; } if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_buildUi != (Object)null) { Hud.instance.m_buildUi.AddRecentPiece(piece); } if (LastToolUseField != null) { LastToolUseField.SetValue(player, Time.time); } if (!ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { player.ConsumeResources(piece.m_resources, 0, -1, 1); } ((Character)player).UseStamina(GetBuildStamina(player)); PieceTable buildPieces = tool.m_shared.m_buildPieces; if ((Object)(object)buildPieces != (Object)null && (int)buildPieces.m_skill != 0) { if ((int)buildPieces.m_skill == 107) { Game.instance.IncrementPlayerStat((PlayerStatType)168, 1f, false); } int num = ((BuildRemoveDebtField != null) ? ((int)BuildRemoveDebtField.GetValue(player)) : 0); if (num > 0) { BuildRemoveDebtField.SetValue(player, num - 1); } else { ((Character)player).RaiseSkill(buildPieces.m_skill, 1f); if ((int)buildPieces.m_skill == 107) { Game.instance.IncrementPlayerStat((PlayerStatType)169, 1f, false); } } } if (tool.m_shared.m_useDurability) { tool.m_durability -= GetPlaceDurability(player, tool) * Game.m_durabilityRate; } tool.m_shared.m_buildEffect.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)player).GetZDOID()); } } finally { RoutingContext.ExitBuild(); } } private static float GetBuildStamina(Player player) { if ((Object)(object)player == (Object)null || GetBuildStaminaMethod == null) { return 0f; } object obj = GetBuildStaminaMethod.Invoke(player, null); if (!(obj is float)) { return 0f; } return (float)obj; } private static float GetPlaceDurability(Player player, ItemData tool) { if ((Object)(object)player == (Object)null || tool == null || GetPlaceDurabilityMethod == null) { return 0f; } object obj = GetPlaceDurabilityMethod.Invoke(player, new object[1] { tool }); if (!(obj is float)) { return 0f; } return (float)obj; } } internal sealed class MucPendingException : Exception { internal readonly MultiUserChestCompat.PendingAction Pending; internal MucPendingException(MultiUserChestCompat.PendingAction pending) { Pending = pending; } } internal static class MultiUserChestCompat { internal sealed class Demand { internal string Name; internal int Amount; internal int Quality = -1; internal bool MatchWorldLevel = true; internal string PrefabName; internal Container SourceOnly; internal bool IgnorePlayer; } internal sealed class PendingAction { internal RouteContext Context; internal readonly List<Demand> Demands = new List<Demand>(); internal readonly Dictionary<Container, Inventory> Reserves = new Dictionary<Container, Inventory>(); internal readonly HashSet<Container> Concurrent = new HashSet<Container>(); internal readonly List<int> RequestIds = new List<int>(); internal Func<bool> StillValid; internal Action Ready; internal Action Failed; internal float Started; internal bool Blocked; } private sealed class RefundJob { internal Container Container; internal Inventory Reserve; internal readonly List<int> RequestIds = new List<int>(); internal float NextTry; } internal const string PluginGuid = "com.maxsch.valheim.MultiUserChest"; private static bool _initialized; private static bool _available; private static MethodInfo _removeItemFromChest; private static MethodInfo _addItemToChest; private static MethodInfo _getPackageGeneric; private static Type _iPackageType; private static PendingAction _pending; private static PendingAction _active; private static readonly List<RefundJob> Refunds = new List<RefundJob>(); internal static bool Executing => _active != null; internal static bool Busy => _pending != null; internal static bool Active { get { EnsureInitialized(); return _available; } } private static void EnsureInitialized() { if (_initialized) { return; } _initialized = true; if (!Chainloader.PluginInfos.ContainsKey("com.maxsch.valheim.MultiUserChest")) { return; } try { Assembly assembly = ((object)Chainloader.PluginInfos["com.maxsch.valheim.MultiUserChest"].Instance).GetType().Assembly; Type type = assembly.GetType("MultiUserChest.ContainerHandler"); Type type2 = assembly.GetType("MultiUserChest.PackageHandler"); _iPackageType = assembly.GetType("MultiUserChest.IPackage"); if (type != null) { MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "RemoveItemFromChest" && methodInfo.GetParameters().Length == 7) { _removeItemFromChest = methodInfo; } if (methodInfo.Name == "AddItemToChest" && methodInfo.GetParameters().Length == 6) { _addItemToChest = methodInfo; } } } if (type2 != null) { MethodInfo[] methods2 = type2.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods2) { if (methodInfo2.Name == "GetPackage" && methodInfo2.IsGenericMethodDefinition && methodInfo2.GetParameters().Length == 1) { _getPackageGeneric = methodInfo2; break; } } } _available = _removeItemFromChest != null && _addItemToChest != null && _getPackageGeneric != null && _iPackageType != null; if (_available && Plugin.Log != null) { Plugin.Log.LogInfo((object)"MultiUserChest compatibility enabled."); } } catch (Exception ex) { _available = false; if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("Could not initialize MultiUserChest compatibility: " + ex.Message)); } } } internal static bool IsConcurrentRemote(Container container) { if (!Active || (Object)(object)container == (Object)null) { return false; } ZNetView netView = ContainerRegistry.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid() || !netView.HasOwner() || netView.IsOwner()) { return false; } ZDO zDO = netView.GetZDO(); if (!container.IsInUse()) { if (zDO != null) { return zDO.GetInt(ZDOVars.s_inUse, 0) == 1; } return false; } return true; } internal static Inventory GetEffectiveInventory(Container container) { if ((Object)(object)container == (Object)null) { return null; } if (_active != null) { if (_active.Reserves.TryGetValue(container, out var value)) { return value; } if (_active.Concurrent.Contains(container)) { return null; } } return container.GetInventory(); } internal static void Update() { UpdatePending(); UpdateRefunds(); } private static void UpdatePending() { PendingAction pending = _pending; if (pending == null) { return; } if (pending.Blocked) { if (pending.Ready != null || pending.Failed != null) { _pending = null; FailPending(pending); } return; } bool flag = false; for (int i = 0; i < pending.RequestIds.Count; i++) { if (RequestExists(pending.RequestIds[i])) { flag = true; break; } } if (flag) { if (!(Time.realtimeSinceStartup - pending.Started < 8f)) { _pending = null; FailPending(pending); } } else { if (pending.Ready == null && pending.Failed == null) { return; } if (CanSatisfy(pending) && (pending.StillValid == null || pending.StillValid())) { _pending = null; _active = pending; try { if (pending.Ready != null) { pending.Ready(); } return; } catch (Exception ex) { if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("MultiUserChest routed action failed: " + ex.Message)); } if (pending.Failed != null) { pending.Failed(); } return; } finally { _active = null; RefundLeftovers(pending); } } _pending = null; FailPending(pending); } } private static void FailPending(PendingAction pending) { RefundLeftovers(pending); if (pending != null && pending.Failed != null) { try { pending.Failed(); } catch { } } } internal static void Attach(PendingAction pending, Func<bool> stillValid, Action ready, Action failed) { if (pending != null && pending.Ready == null && pending.Failed == null) { pending.StillValid = stillValid; pending.Ready = ready; pending.Failed = failed; } } internal static PendingAction PrepareName(RouteContext context, string name, int amount, int quality, bool matchWorldLevel) { Demand item = new Demand { Name = name, Amount = amount, Quality = quality, MatchWorldLevel = matchWorldLevel }; return Prepare(context, new List<Demand> { item }); } internal static PendingAction PrepareExactItem(RouteContext context, ItemData item, int amount) { if (context == null || item == null || item.m_shared == null || amount <= 0) { return null; } List<Container> matching = ContainerRegistry.GetMatching(context); Container val = null; for (int i = 0; i < matching.Count; i++) { Inventory val2 = (((Object)(object)matching[i] != (Object)null) ? matching[i].GetInventory() : null); if (val2 != null && val2.ContainsItem(item)) { val = matching[i]; break; } } if ((Object)(object)val == (Object)null || !IsConcurrentRemote(val)) { return null; } Demand item2 = new Demand { Name = item.m_shared.m_name, Amount = amount, Quality = item.m_quality, MatchWorldLevel = false, PrefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null), SourceOnly = val, IgnorePlayer = true }; return Prepare(context, new List<Demand> { item2 }); } internal static PendingAction Prepare(RouteContext context, List<Demand> demands) { if (!Active || Executing || context == null || demands == null || demands.Count == 0) { return null; } if (_pending != null) { return _pending; } List<Container> matching = ContainerRegistry.GetMatching(context); if (matching.Count == 0) { return null; } PendingAction pendingAction = new PendingAction { Context = context, Started = Time.realtimeSinceStartup }; for (int i = 0; i < matching.Count; i++) { Container val = matching[i]; if (IsConcurrentRemote(val)) { pendingAction.Concurrent.Add(val); } } if (pendingAction.Concurrent.Count == 0) { return null; } bool flag = false; bool flag2 = true; for (int j = 0; j < demands.Count; j++) { Demand demand = demands[j]; if (demand == null || string.IsNullOrEmpty(demand.Name) || demand.Amount <= 0) { continue; } pendingAction.Demands.Add(demand); int num = demand.Amount; if (!demand.IgnorePlayer) { Player localPlayer = Player.m_localPlayer; Inventory val2 = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val2 != null) { num -= Mathf.Min(num, Count(val2, demand)); } } if (num <= 0) { continue; } for (int k = 0; k < matching.Count; k++) { if (num <= 0) { break; } Container val3 = matching[k]; if ((Object)(object)val3 == (Object)null || ((Object)(object)demand.SourceOnly != (Object)null && (Object)(object)val3 != (Object)(object)demand.SourceOnly)) { continue; } Inventory inventory = val3.GetInventory(); if (inventory == null) { continue; } if (!pendingAction.Concurrent.Contains(val3)) { int num2 = Count(inventory, demand); num -= Mathf.Min(num, num2); continue; } int num3 = RequestFromConcurrent(pendingAction, val3, inventory, demand, num); if (num3 > 0) { flag = true; num -= num3; } } if (num > 0) { flag2 = false; } } if (!flag) { return null; } if (!flag2 && pendingAction.RequestIds.Count == 0) { pendingAction.Blocked = true; } _pending = pendingAction; return pendingAction; } private static int RequestFromConcurrent(PendingAction pending, Container container, Inventory source, Demand demand, int amount) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown if (pending == null || (Object)(object)container == (Object)null || source == null || demand == null || amount <= 0) { return 0; } if (!pending.Reserves.TryGetValue(container, out var value)) { value = new Inventory("Wire MUC Reservation", (Sprite)null, 20, 20); pending.Reserves.Add(container, value); } int num = 0; List<ItemData> list = RefillSelection.BottomLeftFirst(source); for (int i = 0; i < list.Count; i++) { if (num >= amount) { break; } ItemData val = list[i]; if (!Matches(val, demand)) { continue; } int amount2 = Mathf.Min(val.m_stack, amount - num); int requestId; int num2 = RequestRemove(container, val, value, amount2, out requestId); if (num2 > 0) { num += num2; if (requestId != 0) { pending.RequestIds.Add(requestId); } } } return num; } private static int RequestRemove(Container container, ItemData item, Inventory reserve, int amount, out int requestId) { //IL_0066: 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) requestId = 0; if (!_available || (Object)(object)container == (Object)null || item == null || reserve == null || amount <= 0) { return 0; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return 0; } int num = reserve.CountItems(item.m_shared.m_name, item.m_quality, false); try { object obj = _removeItemFromChest.Invoke(null, new object[7] { container, item, reserve, (object)new Vector2i(-1, -1), ((Character)localPlayer).GetZDOID(), amount, null }); if (obj == null) { int num2 = reserve.CountItems(item.m_shared.m_name, item.m_quality, false); return Mathf.Max(0, num2 - num); } Type type = obj.GetType(); PropertyInfo property = type.GetProperty("RequestID"); FieldInfo field = type.GetField("dragAmount"); if (property != null) { requestId = (int)property.GetValue(obj, null); } return (field != null) ? Mathf.Max(0, (int)field.GetValue(obj)) : amount; } catch (Exception ex) { if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("MultiUserChest reservation failed: " + ex.Message)); } return 0; } } private static bool CanSatisfy(PendingAction pending) { if (pending == null || pending.Context == null) { return false; } List<Container> matching = ContainerRegistry.GetMatching(pending.Context); for (int i = 0; i < pending.Demands.Count; i++) { Demand demand = pending.Demands[i]; int num = demand.Amount; if (!demand.IgnorePlayer) { Player localPlayer = Player.m_localPlayer; Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val != null) { num -= Mathf.Min(num, Count(val, demand)); } } for (int j = 0; j < matching.Count && num > 0; j++) { Container val2 = matching[j]; if ((Object)(object)demand.SourceOnly != (Object)null && (Object)(object)val2 != (Object)(object)demand.SourceOnly) { continue; } Inventory value = null; if (!pending.Reserves.TryGetValue(val2, out value)) { if (pending.Concurrent.Contains(val2)) { continue; } value = (((Object)(object)val2 != (Object)null) ? val2.GetInventory() : null); } if (value != null) { num -= Mathf.Min(num, Count(value, demand)); } } if (num > 0) { return false; } } return true; } private static int Count(Inventory inventory, Demand demand) { if (inventory == null || demand == null) { return 0; } int num = 0; List<ItemData> allItems = inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (Matches(val, demand)) { num += val.m_stack; } } return num; } private static bool Matches(ItemData item, Demand demand) { if (item == null || item.m_shared == null || demand == null || item.m_shared.m_name != demand.Name) { return false; } if (demand.Quality >= 0 && item.m_quality != demand.Quality) { return false; } if (demand.MatchWorldLevel && item.m_worldLevel < Game.m_worldLevel) { return false; } if (!string.IsNullOrEmpty(demand.PrefabName) && ((Object)(object)item.m_dropPrefab == (Object)null || ((Object)item.m_dropPrefab).name != demand.PrefabName)) { return false; } return true; } private static bool RequestExists(int id) { if (!_available || id == 0) { return false; } try { return _getPackageGeneric.MakeGenericMethod(_iPackageType).Invoke(null, new object[1] { id }) != null; } catch { return false; } } internal static ItemData FindReservedItem(string name, int quality, string prefabName) { if (_active == null) { return null; } List<Container> matching = ContainerRegistry.GetMatching(_active.Context); for (int i = 0; i < matching.Count; i++) { if (!_active.Reserves.TryGetValue(matching[i], out var value)) { continue; } List<ItemData> list = RefillSelection.BottomLeftFirst(value); for (int j = 0; j < list.Count; j++) { ItemData val = list[j]; if (val != null && val.m_shared != null && !(val.m_shared.m_name != name) && (quality < 0 || val.m_quality == quality) && (string.IsNullOrEmpty(prefabName) || (!((Object)(object)val.m_dropPrefab == (Object)null) && !(((Object)val.m_dropPrefab).name != prefabName)))) { return val; } } } return null; } internal static object[] RefreshItemArgs(object[] args) { if (args == null || _active == null) { return args; } object[] array = (object[])args.Clone(); for (int i = 0; i < array.Length; i++) { object obj = array[i]; ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val == null || val.m_shared == null) { continue; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null) || !((Humanoid)localPlayer).GetInventory().ContainsItem(val)) { string prefabName = (((Object)(object)val.m_dropPrefab != (Object)null) ? ((Object)val.m_dropPrefab).name : null); ItemData val2 = FindReservedItem(val.m_shared.m_name, val.m_quality, prefabName); if (val2 != null) { array[i] = val2; } } } return array; } private static void RefundLeftovers(PendingAction pending) { if (pending == null || pending.Reserves.Count == 0) { return; } foreach (KeyValuePair<Container, Inventory> reserf in pending.Reserves) { if (!((Object)(object)reserf.Key == (Object)null) && reserf.Value != null && reserf.Value.NrOfItems() != 0) { RefundJob item = new RefundJob { Container = reserf.Key, Reserve = reserf.Value, NextTry = Time.realtimeSinceStartup }; Refunds.Add(item); } } } private static void UpdateRefunds() { if (!Active || Refunds.Count == 0) { return; } for (int num = Refunds.Count - 1; num >= 0; num--) { RefundJob refundJob = Refunds[num]; if (refundJob == null || refundJob.Reserve == null) { Refunds.RemoveAt(num); } else { bool flag = false; for (int num2 = refundJob.RequestIds.Count - 1; num2 >= 0; num2--) { if (RequestExists(refundJob.RequestIds[num2])) { flag = true; } else { refundJob.RequestIds.RemoveAt(num2); } } if (!flag) { if (refundJob.Reserve.NrOfItems() == 0) { Refunds.RemoveAt(num); } else if ((Object)(object)refundJob.Container == (Object)null) { ReturnReserveToPlayer(refundJob.Reserve); Refunds.RemoveAt(num); } else if (!(Time.realtimeSinceStartup < refundJob.NextTry)) { refundJob.NextTry = Time.realtimeSinceStartup + 0.5f; StartRefund(refundJob); } } } } } private static void StartRefund(RefundJob job) { //IL_0082: 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) Player localPlayer = Player.m_localPlayer; if (job == null || (Object)(object)job.Container == (Object)null || job.Reserve == null || (Object)(object)localPlayer == (Object)null) { return; } List<ItemData> list = new List<ItemData>(job.Reserve.GetAllItems()); for (int i = 0; i < list.Count; i++) { ItemData val = list[i]; if (val == null || val.m_stack <= 0) { continue; } try { object obj = _addItemToChest.Invoke(null, new object[6] { job.Container, val, job.Reserve, (object)new Vector2i(-1, -1), ((Character)localPlayer).GetZDOID(), val.m_stack }); if (obj == null) { continue; } PropertyInfo property = obj.GetType().GetProperty("RequestID"); if (property != null) { int num = (int)property.GetValue(obj, null); if (num != 0) { job.RequestIds.Add(num); } } } catch (Exception ex) { if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("MultiUserChest refund failed: " + ex.Message)); } } } } private static void ReturnReserveToPlayer(Inventory reserve) { Player localPlayer = Player.m_localPlayer; if (reserve == null || (Object)(object)localPlayer == (Object)null) { return; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); List<ItemData> list = new List<ItemData>(reserve.GetAllItems()); for (int i = 0; i < list.Count; i++) { ItemData val = list[i]; if (val != null) { inventory.MoveItemToThis(reserve, val); } } } internal static void Reset() { if (_pending != null) { RefundLeftovers(_pending); } if (_active != null) { RefundLeftovers(_active); } _pending = null; _active = null; _initialized = false; _available = false; _removeItemFromChest = null; _addItemToChest = null; _getPackageGeneric = null; _iPackageType = null; } } [BepInPlugin("wire.valheim.balancedcraftfromnearbychests", "Wire's Balanced Craft From Nearby Chests", "0.1.1")] [BepInProcess("valheim.exe")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "wire.valheim.balancedcraftfromnearbychests"; public const string Name = "Wire's Balanced Craft From Nearby Chests"; public const string Version = "0.1.1"; internal static Plugin Instance; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL