Decompiled source of DFGHub v1.4.2

DFGHub.dll

Decompiled 17 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using MyMods.Web;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DFGHub")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.4.2.0")]
[assembly: AssemblyInformationalVersion("1.4.2+c13f19c3b078d3d6a56a0c7fbbe49d5fd3c23638")]
[assembly: AssemblyProduct("DFGHub")]
[assembly: AssemblyTitle("DFGHub")]
[assembly: AssemblyVersion("1.4.2.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 MyMods.Web
{
	public static class WebNav
	{
		public sealed class Link
		{
			public readonly string Id;

			public readonly string Label;

			public readonly int Port;

			public string Url => "http://127.0.0.1:" + Port + "/";

			public Link(string id, string label, int port)
			{
				Id = id;
				Label = label;
				Port = port;
			}
		}

		public static readonly Link[] Links = new Link[14]
		{
			new Link("monsters", "Monsters", 8777),
			new Link("searoute", "SeaRoute", 8778),
			new Link("longboat", "Harpoon Longboat", 8779),
			new Link("tradewind", "TradeWind", 8780),
			new Link("safeharbor", "Safe Harbor", 8781),
			new Link("hub", "DFG Hub", 8782),
			new Link("vault", "Viking Vault", 8783),
			new Link("qolvault", "QoL Vault Buttons", 8784),
			new Link("qolinventory", "QoL Inventory Buttons", 8785),
			new Link("cacophony", "Cacophony", 8787),
			new Link("ominous", "Ominous", 8788),
			new Link("pebble", "Pebble", 8789),
			new Link("trolls", "Trolls", 8790),
			new Link("transporter", "Transporter", 8791)
		};

		public const string Style = "nav.top{position:sticky;top:0;z-index:20;font-family:Segoe UI,Arial,sans-serif;background:#0d1219;border-bottom:1px solid #2b3b4c;padding:6px 14px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}\nnav.top .brand{color:#7f8fa0;font-size:11px;text-transform:uppercase;letter-spacing:.6px;margin-right:8px}\nnav.top a{color:#9fc3dd;text-decoration:none;font-size:13px;padding:4px 9px;border-radius:5px}\nnav.top a:hover{background:#1a2530}\nnav.top a.here{background:#3a2b4c;border:1px solid #55406b;color:#efe0ff}\n";

		public static string Html(string currentId)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<nav class='top'><span class='brand'>MyMods</span>");
			Link[] links = Links;
			foreach (Link link in links)
			{
				stringBuilder.Append("<a class='");
				if (link.Id == currentId)
				{
					stringBuilder.Append("here");
				}
				stringBuilder.Append("' href='").Append(link.Url).Append("'>")
					.Append(link.Label)
					.Append("</a>");
			}
			stringBuilder.Append("</nav>");
			return stringBuilder.ToString();
		}

		public static Link Find(string id)
		{
			Link[] links = Links;
			foreach (Link link in links)
			{
				if (link.Id == id)
				{
					return link;
				}
			}
			return null;
		}
	}
}
namespace DFGHub
{
	[BepInPlugin("com.dfghub.dfghub", "DFGHub", "1.4.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class DFGHubPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.dfghub.dfghub";

		public const string PluginName = "DFGHub";

		public const string PluginVersion = "1.4.2";

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<string> CategoryName;

		internal static ConfigEntry<bool> UnlockByDefault;

		internal static ConfigEntry<bool> ExclusiveCategory;

		internal static ConfigEntry<bool> EnableWebHub;

		internal static ConfigEntry<int> WebPort;

		internal static ConfigEntry<bool> EnableHall;

		internal static ConfigEntry<KeyboardShortcut> HallKey;

		internal static ConfigEntry<int> HallFontPercent;

		internal static ManualLogSource Log;

		internal static int RoutedPieceCount;

		internal static string ActiveCategory = "DFG";

		private static readonly string[] ModGuids = new string[14]
		{
			"com.harpoonlooter.harpoonlooter", "com.harvestturrets.harvestturrets", "com.seatapult.seatapult", "com.safeharbor.safeharbor", "com.tradewind.tradewind", "com.bluecollartrolls.bluecollartrolls", "com.harpoonlongboat.harpoonlongboat", "com.storagesign.storagesign", "com.dfg.qol", "com.vikingvault.vikingvault",
			"com.dfg.freyjasfeeder", "com.dfg.pebble", "com.dfg.salvagestation", "com.dfg.transporter"
		};

		private Harmony m_harmony;

		private float m_retryWindow;

		private float m_retryTimer;

		private bool m_sawSpawn;

		private int m_refreshCount;

		private float m_refreshCooldown;

		private int m_lastRoutedCount = -1;

		private void Awake()
		{
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Expected O, but got Unknown
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Expected O, but got Unknown
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for DFGHub.");
			CategoryName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "CategoryName", "DFG", "Name of the shared Hammer build category.");
			UnlockByDefault = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UnlockByDefault", true, "Clear any crafting-station requirement so DFG pieces are always buildable.");
			ExclusiveCategory = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ExclusiveCategory", false, "When true, remove pieces from their vanilla tag (Misc/Furniture) so they only appear under the DFG category.");
			EnableWebHub = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableWebHub", true, "Serve the parent web hub on localhost.");
			WebPort = ((BaseUnityPlugin)this).Config.Bind<int>("General", "WebPort", 8782, "TCP port for the DFG hub (http://127.0.0.1:<port>/).");
			EnableHall = ((BaseUnityPlugin)this).Config.Bind<bool>("Hall", "EnableHall", true, "Open Sea Route, TradeWind, Blue Collar, and Pebble bounties in one board. It covers 80% of the screen, and the header can shrink it to the left 30% or drag it.");
			HallKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hall", "HallKey", new KeyboardShortcut((KeyCode)116, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Opens and closes the DFG board. Sea Route and TradeWind stop using this key while the board is on.");
			HallFontPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Hall", "FontPercent", 100, new ConfigDescription("Text size on the DFG board, as a percent of normal. The A- and A+ buttons on the board change this.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(80, 160), Array.Empty<object>()));
			Log = ((BaseUnityPlugin)this).Logger;
			if (Enabled.Value)
			{
				ActiveCategory = CategoryName.Value;
				PieceManager.OnPiecesRegistered += OnPiecesRegistered;
				if (EnableWebHub.Value)
				{
					HubWeb.Start();
				}
				if (EnableHall.Value && (Object)(object)((Component)this).GetComponent<HallBoard>() == (Object)null)
				{
					((Component)this).gameObject.AddComponent<HallBoard>();
					m_harmony = new Harmony("com.dfghub.dfghub.hall");
					m_harmony.PatchAll(typeof(HallInput));
				}
			}
		}

		private void OnDestroy()
		{
			PieceManager.OnPiecesRegistered -= OnPiecesRegistered;
			HubWeb.Stop();
			Harmony harmony = m_harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void OnPiecesRegistered()
		{
			m_retryWindow = 60f;
			m_retryTimer = 0f;
			m_sawSpawn = false;
			m_refreshCount = 0;
			m_refreshCooldown = 0f;
			m_lastRoutedCount = -1;
		}

		private void Update()
		{
			if (m_retryWindow <= 0f)
			{
				return;
			}
			m_retryWindow -= Time.deltaTime;
			m_retryTimer -= Time.deltaTime;
			if (m_retryTimer <= 0f)
			{
				m_retryTimer = 1f;
				ReorganizePieces();
			}
			if (!((Object)(object)Player.m_localPlayer != (Object)null))
			{
				return;
			}
			if (!m_sawSpawn)
			{
				m_sawSpawn = true;
				m_refreshCount = 3;
				m_refreshCooldown = 0f;
			}
			if (m_refreshCount > 0)
			{
				m_refreshCooldown -= Time.deltaTime;
				if (m_refreshCooldown <= 0f)
				{
					m_refreshCooldown = 2f;
					m_refreshCount--;
					ReorganizePieces();
					RefreshCategories();
				}
			}
		}

		private void ReorganizePieces()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected I4, but got Unknown
			//IL_0080: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled.Value)
			{
				return;
			}
			try
			{
				string value = CategoryName.Value;
				PieceCategory val = PieceManager.Instance.AddPieceCategory("Hammer", value);
				int num = 0;
				StringBuilder stringBuilder = new StringBuilder();
				string[] modGuids = ModGuids;
				foreach (string text in modGuids)
				{
					int num2 = 0;
					foreach (CustomPiece piece in ModRegistry.GetPieces(text))
					{
						Piece val2 = ((piece == null) ? null : piece.Piece);
						if (!((Object)(object)val2 == (Object)null))
						{
							val2.m_category = val;
							if (ExclusiveCategory.Value)
							{
								val2.m_usage = (UsageTagFlags)0;
							}
							if (UnlockByDefault.Value)
							{
								val2.m_craftingStation = null;
							}
							num++;
							num2++;
						}
					}
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(text).Append("=").Append(num2);
				}
				RoutedPieceCount = num;
				if (num != m_lastRoutedCount)
				{
					m_lastRoutedCount = num;
					Logger.LogInfo((object)("DFGHub: routed " + num + " pieces into '" + value + "' (id=" + (int)val + "). " + stringBuilder));
					RefreshCategories();
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("DFGHub: piece reorganization failed: " + ex));
			}
		}

		private static void RefreshCategories()
		{
			try
			{
				MethodInfo method = typeof(PieceManager).GetMethod("RefreshCategories", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method != null)
				{
					method.Invoke(PieceManager.Instance, null);
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("DFGHub: could not refresh piece categories: " + ex.Message));
			}
		}
	}
	public class HallBoard : MonoBehaviour
	{
		private sealed class Tab
		{
			public string Id;

			public string Title;

			public int Order;

			public Action<Rect> Draw;
		}

		private static readonly List<Tab> s_tabs = new List<Tab>();

		private static int s_index;

		private static bool s_yield;

		private static bool s_escapeHeld;

		private static bool s_compact;

		private static bool s_placed;

		private static Rect s_window;

		private static bool s_dragging;

		private static Vector2 s_dragOffset;

		private static readonly Color ColBackdrop = new Color(0f, 0f, 0f, 0.8f);

		private static readonly Color ColWindow = new Color(0.08f, 0.1f, 0.14f, 0.99f);

		private static readonly Color ColBorder = new Color(0.74f, 0.58f, 0.28f, 1f);

		private static readonly Color ColInner = new Color(0.18f, 0.23f, 0.32f, 0.85f);

		private static readonly Color ColHeader = new Color(0.12f, 0.16f, 0.22f, 1f);

		private static readonly Color ColTab = new Color(0.1f, 0.13f, 0.18f, 0.95f);

		private static readonly Color ColTabOn = new Color(0.22f, 0.18f, 0.08f, 0.98f);

		private static readonly Color ColGold = new Color(1f, 0.84f, 0.38f, 1f);

		private static readonly Color ColWhite = new Color(0.92f, 0.95f, 0.98f, 1f);

		private static readonly Color ColMuted = new Color(0.6f, 0.66f, 0.75f, 1f);

		private static readonly Dictionary<string, GUIStyle> s_styles = new Dictionary<string, GUIStyle>();

		private bool m_open;

		private Texture2D m_white;

		private bool m_hotkeyWasDown;

		public static HallBoard Instance { get; private set; }

		public static bool Ready => (Object)(object)Instance != (Object)null;

		public static bool IsOpen
		{
			get
			{
				if ((Object)(object)Instance != (Object)null)
				{
					return Instance.m_open;
				}
				return false;
			}
		}

		public static float TextScale => (float)Mathf.Clamp(DFGHubPlugin.HallFontPercent?.Value ?? 100, 80, 160) / 100f;

		private void Awake()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			m_white = new Texture2D(1, 1);
			m_white.SetPixel(0, 0, Color.white);
			m_white.Apply();
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public static void Register(string id, string title, int order, Action<Rect> draw)
		{
			if (string.IsNullOrEmpty(id) || draw == null)
			{
				return;
			}
			for (int i = 0; i < s_tabs.Count; i++)
			{
				if (string.Equals(s_tabs[i].Id, id, StringComparison.OrdinalIgnoreCase))
				{
					s_tabs[i].Title = (string.IsNullOrEmpty(title) ? id : title);
					s_tabs[i].Order = order;
					s_tabs[i].Draw = draw;
					SortTabs();
					return;
				}
			}
			s_tabs.Add(new Tab
			{
				Id = id,
				Title = (string.IsNullOrEmpty(title) ? id : title),
				Order = order,
				Draw = draw
			});
			SortTabs();
			ManualLogSource log = DFGHubPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("DFG board tab '" + title + "' (" + id + ")"));
			}
		}

		public static bool Open(string tabId)
		{
			if ((Object)(object)Instance == (Object)null)
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				return false;
			}
			if (!string.IsNullOrEmpty(tabId))
			{
				int num = IndexOf(tabId);
				if (num < 0)
				{
					return false;
				}
				s_index = num;
			}
			Instance.m_open = true;
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
			return true;
		}

		public static void Close()
		{
			if ((Object)(object)Instance == (Object)null || !Instance.m_open)
			{
				s_yield = false;
				s_escapeHeld = false;
				return;
			}
			Instance.m_open = false;
			s_yield = false;
			s_escapeHeld = false;
			s_dragging = false;
			if (!InventoryGui.IsVisible())
			{
				Cursor.visible = false;
				Cursor.lockState = (CursorLockMode)1;
			}
		}

		public static bool IsHosting(string id)
		{
			if (!IsOpen || s_tabs.Count == 0 || string.IsNullOrEmpty(id))
			{
				return false;
			}
			int index = Mathf.Clamp(s_index, 0, s_tabs.Count - 1);
			return string.Equals(s_tabs[index].Id, id, StringComparison.OrdinalIgnoreCase);
		}

		public static void SetYield(bool yieldScreen)
		{
			s_yield = yieldScreen;
		}

		public static void HoldEscape()
		{
			s_escapeHeld = true;
		}

		private void Update()
		{
			bool flag = HotkeyDown();
			if (!m_open)
			{
				if (flag && CanOpen())
				{
					Open(null);
				}
				return;
			}
			bool flag2 = s_escapeHeld;
			s_escapeHeld = false;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || Menu.IsVisible())
			{
				Close();
				return;
			}
			PlayerController.SetTakeInputDelay(0.25f);
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
			if (flag && GUIUtility.keyboardControl == 0)
			{
				Close();
			}
			else if (Input.GetKeyDown((KeyCode)27) || ZInput.GetButtonDown("JoyButtonB"))
			{
				if (GUIUtility.keyboardControl != 0)
				{
					GUIUtility.keyboardControl = 0;
				}
				else if (!(s_yield || flag2))
				{
					Close();
				}
			}
		}

		private void OnGUI()
		{
			if (!m_open || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			ZCursor.LockState = (CursorLockMode)0;
			ZCursor.Show();
			int depth = GUI.depth;
			GUI.depth = -50;
			try
			{
				if (s_yield)
				{
					DrawYielded();
				}
				else
				{
					DrawFull();
				}
			}
			finally
			{
				GUI.depth = depth;
			}
		}

		private void DrawFull()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			DrawRect(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), ColBackdrop);
			PlaceWindow();
			HandleDrag();
			Rect rect = s_window;
			DrawBox(rect, ColWindow, ColBorder, 2f);
			DrawBorder(new Rect(((Rect)(ref rect)).x + 3f, ((Rect)(ref rect)).y + 3f, ((Rect)(ref rect)).width - 6f, ((Rect)(ref rect)).height - 6f), ColInner, 1f);
			Rect rect2 = default(Rect);
			((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 10f, ((Rect)(ref rect)).width - 24f, 32f);
			DrawBox(rect2, ColHeader, ColInner, 1f);
			GUI.Label(new Rect(((Rect)(ref rect2)).x + 10f, ((Rect)(ref rect2)).y, 64f, ((Rect)(ref rect2)).height), "DFG", Style(16, bold: true, (TextAnchor)3, ColGold));
			float right = ((Rect)(ref rect2)).xMax - 4f;
			float y = ((Rect)(ref rect2)).y + 4f;
			Rect rect3 = TakeButton(ref right, y, 24f);
			Rect rect4 = TakeButton(ref right, y, 36f);
			Rect val = TakeButton(ref right, y, 44f);
			Rect rect5 = TakeButton(ref right, y, 36f);
			Rect rect6 = TakeButton(ref right, y, 44f);
			if (Button(rect6, s_compact ? "80%" : "30%", ColGold))
			{
				s_compact = !s_compact;
				s_placed = false;
				s_dragging = false;
			}
			if (NudgeButton(rect5, "A-"))
			{
				NudgeFont(-10);
			}
			GUI.Label(val, TextScale.ToString("0%"), Style(12, bold: true, (TextAnchor)4, ColGold));
			if (NudgeButton(rect4, "A+"))
			{
				NudgeFont(10);
			}
			if (Button(rect3, "x", ColWhite))
			{
				Close();
				return;
			}
			Rect bar = default(Rect);
			((Rect)(ref bar))..ctor(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect2)).yMax + 8f, ((Rect)(ref rect)).width - 24f, 32f);
			DrawTabs(bar);
			Rect body = default(Rect);
			((Rect)(ref body))..ctor(((Rect)(ref rect)).x + 12f, ((Rect)(ref bar)).yMax + 8f, ((Rect)(ref rect)).width - 24f, ((Rect)(ref rect)).yMax - ((Rect)(ref bar)).yMax - 20f);
			DrawScaled(body);
		}

		private static Rect TakeButton(ref float right, float y, float width)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			right -= width;
			Rect result = new Rect(right, y, width, 24f);
			right -= 4f;
			return result;
		}

		private static void PlaceWindow()
		{
			//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)
			bool flag = s_compact;
			float num = (float)Screen.width * (flag ? 0.3f : 0.8f);
			float num2 = (flag ? ((float)Screen.height - 32f) : ((float)Screen.height * 0.8f));
			if (!s_placed)
			{
				float num3 = (flag ? 16f : (((float)Screen.width - num) * 0.5f));
				float num4 = (flag ? 16f : (((float)Screen.height - num2) * 0.5f));
				s_window = new Rect(num3, num4, num, num2);
				s_placed = true;
			}
			else
			{
				((Rect)(ref s_window)).width = num;
				((Rect)(ref s_window)).height = num2;
				((Rect)(ref s_window)).x = Mathf.Clamp(((Rect)(ref s_window)).x, 0f, Mathf.Max(0f, (float)Screen.width - num));
				((Rect)(ref s_window)).y = Mathf.Clamp(((Rect)(ref s_window)).y, 0f, Mathf.Max(0f, (float)Screen.height - num2));
			}
		}

		private void HandleDrag()
		{
			//IL_007c: 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_00cf: Invalid comparison between Unknown and I4
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Invalid comparison between Unknown and I4
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			Event current = Event.current;
			if (current != null)
			{
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((Rect)(ref s_window)).x + 12f, ((Rect)(ref s_window)).y + 10f, ((Rect)(ref s_window)).width - 24f, 32f);
				float num = 208f;
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, Mathf.Max(48f, ((Rect)(ref val)).width - num), ((Rect)(ref val)).height);
				if ((int)current.type == 0 && ((Rect)(ref val2)).Contains(current.mousePosition))
				{
					s_dragging = true;
					s_dragOffset = current.mousePosition - new Vector2(((Rect)(ref s_window)).x, ((Rect)(ref s_window)).y);
					current.Use();
				}
				else if ((int)current.type == 3 && s_dragging)
				{
					((Rect)(ref s_window)).x = current.mousePosition.x - s_dragOffset.x;
					((Rect)(ref s_window)).y = current.mousePosition.y - s_dragOffset.y;
					((Rect)(ref s_window)).x = Mathf.Clamp(((Rect)(ref s_window)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref s_window)).width));
					((Rect)(ref s_window)).y = Mathf.Clamp(((Rect)(ref s_window)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref s_window)).height));
					current.Use();
				}
				else if ((int)current.type == 1 && s_dragging)
				{
					s_dragging = false;
					current.Use();
				}
			}
		}

		private void DrawYielded()
		{
			//IL_0043: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			float num = 520f;
			float num2 = Mathf.Min(760f, (float)Screen.height - 40f);
			Rect rect = default(Rect);
			((Rect)(ref rect))..ctor((float)Screen.width - num - 20f, ((float)Screen.height - num2) * 0.5f, num, num2);
			DrawBox(rect, ColWindow, ColBorder, 2f);
			Rect body = default(Rect);
			((Rect)(ref body))..ctor(((Rect)(ref rect)).x + 8f, ((Rect)(ref rect)).y + 8f, ((Rect)(ref rect)).width - 16f, ((Rect)(ref rect)).height - 16f);
			DrawScaled(body);
		}

		private void DrawScaled(Rect body)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0051: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			float textScale = TextScale;
			if (textScale < 1.05f && textScale > 0.95f)
			{
				DrawActive(body);
				return;
			}
			Event current = Event.current;
			Vector2 val = ((current != null) ? current.mousePosition : Vector2.zero);
			if (current != null)
			{
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(((Rect)(ref body)).x, ((Rect)(ref body)).y);
				current.mousePosition = val2 + (val - val2) / textScale;
			}
			Matrix4x4 matrix = GUI.matrix;
			GUIUtility.ScaleAroundPivot(new Vector2(textScale, textScale), new Vector2(((Rect)(ref body)).x, ((Rect)(ref body)).y));
			try
			{
				DrawActive(new Rect(((Rect)(ref body)).x, ((Rect)(ref body)).y, ((Rect)(ref body)).width / textScale, ((Rect)(ref body)).height / textScale));
			}
			finally
			{
				GUI.matrix = matrix;
				if (current != null)
				{
					current.mousePosition = val;
				}
			}
		}

		private static void NudgeFont(int delta)
		{
			ConfigEntry<int> hallFontPercent = DFGHubPlugin.HallFontPercent;
			if (hallFontPercent != null)
			{
				hallFontPercent.Value = Mathf.Clamp(hallFontPercent.Value + delta, 80, 160);
			}
		}

		private bool NudgeButton(Rect rect, string text)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			return Button(rect, text, ColGold);
		}

		private void DrawTabs(Rect bar)
		{
			//IL_000c: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			if (s_tabs.Count == 0)
			{
				GUI.Label(bar, "No boards have registered yet.", Style(13, bold: false, (TextAnchor)3, ColMuted));
				return;
			}
			s_index = Mathf.Clamp(s_index, 0, s_tabs.Count - 1);
			float num = 4f;
			float num2 = Mathf.Min(180f, (((Rect)(ref bar)).width - num * (float)(s_tabs.Count - 1)) / (float)s_tabs.Count);
			Rect rect = default(Rect);
			for (int i = 0; i < s_tabs.Count; i++)
			{
				((Rect)(ref rect))..ctor(((Rect)(ref bar)).x + (float)i * (num2 + num), ((Rect)(ref bar)).y, num2, ((Rect)(ref bar)).height);
				bool flag = i == s_index;
				if (Button(rect, s_tabs[i].Title, flag ? ColGold : ColWhite, flag ? ColTabOn : ColTab, flag ? ColBorder : ColInner))
				{
					s_index = i;
					s_yield = false;
				}
			}
		}

		private void DrawActive(Rect body)
		{
			//IL_005f: 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_000c: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			if (s_tabs.Count == 0)
			{
				GUI.Label(body, "Sea Route, TradeWind, Blue Collar, and Bounties appear here when those mods are loaded.", Style(14, bold: false, (TextAnchor)0, ColMuted));
				return;
			}
			s_index = Mathf.Clamp(s_index, 0, s_tabs.Count - 1);
			try
			{
				s_tabs[s_index].Draw(body);
			}
			catch (Exception ex)
			{
				GUI.Label(body, "This tab hit an error. See the BepInEx log.", Style(14, bold: false, (TextAnchor)0, ColMuted));
				ManualLogSource log = DFGHubPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("DFG board tab '" + s_tabs[s_index].Id + "' failed: " + ex.Message));
				}
			}
		}

		private bool CanOpen()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				return false;
			}
			if (Menu.IsVisible() || Console.IsVisible() || InventoryGui.IsVisible() || Minimap.InTextInput())
			{
				return false;
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.IsChatDialogWindowVisible())
			{
				return false;
			}
			if (GUIUtility.keyboardControl != 0)
			{
				return false;
			}
			return true;
		}

		private bool HotkeyDown()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (DFGHubPlugin.HallKey == null)
			{
				return false;
			}
			KeyboardShortcut value = DFGHubPlugin.HallKey.Value;
			bool flag = ((KeyboardShortcut)(ref value)).IsDown();
			bool result = flag && !m_hotkeyWasDown;
			m_hotkeyWasDown = flag;
			return result;
		}

		private bool Button(Rect rect, string text, Color textColor)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			return Button(rect, text, textColor, ColTab, ColInner);
		}

		private bool Button(Rect rect, string text, Color textColor, Color background, Color border)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = ((Rect)(ref rect)).Contains((Event.current != null) ? Event.current.mousePosition : Vector2.zero);
			DrawBox(rect, flag ? (background * 1.25f) : background, flag ? ColGold : border, 1f);
			GUI.Label(rect, text, Style(13, bold: true, (TextAnchor)4, textColor));
			return GUI.Button(rect, GUIContent.none, GUIStyle.none);
		}

		private void DrawRect(Rect rect, Color color)
		{
			//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_000b: Unknown result type (might be due to invalid IL or missing references)
			Color color2 = GUI.color;
			GUI.color = color;
			GUI.DrawTexture(rect, (Texture)(object)m_white);
			GUI.color = color2;
		}

		private void DrawBorder(Rect rect, Color color, float thickness)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			DrawRect(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, thickness), color);
			DrawRect(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - thickness, ((Rect)(ref rect)).width, thickness), color);
			DrawRect(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + thickness, thickness, ((Rect)(ref rect)).height - thickness * 2f), color);
			DrawRect(new Rect(((Rect)(ref rect)).xMax - thickness, ((Rect)(ref rect)).y + thickness, thickness, ((Rect)(ref rect)).height - thickness * 2f), color);
		}

		private void DrawBox(Rect rect, Color background, Color border, float thickness)
		{
			//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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			DrawRect(rect, background);
			if (thickness > 0f)
			{
				DrawBorder(rect, border, thickness);
			}
		}

		private static GUIStyle Style(int size, bool bold, TextAnchor anchor, Color color)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected I4, but got Unknown
			//IL_009f: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			string key = size + ":" + (bold ? 1 : 0) + ":" + (int)anchor + ":" + color.r.ToString("F2") + color.g.ToString("F2") + color.b.ToString("F2");
			if (!s_styles.TryGetValue(key, out var value))
			{
				value = new GUIStyle(GUI.skin.label)
				{
					fontSize = size,
					fontStyle = (FontStyle)(bold ? 1 : 0),
					alignment = anchor,
					clipping = (TextClipping)1
				};
				value.normal.textColor = color;
				value.hover.textColor = color;
				value.active.textColor = color;
				s_styles[key] = value;
			}
			return value;
		}

		private static int IndexOf(string id)
		{
			for (int i = 0; i < s_tabs.Count; i++)
			{
				if (string.Equals(s_tabs[i].Id, id, StringComparison.OrdinalIgnoreCase))
				{
					return i;
				}
			}
			return -1;
		}

		private static void SortTabs()
		{
			s_tabs.Sort((Tab a, Tab b) => (a.Order == b.Order) ? string.Compare(a.Title, b.Title, StringComparison.OrdinalIgnoreCase) : a.Order.CompareTo(b.Order));
		}
	}
	internal static class HallInput
	{
		[HarmonyPatch(typeof(PlayerController), "InInventoryEtc")]
		[HarmonyPostfix]
		public static void InInventoryEtc(ref bool __result)
		{
			if (HallBoard.IsOpen)
			{
				__result = true;
			}
		}

		[HarmonyPatch(typeof(Player), "TakeInput")]
		[HarmonyPrefix]
		public static bool PlayerTakeInput(ref bool __result)
		{
			if (!HallBoard.IsOpen)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(PlayerController), "TakeInput")]
		[HarmonyPrefix]
		public static bool ControllerTakeInput(ref bool __result)
		{
			if (!HallBoard.IsOpen)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
		[HarmonyPostfix]
		public static void KeepCursorFree()
		{
			if (HallBoard.IsOpen)
			{
				ZCursor.LockState = (CursorLockMode)0;
				ZCursor.Show();
			}
		}
	}
	internal static class HubWeb
	{
		private static HttpListener s_listener;

		private static Thread s_thread;

		private static volatile bool s_running;

		public static void Start()
		{
			if (s_running)
			{
				return;
			}
			try
			{
				s_listener = new HttpListener();
				s_listener.Prefixes.Add("http://127.0.0.1:" + DFGHubPlugin.WebPort.Value + "/");
				s_listener.Start();
				s_running = true;
				s_thread = new Thread(Loop)
				{
					IsBackground = true
				};
				s_thread.Start();
				Logger.LogInfo((object)("DFGHub: hub at http://127.0.0.1:" + DFGHubPlugin.WebPort.Value + "/"));
			}
			catch (Exception ex)
			{
				s_running = false;
				Logger.LogWarning((object)("DFGHub: could not start web hub: " + ex.Message));
			}
		}

		public static void Stop()
		{
			s_running = false;
			try
			{
				if (s_listener != null)
				{
					s_listener.Stop();
					s_listener.Close();
				}
			}
			catch
			{
			}
			s_listener = null;
		}

		private static void Loop()
		{
			while (s_running && s_listener != null && s_listener.IsListening)
			{
				try
				{
					Handle(s_listener.GetContext());
				}
				catch
				{
				}
			}
		}

		private static void Handle(HttpListenerContext context)
		{
			try
			{
				switch (context.Request.Url.AbsolutePath)
				{
				case "/":
				case "/index.html":
					Write(context, "text/html; charset=utf-8", BuildPage());
					break;
				case "/status":
					Write(context, "application/json; charset=utf-8", BuildStatus());
					break;
				default:
					context.Response.StatusCode = 404;
					context.Response.Close();
					break;
				}
			}
			catch
			{
				try
				{
					context.Response.StatusCode = 500;
					context.Response.Close();
				}
				catch
				{
				}
			}
		}

		private static bool IsUp(int port)
		{
			try
			{
				HttpWebRequest obj = (HttpWebRequest)WebRequest.Create("http://127.0.0.1:" + port + "/");
				obj.Timeout = 250;
				obj.ReadWriteTimeout = 250;
				obj.Method = "GET";
				using ((HttpWebResponse)obj.GetResponse())
				{
					return true;
				}
			}
			catch
			{
				return false;
			}
		}

		private static string BuildStatus()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\"port\":").Append(DFGHubPlugin.WebPort.Value);
			stringBuilder.Append(",\"category\":\"").Append(Escape(DFGHubPlugin.ActiveCategory)).Append("\"");
			stringBuilder.Append(",\"pieces\":").Append(DFGHubPlugin.RoutedPieceCount);
			stringBuilder.Append(",\"mods\":[");
			for (int i = 0; i < WebNav.Links.Length; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(",");
				}
				WebNav.Link link = WebNav.Links[i];
				stringBuilder.Append("{\"id\":\"").Append(Escape(link.Id)).Append("\"");
				stringBuilder.Append(",\"name\":\"").Append(Escape(link.Label)).Append("\"");
				stringBuilder.Append(",\"port\":").Append(link.Port);
				stringBuilder.Append(",\"up\":").Append(IsUp(link.Port) ? "true" : "false");
				stringBuilder.Append("}");
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private static string Escape(string value)
		{
			return (value ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"");
		}

		private static void Write(HttpListenerContext context, string contentType, string body)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(body ?? "");
			context.Response.ContentType = contentType;
			context.Response.ContentEncoding = Encoding.UTF8;
			context.Response.ContentLength64 = bytes.Length;
			context.Response.OutputStream.Write(bytes, 0, bytes.Length);
			context.Response.OutputStream.Close();
		}

		private static string BuildPage()
		{
			return "<!DOCTYPE html>\n<html>\n<head>\n<meta charset='utf-8'>\n<title>DFG Hub</title>\n<style>\n*{box-sizing:border-box;font-family:Segoe UI,Arial,sans-serif}\nbody{margin:0;background:#10151c;color:#dce3ec;font-size:14px}\n#wrap{max-width:900px;margin:0 auto;padding:8px 16px 30px}\nh1{margin:10px 0;font-size:20px;color:#9fd7ff}\nh1 small{color:#6b7f8c;font-weight:400}\n.sub{color:#7f8fa0;font-size:13px;margin-bottom:16px}\nnav.top{position:sticky;top:0;z-index:20;font-family:Segoe UI,Arial,sans-serif;background:#0d1219;border-bottom:1px solid #2b3b4c;padding:6px 14px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}\nnav.top .brand{color:#7f8fa0;font-size:11px;text-transform:uppercase;letter-spacing:.6px;margin-right:8px}\nnav.top a{color:#9fc3dd;text-decoration:none;font-size:13px;padding:4px 9px;border-radius:5px}\nnav.top a:hover{background:#1a2530}\nnav.top a.here{background:#3a2b4c;border:1px solid #55406b;color:#efe0ff}\n\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}\n.card{background:#151e28;border:1px solid #28363f;border-radius:8px;padding:14px;display:flex;flex-direction:column;gap:10px}\n.card .name{font-size:15px;font-weight:600;color:#e6f4ff}\n.card .meta{color:#7f8fa0;font-size:12px}\n.card a{display:inline-block;background:#2e6b96;border:1px solid #3f86b8;color:#e6f4ff;text-decoration:none;border-radius:5px;padding:7px 10px;text-align:center;font-size:13px}\n.card a:hover{background:#3a82b8}\n.dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px;vertical-align:1px}\n.dot.up{background:#59d07e}\n.dot.down{background:#c05a5a}\n.info{background:#151e28;border:1px solid #28363f;border-radius:8px;padding:12px;margin-top:16px;color:#9fb3c4;font-size:13px;line-height:1.6}\n</style>\n</head>\n<body>\n" + WebNav.Html("hub") + "\n<div id='wrap'>\n<h1>DFG Hub <small id='ver'></small></h1>\n<div class='sub' id='summary'>Loading&hellip;</div>\n<div class='grid' id='grid'></div>\n<div class='info'>\nAll build pieces from this mod collection are routed into the in-game Hammer category <b id='cat'>DFG</b> and unlocked by default (no crafting station required). If a link below shows offline, that mod is not loaded or its web GUI is disabled.\n</div>\n</div>\n<script>\nvar $=function(s){return document.querySelector(s)};\nfunction mk(tag,cls,text){var e=document.createElement(tag);if(cls)e.className=cls;if(text!==undefined)e.textContent=text;return e}\nfunction render(s){\n  $('#ver').textContent='v1.0.0';\n  $('#cat').textContent=s.category||'DFG';\n  $('#summary').textContent='Hammer category: '+s.category+' - '+s.pieces+' pieces routed.';\n  var g=$('#grid');g.innerHTML='';\n  (s.mods||[]).forEach(function(m){\n    var c=mk('div','card');\n    c.appendChild(mk('div','name',m.name));\n    var meta=mk('div','meta');meta.textContent='127.0.0.1:'+m.port;\n    c.appendChild(meta);\n    var a=mk('a',null,'Open');a.href='http://127.0.0.1:'+m.port+'/';a.target='_blank';\n    c.appendChild(a);\n    var st=mk('div','meta');\n    var d=mk('span','dot '+(m.up?'up':'down'));\n    st.appendChild(d);\n    st.appendChild(document.createTextNode(m.up?'online':'offline'));\n    c.appendChild(st);\n    g.appendChild(c);\n  });\n}\nfunction poll(){\n  fetch('/status').then(function(r){return r.json()}).then(render).catch(function(){});\n}\npoll();\nsetInterval(poll,2500);\n</script>\n</body>\n</html>";
		}
	}
}