Decompiled source of PlanBuildDupeFix v1.0.1

PlanBuildDupeFix.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
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("PlanBuildDupeFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+13f792a862a96ff7f354c9cd85f4c5cfdb005caf")]
[assembly: AssemblyProduct("PlanBuildDupeFix")]
[assembly: AssemblyTitle("PlanBuildDupeFix")]
[assembly: AssemblyVersion("1.0.1.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 PlanBuildDupeFix
{
	[BepInPlugin("qua8ion.valheim.planbuilddupefix", "PlanBuildDupeFix", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PlanBuildDupeFixPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(TeleportWorld), "SetText")]
		private static class TeleportWorldSetTextPatch
		{
			private static bool Prefix(string text, ZNetView ___m_nview)
			{
				if (HasLocalUser())
				{
					return true;
				}
				if ((Object)(object)___m_nview != (Object)null && ___m_nview.IsValid())
				{
					___m_nview.InvokeRPC("RPC_SetTag", new object[2] { text, "" });
					CountText("portal");
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Sign), "SetText")]
		private static class SignSetTextPatch
		{
			private static bool Prefix(string text, ZNetView ___m_nview)
			{
				if (HasLocalUser())
				{
					return true;
				}
				if ((Object)(object)___m_nview != (Object)null && ___m_nview.IsValid())
				{
					___m_nview.ClaimOwnership();
					___m_nview.GetZDO().Set(ZDOVars.s_text, text);
					___m_nview.GetZDO().Set(ZDOVars.s_author, "");
					CountText("sign");
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "LoadChunks")]
		private static class LoadChunksPatch
		{
			private static void Postfix()
			{
				//IL_011c: Unknown result type (might be due to invalid IL or missing references)
				if (!removeStackedCopies.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
				{
					return;
				}
				List<ZDO> list = (from val in ZDOMan.instance.GetPortalList()
					where val.GetLong(ZDOVars.s_creator, 0L) == 0
					select val).ToList();
				HashSet<ZDO> hashSet = new HashSet<ZDO>();
				foreach (ZDO zdo in list)
				{
					if (hashSet.Contains(zdo))
					{
						continue;
					}
					List<ZDO> list2 = list.Where((ZDO other) => other.GetPrefab() == zdo.GetPrefab() && Vector3.Distance(other.GetPosition(), zdo.GetPosition()) < 0.05f).ToList();
					hashSet.UnionWith(list2);
					if (list2.Count < 2)
					{
						continue;
					}
					foreach (ZDO item in list2)
					{
						item.SetOwner(ZDOMan.GetSessionID());
						ZDOMan.instance.DestroyZDO(item);
					}
					log.LogInfo((object)$"Removed {list2.Count} portal copies without a creator stacked at {zdo.GetPosition()}");
				}
			}
		}

		private static class XPortalList
		{
			private static object manager;

			private static MethodInfo getList;

			private static PropertyInfo portalId;

			private static Func<ZDOID, bool> isKnown;

			private static Action<long, ZDOID> removeRequest;

			private static readonly HashSet<ZDOID> destroyed = new HashSet<ZDOID>();

			internal static bool Ready { get; private set; }

			internal static bool Init()
			{
				Type type = AccessTools.TypeByName("XPortal.KnownPortalsManager");
				Type type2 = AccessTools.TypeByName("XPortal.KnownPortal");
				MethodInfo methodInfo = AccessTools.Method("XPortal.RPC.Server.ServerEvents:RPC_RemoveRequest", (Type[])null, (Type[])null);
				if (type == null || type2 == null || methodInfo == null)
				{
					return false;
				}
				manager = AccessTools.Property(type, "Instance")?.GetValue(null);
				getList = AccessTools.Method(type, "GetList", (Type[])null, (Type[])null);
				portalId = AccessTools.Property(type2, "Id");
				MethodInfo methodInfo2 = AccessTools.Method(type, "ContainsId", (Type[])null, (Type[])null);
				if (manager == null || getList == null || portalId == null || methodInfo2 == null)
				{
					return false;
				}
				isKnown = AccessTools.MethodDelegate<Func<ZDOID, bool>>(methodInfo2, manager, true);
				removeRequest = AccessTools.MethodDelegate<Action<long, ZDOID>>(methodInfo, (object)null, true);
				Ready = true;
				return true;
			}

			internal static void OnDestroyed(ZDOID id)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				if (Ready && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && isKnown(id))
				{
					destroyed.Add(id);
				}
			}

			internal static void RemoveNonPortals()
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: 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_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0121: Unknown result type (might be due to invalid IL or missing references)
				//IL_0139: Unknown result type (might be due to invalid IL or missing references)
				//IL_014f: Unknown result type (might be due to invalid IL or missing references)
				Dictionary<ZDOID, string> dictionary = new Dictionary<ZDOID, string>();
				foreach (ZDOID item in destroyed)
				{
					dictionary[item] = "its object was removed";
				}
				destroyed.Clear();
				foreach (object item2 in (IEnumerable)getList.Invoke(manager, null))
				{
					ZDOID val = (ZDOID)portalId.GetValue(item2);
					ZDO zDO = ZDOMan.instance.GetZDO(val);
					GameObject val2 = ((zDO != null) ? ZNetScene.instance.GetPrefab(zDO.GetPrefab()) : null);
					if ((Object)(object)val2 != (Object)null && ((Object)val2).name.EndsWith("_planned", StringComparison.Ordinal))
					{
						dictionary[val] = "it is a plan (" + ((Object)val2).name + ")";
					}
				}
				foreach (KeyValuePair<ZDOID, string> item3 in dictionary)
				{
					if (isKnown(item3.Key))
					{
						removeRequest(ZDOMan.GetSessionID(), item3.Key);
						log.LogInfo((object)$"Removed {item3.Key} from the XPortal list: {item3.Value}");
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")]
		private static class HandleDestroyedZDOPatch
		{
			private static void Prefix(ZDOID uid)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				XPortalList.OnDestroyed(uid);
			}
		}

		[HarmonyPatch]
		private static class SpawnPieceAndDestroyPatch
		{
			internal static MethodBase Target;

			private static bool Prepare()
			{
				Target = AccessTools.Method("PlanBuild.Plans.PlanPiece:RPC_SpawnPieceAndDestroy", (Type[])null, (Type[])null);
				return Target != null;
			}

			private static MethodBase TargetMethod()
			{
				return Target;
			}

			private static Exception Finalizer(Exception __exception, MonoBehaviour __instance)
			{
				if (__exception == null)
				{
					return null;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner())
				{
					ZNetScene.instance.Destroy(((Component)__instance).gameObject);
					log.LogWarning((object)$"Building {((Object)__instance).name} failed, the plan is removed so it cannot be built again: {__exception}");
				}
				else
				{
					log.LogWarning((object)$"Building {((Object)__instance).name} failed: {__exception}");
				}
				return null;
			}
		}

		public const string PluginGuid = "qua8ion.valheim.planbuilddupefix";

		public const string PluginName = "PlanBuildDupeFix";

		public const string PluginVersion = "1.0.1";

		private const string PlanBuildGuid = "marcopogo.PlanBuild";

		private const string XPortalGuid = "yay.spikehimself.xportal";

		private const string SpawnPieceAndDestroyMethod = "PlanBuild.Plans.PlanPiece:RPC_SpawnPieceAndDestroy";

		private const float StackDistance = 0.05f;

		private const float XPortalCheckInterval = 2f;

		private static ConfigEntry<bool> removeStackedCopies;

		private static ConfigEntry<bool> cleanXPortalList;

		private static ManualLogSource log;

		private static int textCount;

		private float nextXPortalCheck;

		private void Awake()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			log = ((BaseUnityPlugin)this).Logger;
			removeStackedCopies = ((BaseUnityPlugin)this).Config.Bind<bool>("Cleanup", "RemoveStackedPortalCopies", true, "When the world loads, remove portals that have no creator and stand two or more at one spot. These are copies left by the bug; portals placed by players always have a creator.");
			cleanXPortalList = ((BaseUnityPlugin)this).Config.Bind<bool>("Cleanup", "CleanXPortalList", true, "On the server, keep PlanBuild plans and removed objects out of the XPortal portal list. XPortal lists a portal plan as a portal and keeps the entry after the plan is built.");
			new Harmony("qua8ion.valheim.planbuilddupefix").PatchAll(typeof(PlanBuildDupeFixPlugin).Assembly);
			bool flag = cleanXPortalList.Value && XPortalList.Init();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Portal and sign text is set without a platform user; " + ((SpawnPieceAndDestroyPatch.Target != null) ? "a plan whose build fails is removed" : "PlanBuild not found, plans are not guarded") + (removeStackedCopies.Value ? "; stacked portal copies are removed on world load" : "") + (flag ? "; plans are kept out of the XPortal list" : "")));
		}

		private void Update()
		{
			if (XPortalList.Ready && !(Time.time < nextXPortalCheck))
			{
				nextXPortalCheck = Time.time + 2f;
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZDOMan.instance != null && (Object)(object)ZNetScene.instance != (Object)null)
				{
					XPortalList.RemoveNonPortals();
				}
			}
		}

		private static bool HasLocalUser()
		{
			IDistributionPlatform distributionPlatform = PlatformManager.DistributionPlatform;
			return ((distributionPlatform != null) ? distributionPlatform.LocalUser : null) != null;
		}

		private static void CountText(string what)
		{
			textCount++;
			if (textCount <= 20 || textCount % 100 == 0)
			{
				log.LogInfo((object)$"Set {what} text without a platform user ({textCount} so far)");
			}
		}
	}
}