Decompiled source of ReturnToLobbyAnyway v1.0.0

BepInEx/plugins/ReturnToLobbyAnyway/ReturnToLobbyAnyway.dll

Decompiled 4 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using HarmonyLib;
using HawChow.ReturnToLobbyAnyway;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using SNetwork;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[BepInPlugin("QingJu.ReturnToLobbyAnyway", "ReturnToLobbyAnyway", "1.0.0")]
public sealed class Plugin : BasePlugin
{
	internal static ManualLogSource Logger;

	internal static ConfigEntry<string> Language;

	public override void Load()
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		Logger = ((BasePlugin)this).Log;
		Language = ((BasePlugin)this).Config.Bind<string>("General", "Language", "zh", "Button copy. zh (default) = 长按 1 秒返回大厅, en = HOLD TO RETURN TO LOBBY. Chinese needs a CJK-capable system font; if none can be created the button falls back to English automatically.");
		ClassInjector.RegisterTypeInIl2Cpp<ReturnLobbyButton>();
		((BasePlugin)this).AddComponent<ReturnLobbyButton>();
		Harmony val = new Harmony("QingJu.ReturnToLobbyAnyway");
		val.PatchAll();
		Logger.LogInfo((object)"Loaded: guest-only hold-to-return patches are active.");
		Logger.LogInfo((object)("patch state: " + DescribePatches(val)));
	}

	private static string DescribePatches(Harmony harmony)
	{
		try
		{
			return "fail.Update=" + Describe(harmony, typeof(CM_PageExpeditionFail)) + " | success.Update=" + Describe(harmony, typeof(CM_PageExpeditionSuccess));
		}
		catch (Exception ex)
		{
			return "unavailable (" + ex.Message + ")";
		}
	}

	private static string Describe(Harmony harmony, Type page)
	{
		MethodBase methodBase = AccessTools.Method(page, "Update", (Type[])null, (Type[])null);
		if (methodBase == null)
		{
			return "target-missing";
		}
		Patches patchInfo = Harmony.GetPatchInfo(methodBase);
		if (patchInfo == null)
		{
			return "no-patch-info";
		}
		return "pre:" + patchInfo.Prefixes.Count + ",post:" + patchInfo.Postfixes.Count;
	}
}
namespace HawChow.ReturnToLobbyAnyway;

public static class ButtonTextPolicy
{
	public const string ChineseLabel = "长按 1 秒返回大厅";

	public const string EnglishLabel = "HOLD TO RETURN TO LOBBY";

	public static bool IsEnglish(string language)
	{
		if (string.IsNullOrWhiteSpace(language))
		{
			return false;
		}
		string text = language.Trim().ToLowerInvariant();
		switch (text)
		{
		default:
			return text == "english";
		case "en":
		case "en-us":
		case "en-gb":
			return true;
		}
	}

	public static string Label(string language, int percent, bool cjkFontAvailable)
	{
		int num = ((percent >= 0) ? ((percent > 100) ? 100 : percent) : 0);
		return ((IsEnglish(language) || !cjkFontAvailable) ? "HOLD TO RETURN TO LOBBY" : "长按 1 秒返回大厅") + "  (" + num + "%)";
	}
}
internal static class SettlementPageTracker
{
	internal static CM_PageBase Page { get; private set; }

	internal static TextMeshPro WaitingText { get; private set; }

	internal static int Revision { get; private set; }

	internal static void Observe(CM_PageExpeditionFail page)
	{
		TextMeshPro text = null;
		if (IsPageActive((CM_PageBase)(object)page))
		{
			text = page.m_returnToLobby_text;
			if (!IsTextVisible(text))
			{
				text = FindWaitingText((CM_PageBase)(object)page);
			}
		}
		Set((CM_PageBase)(object)page, text);
	}

	internal static void Observe(CM_PageExpeditionSuccess page)
	{
		TextMeshPro text = (IsPageActive((CM_PageBase)(object)page) ? FindWaitingText((CM_PageBase)(object)page) : null);
		Set((CM_PageBase)(object)page, text);
	}

	internal static void Clear()
	{
		Set(null, null);
	}

	internal static bool IsPageActive(CM_PageBase page)
	{
		if ((Object)(object)page != (Object)null && ((Behaviour)page).isActiveAndEnabled && (Object)(object)((Component)page).gameObject != (Object)null && ((Component)page).gameObject.activeInHierarchy)
		{
			return page.m_isActive;
		}
		return false;
	}

	internal static bool IsTextVisible(TextMeshPro text)
	{
		if ((Object)(object)text != (Object)null && ((Behaviour)text).enabled && (Object)(object)((Component)text).gameObject != (Object)null && ((Component)text).gameObject.activeInHierarchy)
		{
			return !string.IsNullOrWhiteSpace(((TMP_Text)text).text);
		}
		return false;
	}

	private static void Set(CM_PageBase page, TextMeshPro text)
	{
		int num = ((!((Object)(object)page == (Object)null)) ? ((Object)page).GetInstanceID() : 0);
		int num2 = ((!((Object)(object)text == (Object)null)) ? ((Object)text).GetInstanceID() : 0);
		int num3 = ((!((Object)(object)Page == (Object)null)) ? ((Object)Page).GetInstanceID() : 0);
		int num4 = ((!((Object)(object)WaitingText == (Object)null)) ? ((Object)WaitingText).GetInstanceID() : 0);
		Page = page;
		WaitingText = text;
		if (num != num3 || num2 != num4)
		{
			Revision++;
		}
	}

	private static TextMeshPro FindWaitingText(CM_PageBase page)
	{
		Il2CppReferenceArray<iCellMenuCursorItem> allItems = page.m_allItems;
		if (allItems == null)
		{
			return null;
		}
		for (int i = 0; i < ((Il2CppArrayBase<iCellMenuCursorItem>)(object)allItems).Length; i++)
		{
			CM_Item val = ((((Il2CppArrayBase<iCellMenuCursorItem>)(object)allItems)[i] == null) ? null : ((Il2CppObjectBase)((Il2CppArrayBase<iCellMenuCursorItem>)(object)allItems)[i]).TryCast<CM_Item>());
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Il2CppReferenceArray<TextMeshPro> texts = val.GetTexts();
			if (texts == null)
			{
				continue;
			}
			for (int j = 0; j < ((Il2CppArrayBase<TextMeshPro>)(object)texts).Length; j++)
			{
				TextMeshPro val2 = ((Il2CppArrayBase<TextMeshPro>)(object)texts)[j];
				if (IsTextVisible(val2) && IsWaitingCopy(((TMP_Text)val2).text))
				{
					return val2;
				}
			}
		}
		return null;
	}

	private static bool IsWaitingCopy(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return false;
		}
		bool num = value.IndexOf("wait", StringComparison.OrdinalIgnoreCase) >= 0 && value.IndexOf("host", StringComparison.OrdinalIgnoreCase) >= 0 && (value.IndexOf("lobby", StringComparison.OrdinalIgnoreCase) >= 0 || value.IndexOf("return", StringComparison.OrdinalIgnoreCase) >= 0);
		bool flag = value.Contains("等待") && (value.Contains("厅主") || value.Contains("房主") || value.Contains("主机")) && (value.Contains("大厅") || value.Contains("返回") || value.Contains("送回"));
		return num || flag;
	}
}
[HarmonyPatch(typeof(CM_PageExpeditionFail), "Update")]
internal static class ExpeditionFailPagePatch
{
	[HarmonyPostfix]
	private static void Postfix(CM_PageExpeditionFail __instance)
	{
		SettlementPageTracker.Observe(__instance);
	}
}
[HarmonyPatch(typeof(CM_PageExpeditionSuccess), "Update")]
internal static class ExpeditionSuccessPagePatch
{
	[HarmonyPostfix]
	private static void Postfix(CM_PageExpeditionSuccess __instance)
	{
		SettlementPageTracker.Observe(__instance);
	}
}
public sealed class ReturnLobbyButton : MonoBehaviour
{
	private const float HoldSeconds = 1f;

	private const float ButtonWidth = 300f;

	private const float ButtonHeight = 54f;

	private const float GapFromLabel = 14f;

	private const int ButtonFontSize = 19;

	private static readonly string[] CjkFontNames = new string[5] { "Microsoft YaHei UI", "Microsoft YaHei", "SimHei", "SimSun", "Noto Sans CJK SC" };

	private static Font cjkFont;

	private static GUIStyle buttonStyle;

	private static bool fontResolved;

	private static bool fontLogged;

	private readonly HoldController hold = new HoldController();

	private Rect buttonRect;

	private int observedRevision = -1;

	private bool visible;

	private bool pointerStartedInside;

	private bool leaveRequested;

	public ReturnLobbyButton(IntPtr pointer)
		: base(pointer)
	{
	}

	private void Update()
	{
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		if (observedRevision != SettlementPageTracker.Revision)
		{
			observedRevision = SettlementPageTracker.Revision;
			hold.Reset();
			pointerStartedInside = false;
			leaveRequested = false;
		}
		visible = CanShowButton() && TryPlaceBesideWaitingText(SettlementPageTracker.WaitingText, out buttonRect);
		if (!visible)
		{
			pointerStartedInside = false;
			hold.Tick(eligible: false, pressed: false, 0f);
			return;
		}
		Vector3 mousePosition = Input.mousePosition;
		Vector2 val = default(Vector2);
		((Vector2)(ref val))..ctor(mousePosition.x, (float)Screen.height - mousePosition.y);
		bool flag = ((Rect)(ref buttonRect)).Contains(val);
		if (Input.GetMouseButtonDown(0))
		{
			pointerStartedInside = flag;
		}
		bool mouseButton = Input.GetMouseButton(0);
		bool pressed = mouseButton && pointerStartedInside && flag && !leaveRequested;
		if (hold.Tick(!leaveRequested, pressed, Time.unscaledDeltaTime))
		{
			leaveRequested = true;
			LeaveCurrentLobby();
		}
		if (!mouseButton || Input.GetMouseButtonUp(0))
		{
			pointerStartedInside = false;
		}
	}

	private void OnGUI()
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		if (visible && !leaveRequested)
		{
			GUIStyle val = Style();
			int percent = Mathf.RoundToInt(hold.Progress * 100f);
			string language = ((Plugin.Language == null) ? "zh" : Plugin.Language.Value);
			GUI.Button(buttonRect, ButtonTextPolicy.Label(language, percent, (Object)(object)cjkFont != (Object)null), val);
		}
	}

	private static GUIStyle Style()
	{
		//IL_0038: 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_003e: Expected O, but got Unknown
		Font val = CjkFont();
		if (buttonStyle != null && fontResolved)
		{
			return buttonStyle;
		}
		GUIStyle val2 = (((Object)(object)GUI.skin == (Object)null) ? new GUIStyle() : new GUIStyle(GUI.skin.button));
		val2.alignment = (TextAnchor)4;
		val2.wordWrap = false;
		val2.fontSize = 19;
		if ((Object)(object)val != (Object)null)
		{
			val2.font = val;
		}
		buttonStyle = val2;
		fontResolved = true;
		return buttonStyle;
	}

	private static Font CjkFont()
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected O, but got Unknown
		if ((Object)(object)cjkFont != (Object)null && ((Il2CppObjectBase)cjkFont).Pointer != IntPtr.Zero)
		{
			return cjkFont;
		}
		try
		{
			cjkFont = new Font();
			Il2CppStringArray val = new Il2CppStringArray(CjkFontNames);
			Font.Internal_CreateDynamicFont(cjkFont, val, 19);
			if ((Object)(object)cjkFont == (Object)null || ((Il2CppObjectBase)cjkFont).Pointer == IntPtr.Zero)
			{
				cjkFont = null;
				LogFontOnce("[DIAG] no CJK-capable system font was created; the button falls back to English copy.");
				return null;
			}
			if (!fontLogged)
			{
				fontLogged = true;
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)("[DIAG] button font: '" + ((Object)cjkFont).name + "' loaded from the operating system (CJK capable)."));
				}
			}
			return cjkFont;
		}
		catch (Exception ex)
		{
			cjkFont = null;
			LogFontOnce("[DIAG] CJK font creation failed, the button falls back to English copy: " + ex.Message);
			return null;
		}
	}

	private static void LogFontOnce(string message)
	{
		if (!fontLogged)
		{
			fontLogged = true;
			ManualLogSource logger = Plugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)message);
			}
		}
	}

	private static bool CanShowButton()
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Invalid comparison between Unknown and I4
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Invalid comparison between Unknown and I4
		CM_PageBase page = SettlementPageTracker.Page;
		TextMeshPro waitingText = SettlementPageTracker.WaitingText;
		eGameStateName currentStateName = GameStateManager.CurrentStateName;
		bool flag = (int)currentStateName == 14 || (int)currentStateName == 15;
		bool flag2 = SettlementPageTracker.IsPageActive(page);
		bool waitingLabelVisible = SettlementPageTracker.IsTextVisible(waitingText);
		return VisibilityPolicy.ShouldShow(SNet.IsOnline, SNet.IsMaster, flag && flag2, waitingLabelVisible);
	}

	private static bool TryPlaceBesideWaitingText(TextMeshPro text, out Rect rect)
	{
		//IL_0001: 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_004b: 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_0064: 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_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		rect = default(Rect);
		if (!SettlementPageTracker.IsTextVisible(text) || (Object)(object)((TMP_Text)text).rectTransform == (Object)null)
		{
			return false;
		}
		Camera val = CM_PageBase.Camera;
		if ((Object)(object)val == (Object)null)
		{
			val = Camera.main;
		}
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		RectTransform rectTransform = ((TMP_Text)text).rectTransform;
		Rect rect2 = rectTransform.rect;
		Vector3 val2 = ((Transform)rectTransform).TransformPoint(new Vector3(((Rect)(ref rect2)).xMax, ((Rect)(ref rect2)).center.y, 0f));
		Vector3 val3 = val.WorldToScreenPoint(val2);
		if (val3.z <= 0f)
		{
			return false;
		}
		float num = (float)Screen.width - val3.x - 14f - 16f;
		if (num < 210f)
		{
			return false;
		}
		float num2 = Mathf.Min(300f, num);
		float num3 = val3.x + 14f;
		float num4 = Mathf.Clamp((float)Screen.height - val3.y - 27f, 16f, (float)Screen.height - 54f - 16f);
		rect = new Rect(num3, num4, num2, 54f);
		return true;
	}

	private static void LeaveCurrentLobby()
	{
		try
		{
			SNet_LobbyManager lobbies = SNet.Lobbies;
			if ((Object)(object)lobbies == (Object)null)
			{
				Plugin.Logger.LogError((object)"Long-hold completed, but SNet.Lobbies was unavailable.");
				return;
			}
			lobbies.LeaveLobby();
			Plugin.Logger.LogInfo((object)"Guest requested the native local lobby-leave flow.");
		}
		catch (Exception ex)
		{
			Plugin.Logger.LogError((object)("Native lobby leave failed: " + ex));
		}
	}
}
public sealed class HoldController
{
	private readonly float requiredSeconds;

	private float elapsedSeconds;

	private bool triggered;

	public float Progress => Math.Min(1f, elapsedSeconds / requiredSeconds);

	public bool IsHolding
	{
		get
		{
			if (elapsedSeconds > 0f)
			{
				return !triggered;
			}
			return false;
		}
	}

	public HoldController(float requiredSeconds = 1f)
	{
		this.requiredSeconds = Math.Max(0.01f, requiredSeconds);
	}

	public bool Tick(bool eligible, bool pressed, float deltaSeconds)
	{
		if (!eligible || !pressed)
		{
			Reset();
			return false;
		}
		if (triggered)
		{
			return false;
		}
		if (deltaSeconds > 0f && !float.IsNaN(deltaSeconds) && !float.IsInfinity(deltaSeconds))
		{
			elapsedSeconds += deltaSeconds;
		}
		if (elapsedSeconds < requiredSeconds)
		{
			return false;
		}
		elapsedSeconds = requiredSeconds;
		triggered = true;
		return true;
	}

	public void Reset()
	{
		elapsedSeconds = 0f;
		triggered = false;
	}
}
internal static class PluginInfo
{
	internal const string Guid = "QingJu.ReturnToLobbyAnyway";

	internal const string Name = "ReturnToLobbyAnyway";

	internal const string Version = "1.0.0";
}
public static class VisibilityPolicy
{
	public static bool ShouldShow(bool isOnline, bool isMaster, bool settlementPageActive, bool waitingLabelVisible)
	{
		return isOnline && !isMaster && settlementPageActive && waitingLabelVisible;
	}
}