Decompiled source of PortalNetwork v0.2.1

plugins/PortalNetwork.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
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 Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using PortalNetwork.Core;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("PortalNetwork")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+deb90a60d854664f408151a45a03da157f40fa8c")]
[assembly: AssemblyProduct("PortalNetwork")]
[assembly: AssemblyTitle("PortalNetwork")]
[assembly: AssemblyVersion("0.2.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 PortalNetwork
{
	internal static class ChoiceWindow
	{
		private sealed class Exit
		{
			public string Label;

			public Vector3 Position;

			public Quaternion Rotation;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Action<ZRpc, ZPackage> <0>__OnListReply;

			public static UnityAction <1>__Close;
		}

		private const float ReplyTimeoutSeconds = 3f;

		private const float RowHeight = 46f;

		private const float Width = 420f;

		private static ZRpc _server;

		private static TeleportWorld _portal;

		private static ZDOID _pending = ZDOID.None;

		private static float _pendingSince;

		private static GameObject _panel;

		private static bool _open;

		private static bool _blocked;

		private static int _closedFrame = -1;

		private static float _health;

		internal static bool Bypass;

		internal static bool BlocksGame
		{
			get
			{
				if (!_open)
				{
					return Time.frameCount == _closedFrame;
				}
				return true;
			}
		}

		internal static bool Connected
		{
			get
			{
				if (_server != null && (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
				{
					return ZNet.instance.GetServerPeer()?.m_rpc == _server;
				}
				return false;
			}
		}

		internal static void RegisterServer(ZRpc rpc)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_server = rpc;
			_pending = ZDOID.None;
			rpc.Register<ZPackage>("PortalNetwork_ListReply", (Action<ZRpc, ZPackage>)OnListReply);
		}

		internal static void RequestList(TeleportWorld portal, ZDOID id)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (!_open && (!(_pending == id) || !(Time.time - _pendingSince < 3f)))
			{
				_portal = portal;
				_pending = id;
				_pendingSince = Time.time;
				ZPackage val = new ZPackage();
				val.Write(id);
				_server.Invoke("PortalNetwork_ListRequest", new object[1] { val });
			}
		}

		private static void OnListReply(ZRpc rpc, ZPackage reply)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			ZDOID val = reply.ReadZDOID();
			string network = reply.ReadString();
			int num = reply.ReadInt();
			List<Exit> list = new List<Exit>(num);
			for (int i = 0; i < num; i++)
			{
				Exit exit = new Exit
				{
					Label = reply.ReadString()
				};
				reply.ReadZDOID();
				exit.Position = reply.ReadVector3();
				exit.Rotation = reply.ReadQuaternion();
				list.Add(exit);
			}
			if (val != _pending)
			{
				return;
			}
			_pending = ZDOID.None;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)_portal == (Object)null || !InReach(localPlayer))
			{
				return;
			}
			if (list.Count == 0)
			{
				Bypass = true;
				try
				{
					_portal.Teleport(localPlayer);
					return;
				}
				finally
				{
					Bypass = false;
				}
			}
			Open(localPlayer, network, list);
		}

		private static bool InReach(Player player)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Distance(((Component)player).transform.position, ((Component)_portal).transform.position) <= _portal.m_activationRange + 1f;
		}

		internal static void Update()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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)
			if (_pending != ZDOID.None && Time.time - _pendingSince >= 3f)
			{
				_pending = ZDOID.None;
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, "Сеть порталов не отвечает.", 0, (Sprite)null, false);
				}
			}
			if (!_open)
			{
				return;
			}
			Player localPlayer2 = Player.m_localPlayer;
			if ((Object)(object)localPlayer2 == (Object)null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)_panel == (Object)null || (Object)(object)_portal == (Object)null || GameUiOpen() || ((Character)localPlayer2).IsDead() || !InReach(localPlayer2) || ((Character)localPlayer2).GetHealth() < _health - 0.01f)
			{
				Close();
				return;
			}
			_health = ((Character)localPlayer2).GetHealth();
			if (ZInput.GetKeyDown((KeyCode)27, false) || ZInput.GetButtonDown("JoyButtonB"))
			{
				Close();
			}
		}

		private static bool GameUiOpen()
		{
			if (!Console.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Menu.IsVisible() && !InventoryGui.IsVisible() && !Minimap.IsOpen())
			{
				return StoreGui.IsVisible();
			}
			return true;
		}

		private static void Open(Player player, string network, List<Exit> exits)
		{
			if (!GameUiOpen() && !((Object)(object)GUIManager.CustomGUIFront == (Object)null))
			{
				if ((Object)(object)_panel != (Object)null)
				{
					Object.Destroy((Object)(object)_panel);
				}
				Build(network, exits);
				_health = ((Character)player).GetHealth();
				_open = true;
				if (!_blocked)
				{
					GUIManager.BlockInput(true);
					_blocked = true;
				}
			}
		}

		private static void Build(string network, List<Exit> exits)
		{
			//IL_0059: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Expected O, but got Unknown
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Expected O, but got Unknown
			GUIManager instance = GUIManager.Instance;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 1f);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0f);
			float num = 170f + (float)exits.Count * 46f;
			_panel = instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, val, val, Vector2.zero, 420f, num, true);
			((Object)_panel).name = "SansaraPortalNetworkWindow";
			Text component = instance.CreateText("Портал «" + network + "»", _panel.transform, val2, val2, new Vector2(0f, -40f), instance.AveriaSerifBold, 30, instance.ValheimOrange, true, Color.black, 380f, 50f, false).GetComponent<Text>();
			component.alignment = (TextAnchor)4;
			component.resizeTextForBestFit = true;
			component.resizeTextMinSize = 16;
			component.resizeTextMaxSize = 30;
			for (int i = 0; i < exits.Count; i++)
			{
				Exit exit = exits[i];
				((UnityEvent)instance.CreateButton(exit.Label, _panel.transform, val2, val2, new Vector2(0f, -95f - (float)i * 46f), 340f, 40f).GetComponent<Button>().onClick).AddListener((UnityAction)delegate
				{
					Go(exit);
				});
			}
			ButtonClickedEvent onClick = instance.CreateButton("Закрыть (Esc)", _panel.transform, val3, val3, new Vector2(0f, 40f), 220f, 40f).GetComponent<Button>().onClick;
			object obj = <>O.<1>__Close;
			if (obj == null)
			{
				UnityAction val4 = Close;
				<>O.<1>__Close = val4;
				obj = (object)val4;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
		}

		private static void Go(Exit exit)
		{
			//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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_007f: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)_portal == (Object)null)
			{
				Close();
				return;
			}
			if (!((Humanoid)localPlayer).IsTeleportable(_portal.m_allowAllItems))
			{
				((Character)localPlayer).Message((MessageType)2, "$msg_noteleport", 0, (Sprite)null, false);
				Close();
				return;
			}
			Vector3 val = exit.Position + exit.Rotation * Vector3.forward * _portal.m_exitDistance + Vector3.up;
			if (!((Character)localPlayer).TeleportTo(val, exit.Rotation, true))
			{
				((Character)localPlayer).Message((MessageType)2, "Портал ещё не готов, подождите секунду.", 0, (Sprite)null, false);
				return;
			}
			Game.instance.IncrementPlayerStat((PlayerStatType)15, 1f, false);
			Close();
		}

		private static void Close()
		{
			_open = false;
			_closedFrame = Time.frameCount;
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.SetActive(false);
			}
			if (_blocked)
			{
				GUIManager.BlockInput(false);
				_blocked = false;
			}
		}
	}
	[HarmonyPatch(typeof(TextInput), "RequestText")]
	internal static class PortalNameLimitPatch
	{
		private static void Prefix(TextReceiver sign, ref int charLimit)
		{
			if (sign is TeleportWorld)
			{
				charLimit = Math.Max(charLimit, Plugin.MaxNameLength.Value);
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
	internal static class TeleportPatch
	{
		private static bool Prefix(TeleportWorld __instance, Player player, ZNetView ___m_nview)
		{
			//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_006e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (ChoiceWindow.Bypass || !Plugin.Enabled.Value || !ChoiceWindow.Connected || (Object)(object)___m_nview == (Object)null)
				{
					return true;
				}
				ZDO zDO = ___m_nview.GetZDO();
				if (zDO != null && PortalTag.Parse(zDO.GetString(ZDOVars.s_tag, "")).Kind == PortalTagKind.Parent)
				{
					ZDOID connectionZDOID = zDO.GetConnectionZDOID((ConnectionType)1);
					if (!((ZDOID)(ref connectionZDOID)).IsNone() && !Refused(__instance, player))
					{
						ChoiceWindow.RequestList(__instance, zDO.m_uid);
						return false;
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Сеть порталов: вход в портал: " + ex));
				return true;
			}
		}

		private static bool Refused(TeleportWorld portal, Player player)
		{
			if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)32))
			{
				return true;
			}
			float num = default(float);
			if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)33) && (RandEventSystem.instance.GetBossEvent() != null || (ZoneSystem.instance.GetGlobalKey((GlobalKeys)47, ref num) && num > 0f)))
			{
				return true;
			}
			return !((Humanoid)player).IsTeleportable(portal.m_allowAllItems);
		}
	}
	[HarmonyPatch(typeof(PlayerController), "TakeInput")]
	internal static class ControllerInputPatch
	{
		private static void Postfix(ref bool __result)
		{
			if (ChoiceWindow.BlocksGame)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class PlayerInputPatch
	{
		private static void Postfix(ref bool __result)
		{
			if (ChoiceWindow.BlocksGame)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Menu), "Update")]
	internal static class MenuPatch
	{
		private static bool Prefix()
		{
			return !ChoiceWindow.BlocksGame;
		}
	}
	internal static class Net
	{
		public const string ListRequest = "PortalNetwork_ListRequest";

		public const string ListReply = "PortalNetwork_ListReply";
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	internal static class NewConnectionPatch
	{
		private static void Postfix(ZNet __instance, ZNetPeer peer)
		{
			try
			{
				if (peer?.m_rpc != null)
				{
					if (__instance.IsServer())
					{
						peer.m_rpc.Register<ZPackage>("PortalNetwork_ListRequest", (Action<ZRpc, ZPackage>)ServerLinks.OnListRequest);
					}
					else
					{
						ChoiceWindow.RegisterServer(peer.m_rpc);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Сеть порталов: OnNewConnection: " + ex));
			}
		}
	}
	[BepInPlugin("sansara.PortalNetwork", "Portal Network", "0.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "sansara.PortalNetwork";

		public const string Name = "Portal Network";

		public const string Version = "0.2.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> MaxChildren;

		internal static ConfigEntry<int> MaxNameLength;

		internal static ConfigEntry<string> SelfTestMode;

		private void Awake()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Сеть порталов: «(сеть)» ведёт в окно выбора, «(сеть)имя» — в «(сеть)». false — все порталы как в обычной игре.");
			MaxChildren = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxChildren", 10, new ConfigDescription("Сервер: сколько выходов может быть у одного центрального портала. Лишние работают как обычные порталы.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			MaxNameLength = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxNameLength", 20, new ConfigDescription("Клиент: сколько символов можно ввести в имя портала (в игре 10). Работает и при выключенной сети.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 40), Array.Empty<object>()));
			SelfTestMode = ((BaseUnityPlugin)this).Config.Bind<string>("Debug", "SelfTest", "Off", "Только для тестовой копии. Run — создать три служебных портала «(st)» и записать в журнал их связи; Cleanup — удалить их по одному, проверяя связи; Off — выключено.");
			new Harmony("sansara.PortalNetwork").PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)string.Format("{0} {1} loaded: Enabled={2}, MaxChildren={3}, SelfTest={4}", "Portal Network", "0.2.1", Enabled.Value, MaxChildren.Value, SelfTestMode.Value));
		}

		private void Update()
		{
			try
			{
				if (Application.isBatchMode)
				{
					SelfTest.Tick();
				}
				else
				{
					ChoiceWindow.Update();
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Сеть порталов: Update: " + ex));
			}
		}
	}
	internal static class SelfTest
	{
		private const string Parent = "(st)";

		private const string Child1 = "(st)1";

		private const string Child2 = "(st)2";

		private static readonly Vector3 Origin = new Vector3(9000f, 500f, 3000f);

		private const float CycleSeconds = 5f;

		private const float AfterLoadSeconds = 20f;

		private static int _cycle;

		private static float _nextAt = -1f;

		private static int _vanillaCalls;

		internal static void CountVanillaCall()
		{
			_vanillaCalls++;
		}

		internal static void Tick()
		{
			string value = Plugin.SelfTestMode.Value;
			if (value == "Off" || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)Game.instance == (Object)null || ZDOMan.instance == null || ZDOMan.instance.NrOfObjects() == 0)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_nextAt < 0f)
			{
				_nextAt = realtimeSinceStartup + 20f;
			}
			else if (!(realtimeSinceStartup < _nextAt) && _cycle < ((value == "Run") ? 6 : 7))
			{
				_nextAt = realtimeSinceStartup + 5f;
				_cycle++;
				Plugin.Log.LogInfo((object)$"Самопроверка сети порталов: timeScale {Time.timeScale}, вызовов ConnectPortals всего {_vanillaCalls}.");
				if (value == "Run")
				{
					Run(_cycle);
				}
				else if (value == "Cleanup")
				{
					Cleanup(_cycle);
				}
				Game.instance.ConnectPortals();
			}
		}

		private static void Run(int cycle)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			if (cycle <= 6)
			{
				Report($"цикл {cycle}");
				switch (cycle)
				{
				case 1:
					Create("(st)", 0);
					Create("(st)1", 1);
					Create("(st)2", 2);
					break;
				case 6:
				{
					ZDO val = Find("(st)");
					ZDO val2 = Find("(st)1");
					ZDO val3 = Find("(st)2");
					bool flag = val != null && val2 != null && val3 != null && Link(val2) == val.m_uid && Link(val3) == val.m_uid && Link(val) == val2.m_uid;
					Plugin.Log.LogInfo((object)("Самопроверка сети порталов: " + (flag ? "ПРОЙДЕНА" : "НЕ ПРОЙДЕНА") + " — связи пережили циклы ConnectPortals."));
					break;
				}
				}
			}
		}

		private static void Cleanup(int cycle)
		{
			//IL_009b: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (cycle > 7)
			{
				return;
			}
			Report($"очистка, цикл {cycle}");
			switch (cycle)
			{
			case 1:
				Destroy("(st)1");
				break;
			case 3:
			{
				ZDO val3 = Find("(st)");
				ZDO val4 = Find("(st)2");
				Verdict("центральный перешёл на оставшийся выход", val3 != null && val4 != null && Link(val3) == val4.m_uid);
				Destroy("(st)2");
				break;
			}
			case 5:
			{
				ZDO val = Find("(st)");
				int ok;
				if (val != null)
				{
					ZDOID val2 = Link(val);
					ok = (((ZDOID)(ref val2)).IsNone() ? 1 : 0);
				}
				else
				{
					ok = 0;
				}
				Verdict("без выходов центральный стал обычным порталом без связи", (byte)ok != 0);
				Destroy("(st)");
				break;
			}
			case 2:
			case 4:
				break;
			}
		}

		private static void Verdict(string what, bool ok)
		{
			Plugin.Log.LogInfo((object)("Самопроверка сети порталов: " + (ok ? "ПРОЙДЕНА" : "НЕ ПРОЙДЕНА") + " — " + what + "."));
		}

		private static void Report(string when)
		{
			string text = string.Join("; ", new string[3] { "(st)", "(st)1", "(st)2" }.Select(delegate(string tag)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				ZDO val = Find(tag);
				if (val == null)
				{
					return tag + " нет";
				}
				ZDO zDO = ZDOMan.instance.GetZDO(Link(val));
				object obj;
				if (zDO == null)
				{
					ZDOID val2 = Link(val);
					obj = (((ZDOID)(ref val2)).IsNone() ? "нет связи" : "неизвестный");
				}
				else
				{
					obj = zDO.GetString(ZDOVars.s_tag, "");
				}
				return tag + " → " + (string?)obj;
			}));
			Plugin.Log.LogInfo((object)("Самопроверка сети порталов (" + when + "): " + text));
		}

		private static ZDOID Link(ZDO zdo)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			return zdo.GetConnectionZDOID((ConnectionType)1);
		}

		private static ZDO Find(string tag)
		{
			return ((IEnumerable<ZDO>)ZDOMan.instance.GetPortalList()).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => z.GetString(ZDOVars.s_tag, "") == tag));
		}

		private static void Create(string tag, int index)
		{
			//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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			if (Find(tag) == null)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("portal_wood");
				ZNetView component = prefab.GetComponent<ZNetView>();
				int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name);
				ZDO val = ZDOMan.instance.CreateNewZDO(Origin + Vector3.right * ((float)index * 10f), stableHashCode);
				val.Persistent = component.m_persistent;
				val.Type = component.m_type;
				val.Distant = component.m_distant;
				val.SetPrefab(stableHashCode);
				val.Set(ZDOVars.s_tag, tag);
				Plugin.Log.LogInfo((object)$"Самопроверка сети порталов: создан {tag} {val.m_uid}.");
			}
		}

		private static void Destroy(string tag)
		{
			ZDO val = Find(tag);
			if (val != null)
			{
				val.SetOwner(ZDOMan.GetSessionID());
				ZDOMan.instance.DestroyZDO(val);
				Plugin.Log.LogInfo((object)("Самопроверка сети порталов: удалён " + tag + "."));
			}
		}
	}
	internal static class ServerLinks
	{
		internal static bool Hiding;

		private static NetworkPlan<ZDOID> _plan;

		private static string _lastSummary = "";

		internal static NetworkPlan<ZDOID> BuildPlan()
		{
			return NetworkPlan<ZDOID>.Build(from z in ZDOMan.instance.GetPortalList()
				select new KeyValuePair<ZDOID, string>(z.m_uid, z.GetString(ZDOVars.s_tag, "")), Plugin.MaxChildren.Value);
		}

		internal static NetworkPlan<ZDOID> BuildPlanForHiding()
		{
			return _plan;
		}

		internal static void Reconcile()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			_plan = BuildPlan();
			foreach (ZDO portal in ZDOMan.instance.GetPortalList())
			{
				if (_plan.TryGetTarget(portal.m_uid, out var target) && portal.GetConnectionZDOID((ConnectionType)1) != target)
				{
					SetConnection(portal, target);
				}
			}
			LogChanges();
		}

		private static void SetConnection(ZDO portal, ZDOID target)
		{
			//IL_002c: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			long owner = portal.GetOwner();
			if (owner == 0L || owner == ZDOMan.GetSessionID() || ZNet.instance.GetPeer(owner) == null)
			{
				portal.SetOwner(ZDOMan.GetSessionID());
				portal.SetConnection((ConnectionType)1, target);
				ZDOMan.instance.ForceSendZDO(portal.m_uid);
			}
			else
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(owner, "RPC_SetConnection", new object[2] { portal.m_uid, target });
			}
		}

		private static void LogChanges()
		{
			string text = string.Join("; ", _plan.Networks.Select((Network<ZDOID> n) => "(" + n.Name + "): " + string.Join(", ", n.Children.Select((NetworkChild<ZDOID> c) => c.Label)) + ((n.Overflow > 0) ? $" и ещё {n.Overflow} сверх лимита" : "")).OrderBy<string, string>((string s) => s, StringComparer.Ordinal).Concat(from a in _plan.AmbiguousNetworks.OrderBy<string, string>((string s) => s, StringComparer.Ordinal)
				select "(" + a + "): несколько центральных порталов, сеть не работает"));
			if (text != _lastSummary)
			{
				_lastSummary = text;
				Plugin.Log.LogInfo((object)("Сети порталов: " + ((text.Length > 0) ? text : "нет")));
			}
		}

		internal static void OnListRequest(ZRpc rpc, ZPackage request)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ZDOID val = request.ReadZDOID();
				Network<ZDOID> network = (Plugin.Enabled.Value ? BuildPlan().FindByParent(val) : null);
				ZPackage val2 = new ZPackage();
				val2.Write(val);
				val2.Write(network?.Name ?? "");
				val2.Write(network?.Children.Count ?? 0);
				if (network != null)
				{
					foreach (NetworkChild<ZDOID> child in network.Children)
					{
						ZDO zDO = ZDOMan.instance.GetZDO(child.Id);
						val2.Write(child.Label);
						val2.Write(child.Id);
						val2.Write(zDO.GetPosition());
						val2.Write(zDO.GetRotation());
					}
				}
				rpc.Invoke("PortalNetwork_ListReply", new object[1] { val2 });
				Plugin.Log.LogInfo((object)$"Сеть порталов: запрос списка {val}: выходов {network?.Children.Count ?? 0}.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Сеть порталов: запрос списка: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Game), "ConnectPortals")]
	internal static class ConnectPortalsPatch
	{
		private static void Prefix()
		{
			if (!Plugin.Enabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				SelfTest.CountVanillaCall();
				ServerLinks.Reconcile();
				ServerLinks.Hiding = true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Сеть порталов: связи: " + ex));
			}
		}

		private static void Finalizer()
		{
			ServerLinks.Hiding = false;
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "GetPortalList")]
	internal static class PortalListPatch
	{
		private static void Postfix(List<ZDO> __result)
		{
			if (ServerLinks.Hiding)
			{
				NetworkPlan<ZDOID> plan = ServerLinks.BuildPlanForHiding();
				__result.RemoveAll((ZDO z) => plan.IsManaged(z.m_uid));
			}
		}
	}
}
namespace PortalNetwork.Core
{
	public sealed class NetworkChild<TId>
	{
		public TId Id { get; }

		public string Label { get; }

		public NetworkChild(TId id, string label)
		{
			Id = id;
			Label = label;
		}
	}
	public sealed class Network<TId>
	{
		public string Name { get; }

		public TId ParentId { get; }

		public IReadOnlyList<NetworkChild<TId>> Children { get; }

		public int Overflow { get; }

		public Network(string name, TId parentId, IReadOnlyList<NetworkChild<TId>> children, int overflow)
		{
			Name = name;
			ParentId = parentId;
			Children = children;
			Overflow = overflow;
		}
	}
	public sealed class NetworkPlan<TId>
	{
		private sealed class LabelOrder : IComparer<string>
		{
			public static readonly LabelOrder Instance = new LabelOrder();

			public int Compare(string a, string b)
			{
				long result;
				bool flag = long.TryParse(a, NumberStyles.None, CultureInfo.InvariantCulture, out result);
				long result2;
				bool flag2 = long.TryParse(b, NumberStyles.None, CultureInfo.InvariantCulture, out result2);
				if (flag && flag2)
				{
					return result.CompareTo(result2);
				}
				if (flag != flag2)
				{
					if (!flag)
					{
						return 1;
					}
					return -1;
				}
				return string.CompareOrdinal(a, b);
			}
		}

		private readonly Dictionary<TId, TId> _targets = new Dictionary<TId, TId>();

		private readonly Dictionary<TId, Network<TId>> _byParent = new Dictionary<TId, Network<TId>>();

		public IReadOnlyCollection<Network<TId>> Networks => _byParent.Values;

		public IReadOnlyList<string> AmbiguousNetworks { get; }

		private NetworkPlan(List<string> ambiguous)
		{
			AmbiguousNetworks = ambiguous;
		}

		public bool IsManaged(TId id)
		{
			return _targets.ContainsKey(id);
		}

		public bool TryGetTarget(TId id, out TId target)
		{
			return _targets.TryGetValue(id, out target);
		}

		public Network<TId> FindByParent(TId id)
		{
			if (!_byParent.TryGetValue(id, out var value))
			{
				return null;
			}
			return value;
		}

		public static NetworkPlan<TId> Build(IEnumerable<KeyValuePair<TId, string>> portals, int maxChildren)
		{
			Dictionary<string, List<TId>> dictionary = new Dictionary<string, List<TId>>(StringComparer.Ordinal);
			Dictionary<string, List<NetworkChild<TId>>> dictionary2 = new Dictionary<string, List<NetworkChild<TId>>>(StringComparer.Ordinal);
			foreach (KeyValuePair<TId, string> portal in portals)
			{
				PortalTag portalTag = PortalTag.Parse(portal.Value);
				if (portalTag.Kind == PortalTagKind.Parent)
				{
					Bucket(dictionary, portalTag.Network).Add(portal.Key);
				}
				else if (portalTag.Kind == PortalTagKind.Child)
				{
					Bucket(dictionary2, portalTag.Network).Add(new NetworkChild<TId>(portal.Key, portalTag.Label));
				}
			}
			List<string> list = new List<string>();
			NetworkPlan<TId> networkPlan = new NetworkPlan<TId>(list);
			int count = Math.Max(1, maxChildren);
			foreach (KeyValuePair<string, List<NetworkChild<TId>>> item in dictionary2)
			{
				if (!dictionary.TryGetValue(item.Key, out var value))
				{
					continue;
				}
				if (value.Count > 1)
				{
					list.Add(item.Key);
					continue;
				}
				List<NetworkChild<TId>> list2 = item.Value.OrderBy((NetworkChild<TId> c) => c.Label, LabelOrder.Instance).ToList();
				List<NetworkChild<TId>> list3 = list2.Take(count).ToList();
				TId val = value[0];
				networkPlan._byParent[val] = new Network<TId>(item.Key, val, list3, list2.Count - list3.Count);
				networkPlan._targets[val] = list3[0].Id;
				foreach (NetworkChild<TId> item2 in list3)
				{
					networkPlan._targets[item2.Id] = val;
				}
			}
			return networkPlan;
		}

		private static List<T> Bucket<T>(Dictionary<string, List<T>> map, string key)
		{
			if (!map.TryGetValue(key, out var value))
			{
				value = (map[key] = new List<T>());
			}
			return value;
		}
	}
	public enum PortalTagKind
	{
		None,
		Parent,
		Child
	}
	public sealed class PortalTag
	{
		private static readonly PortalTag NoneTag = new PortalTag(PortalTagKind.None, "", "");

		public PortalTagKind Kind { get; }

		public string Network { get; }

		public string Label { get; }

		private PortalTag(PortalTagKind kind, string network, string label)
		{
			Kind = kind;
			Network = network;
			Label = label;
		}

		public static PortalTag Parse(string text)
		{
			string text2 = (text ?? "").Trim();
			if (text2.Length == 0 || text2[0] != '(')
			{
				return NoneTag;
			}
			int num = text2.IndexOf(')');
			if (num < 0)
			{
				return NoneTag;
			}
			string text3 = text2.Substring(1, num - 1).Trim();
			if (text3.Length == 0)
			{
				return NoneTag;
			}
			string text4 = text2.Substring(num + 1).Trim();
			return new PortalTag((text4.Length == 0) ? PortalTagKind.Parent : PortalTagKind.Child, text3, text4);
		}
	}
}