Decompiled source of MyAwesomeWhitelist v0.1.0

plugins/MyAwesomeWhitelist.dll

Decompiled 2 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Multiplayer;
using MyAwesomeWhitelist.Config;
using MyAwesomeWhitelist.Core;
using MyAwesomeWhitelist.I18n;
using MyAwesomeWhitelist.Lists;
using MyAwesomeWhitelist.Patches;
using MyAwesomeWhitelist.UI;
using Steamworks;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 MyAwesomeWhitelist
{
	[BepInPlugin("MyAwesomeWhitelist", "MyAwesomeWhitelist", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		internal static PluginConfig Config;

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin MyAwesomeWhitelist v0.1.0 is loaded!");
			Config = new PluginConfig(new ConfigFile(Path.Combine(Path.Combine(Paths.ConfigPath, "MyAwesomeWhitelist"), "MyAwesomeWhitelist.cfg"), false));
			ListService.Init();
			SessionTracker.Init();
			NameCache.Init();
			LocalizationService.Init();
			PatchModule.Apply();
			Config.BlacklistEnabled.SettingChanged += NetGamePatches.OnBlacklistToggleChanged;
			GameObject val = new GameObject("MyAwesomeWhitelist.Window");
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<WhitelistWindow>();
		}
	}
	internal static class PluginInfo
	{
		public const string PLUGIN_GUID = "MyAwesomeWhitelist";

		public const string PLUGIN_NAME = "MyAwesomeWhitelist";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}
namespace MyAwesomeWhitelist.UI
{
	internal sealed class Hotkey
	{
		private readonly string _spec;

		private static readonly Dictionary<string, KeyCode> NamedKeys = new Dictionary<string, KeyCode>(StringComparer.OrdinalIgnoreCase)
		{
			{
				"Space",
				(KeyCode)32
			},
			{
				"Enter",
				(KeyCode)13
			},
			{
				"Return",
				(KeyCode)13
			},
			{
				"KeypadEnter",
				(KeyCode)271
			},
			{
				"Tab",
				(KeyCode)9
			},
			{
				"Esc",
				(KeyCode)27
			},
			{
				"Escape",
				(KeyCode)27
			},
			{
				"Backspace",
				(KeyCode)8
			},
			{
				"Delete",
				(KeyCode)127
			},
			{
				"Up",
				(KeyCode)273
			},
			{
				"Down",
				(KeyCode)274
			},
			{
				"Left",
				(KeyCode)276
			},
			{
				"Right",
				(KeyCode)275
			},
			{
				"Home",
				(KeyCode)278
			},
			{
				"End",
				(KeyCode)279
			},
			{
				"PageUp",
				(KeyCode)280
			},
			{
				"PageDown",
				(KeyCode)281
			},
			{
				"Insert",
				(KeyCode)277
			}
		};

		public bool Ctrl { get; private set; }

		public bool Shift { get; private set; }

		public bool Alt { get; private set; }

		public KeyCode Key { get; private set; }

		public bool Valid { get; }

		public bool IsDown
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				if (Valid && ModifiersHeld())
				{
					return Input.GetKeyDown(Key);
				}
				return false;
			}
		}

		public Hotkey(string spec)
		{
			//IL_0046: 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: Invalid comparison between Unknown and I4
			_spec = (spec ?? "").Trim();
			Parse(_spec, out var ctrl, out var shift, out var alt, out var key, out var ok);
			Ctrl = ctrl;
			Shift = shift;
			Alt = alt;
			Key = key;
			Valid = ok && (int)key > 0;
		}

		public bool Matches(Event ev)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (!Valid || (int)ev.type != 4 || ev.keyCode != Key)
			{
				return false;
			}
			if ((ev.control || ev.command) == Ctrl && ev.shift == Shift)
			{
				return ev.alt == Alt;
			}
			return false;
		}

		public override string ToString()
		{
			return _spec;
		}

		private bool ModifiersHeld()
		{
			if (Ctrl && !Input.GetKey((KeyCode)306) && !Input.GetKey((KeyCode)305) && !Input.GetKey((KeyCode)310) && !Input.GetKey((KeyCode)309))
			{
				return false;
			}
			if (Shift && !Input.GetKey((KeyCode)304) && !Input.GetKey((KeyCode)303))
			{
				return false;
			}
			if (Alt && !Input.GetKey((KeyCode)308) && !Input.GetKey((KeyCode)307))
			{
				return false;
			}
			return true;
		}

		private static void Parse(string spec, out bool ctrl, out bool shift, out bool alt, out KeyCode key, out bool ok)
		{
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected I4, but got Unknown
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected I4, but got Unknown
			ctrl = (shift = (alt = false));
			key = (KeyCode)0;
			ok = false;
			if (string.IsNullOrWhiteSpace(spec))
			{
				return;
			}
			string[] array = spec.Split(new char[1] { '+' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				KeyCode value;
				if (i < array.Length - 1)
				{
					string text2 = text.ToLowerInvariant();
					if (text2 == null)
					{
						return;
					}
					switch (text2.Length)
					{
					default:
						return;
					case 7:
						switch (text2[2])
						{
						default:
							return;
						case 'n':
							if (!(text2 == "control"))
							{
								return;
							}
							break;
						case 'm':
							if (!(text2 == "command"))
							{
								return;
							}
							break;
						}
						goto IL_0123;
					case 3:
					{
						char c = text2[0];
						if (c != 'a')
						{
							if (c != 'c' || !(text2 == "cmd"))
							{
								return;
							}
							goto IL_0123;
						}
						if (!(text2 == "alt"))
						{
							return;
						}
						break;
					}
					case 4:
						if (!(text2 == "ctrl"))
						{
							return;
						}
						goto IL_0123;
					case 5:
						if (!(text2 == "shift"))
						{
							return;
						}
						shift = true;
						continue;
					case 6:
						{
							if (!(text2 == "option"))
							{
								return;
							}
							break;
						}
						IL_0123:
						ctrl = true;
						continue;
					}
					alt = true;
				}
				else if (text.Length == 1 && char.IsLetterOrDigit(text[0]))
				{
					char c2 = text[0];
					if (c2 >= 'A' && c2 <= 'Z')
					{
						c2 = (char)(c2 - 65 + 97);
					}
					key = (KeyCode)c2;
				}
				else if (NamedKeys.TryGetValue(text, out value))
				{
					key = (KeyCode)(int)value;
				}
				else
				{
					if (!Enum.TryParse<KeyCode>(text, ignoreCase: true, out KeyCode result))
					{
						return;
					}
					key = (KeyCode)(int)result;
				}
			}
			ok = (int)key != 0;
		}
	}
	internal sealed class Styles
	{
		public GUIStyle Label;

		public GUIStyle Value;

		public GUIStyle Section;

		public GUIStyle Small;

		public GUIStyle Toggle;

		public GUIStyle Button;

		public GUIStyle TextField;

		public GUIStyle MiniButton;

		public GUIStyle Header;

		public GUIStyle Box;

		public GUIStyle ListBox;

		public GUIStyle Path;

		private static Texture2D _bgTex;

		public bool Ready { get; private set; }

		public void Ensure()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00ba: 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_00c6: 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_00da: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: 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_014f: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Expected O, but got Unknown
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Expected O, but got Unknown
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Expected O, but got Unknown
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Expected O, but got Unknown
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Expected O, but got Unknown
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Expected O, but got Unknown
			if (!Ready)
			{
				Font val = null;
				try
				{
					val = Font.CreateDynamicFontFromOSFont(new string[8] { "Microsoft YaHei", "PingFang SC", "Heiti SC", "STHeiti", "Noto Sans CJK SC", "WenQuanYi Zen Hei", "Arial Unicode MS", "Arial" }, 14);
				}
				catch
				{
					val = null;
				}
				Label = new GUIStyle(GUI.skin.label)
				{
					font = val,
					fontSize = 13,
					wordWrap = false
				};
				Value = new GUIStyle(GUI.skin.label)
				{
					font = val,
					fontSize = 13
				};
				Section = new GUIStyle(GUI.skin.label)
				{
					font = val,
					fontSize = 14,
					fontStyle = (FontStyle)1
				};
				Small = new GUIStyle(GUI.skin.label)
				{
					font = val,
					fontSize = 11,
					wordWrap = true
				};
				GUIStyle val2 = new GUIStyle(GUI.skin.label)
				{
					font = val,
					fontSize = 12,
					fontStyle = (FontStyle)1
				};
				val2.normal.textColor = new Color(0.72f, 0.78f, 0.9f);
				Header = val2;
				Toggle = new GUIStyle(GUI.skin.toggle)
				{
					font = val,
					fontSize = 13,
					wordWrap = false
				};
				Button = new GUIStyle(GUI.skin.button)
				{
					font = val,
					fontSize = 13,
					wordWrap = false
				};
				MiniButton = new GUIStyle(GUI.skin.button)
				{
					font = val,
					fontSize = 11,
					wordWrap = false
				};
				TextField = new GUIStyle(GUI.skin.textField)
				{
					font = val,
					fontSize = 13
				};
				_bgTex = DarkBg();
				Box = new GUIStyle(GUI.skin.box);
				Box.normal.background = _bgTex;
				Box.padding = new RectOffset(8, 8, 6, 6);
				Texture2D val3 = new Texture2D(1, 1, (TextureFormat)4, false);
				val3.SetPixel(0, 0, new Color(0.1f, 0.11f, 0.14f, 0.9f));
				val3.Apply();
				ListBox = new GUIStyle(GUI.skin.box);
				ListBox.normal.background = val3;
				ListBox.padding = new RectOffset(0, 0, 0, 0);
				Path = new GUIStyle(GUI.skin.label)
				{
					font = val,
					fontSize = 11,
					wordWrap = false
				};
				Ready = true;
			}
		}

		private static Texture2D DarkBg()
		{
			//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)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false);
			val.SetPixel(0, 0, new Color(0.07f, 0.08f, 0.1f, 0.94f));
			val.Apply();
			return val;
		}
	}
	internal sealed class WhitelistWindow : MonoBehaviour
	{
		private sealed class BlockCursorRaycasts : UIBehaviour, ICanvasRaycastFilter
		{
			internal Rect WindowRect;

			public bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				return !((Rect)(ref WindowRect)).Contains(screenPoint);
			}

			private void LateUpdate()
			{
				((Component)this).transform.SetAsLastSibling();
			}
		}

		private sealed class FriendEntry
		{
			public string Name;

			public string SteamId;
		}

		private bool _open;

		private Rect _rect = new Rect(16f, 16f, 620f, 560f);

		private readonly Styles _styles = new Styles();

		private Hotkey _hotkey;

		private int _tab;

		private string[] _tabs;

		private Vector2 _scroll;

		private int _whiteWhich;

		private int _blackWhich;

		private Vector2 _whiteListScroll;

		private Vector2 _blackListScroll;

		private string _whiteInput = string.Empty;

		private string _blackInput = string.Empty;

		private string _hotkeyInput;

		private string _hotkeyError;

		private bool _langOpen;

		private Rect _langPopupRect;

		private string _toast;

		private float _toastUntil;

		private bool _pauseShown;

		private float _nextSweep;

		private float _friendCacheUntil;

		private List<FriendEntry> _friendCache;

		private string _friendQuery = string.Empty;

		private GameObject _blocker;

		private BlockCursorRaycasts _blockerBehaviour;

		private static List<SessionTracker.PlayerInfo> _playerCache;

		private static bool IsHost
		{
			get
			{
				if ((Object)(object)NetGame.instance != (Object)null && NetGame.isServer)
				{
					return NetGame.isNetStarted;
				}
				return false;
			}
		}

		private static string L(string key)
		{
			return LocalizationService.Instance.Get(key);
		}

		private static string LF(string key, params object[] args)
		{
			return string.Format(LocalizationService.Instance.Get(key), args);
		}

		private void Awake()
		{
			_ = LocalizationService.Instance;
			_tabs = new string[5]
			{
				L("TAB_PLAYERS"),
				L("TAB_WHITELIST"),
				L("TAB_BLACKLIST"),
				L("TAB_FRIENDS"),
				L("TAB_SETTINGS")
			};
			_hotkey = new Hotkey(Plugin.Config.ToggleWindowHotkey.Value);
			_hotkeyInput = Plugin.Config.ToggleWindowHotkey.Value;
		}

		private void Update()
		{
			KickService.DrainMainThreadQueue();
			if (!_open && _hotkey != null && _hotkey.IsDown)
			{
				Open();
			}
			if (Time.unscaledTime >= _nextSweep)
			{
				_nextSweep = Time.unscaledTime + 1f;
				SweepBlacklist();
			}
		}

		private void OnGUI()
		{
			//IL_007a: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			//IL_0064: 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_0035: Invalid comparison between Unknown and I4
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			_styles.Ensure();
			if (!_open)
			{
				return;
			}
			if (_open && (int)Event.current.type == 4 && ((int)Event.current.keyCode == 27 || (_hotkey != null && _hotkey.Matches(Event.current))))
			{
				Close();
				Event.current.Use();
				return;
			}
			try
			{
				MenuSystem.keyboardState = (KeyboardState)2;
			}
			catch
			{
			}
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
			GUI.color = Color.white;
			GUILayout.BeginArea(_rect, _styles.Box);
			DrawContent();
			if (_langOpen)
			{
				DrawLangPopup();
			}
			GUILayout.EndArea();
			if ((Object)(object)_blockerBehaviour != (Object)null)
			{
				_blockerBehaviour.WindowRect = _rect;
			}
		}

		private void Open()
		{
			_open = true;
			_langOpen = false;
			ListService.Instance?.ReloadFromDisk();
			EnsureBlocker();
			if ((Object)(object)_blocker != (Object)null)
			{
				_blocker.SetActive(true);
			}
			TryEnterPauseMenu();
		}

		private void EnsureBlocker()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_blocker != (Object)null)
			{
				return;
			}
			try
			{
				Canvas val = Object.FindObjectOfType<Canvas>();
				if (!((Object)(object)val == (Object)null))
				{
					_blocker = new GameObject("MyAwesomeWhitelist.ClickBlocker");
					_blocker.transform.SetParent(((Component)val).transform, false);
					Image obj = _blocker.AddComponent<Image>();
					((Graphic)obj).color = new Color(0f, 0f, 0f, 0f);
					((Graphic)obj).raycastTarget = true;
					_blockerBehaviour = _blocker.AddComponent<BlockCursorRaycasts>();
					RectTransform rectTransform = ((Graphic)obj).rectTransform;
					rectTransform.anchorMin = Vector2.zero;
					rectTransform.anchorMax = Vector2.one;
					rectTransform.offsetMin = Vector2.zero;
					rectTransform.offsetMax = Vector2.zero;
					_blocker.transform.SetAsLastSibling();
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[UI] Click blocker setup failed: " + ex.Message));
				_blocker = null;
			}
		}

		private void TryEnterPauseMenu()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MenuSystem instance = MenuSystem.instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)Game.instance == (Object)null) && (int)instance.state == 0)
				{
					instance.ShowPauseMenu();
					_pauseShown = true;
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[UI] ShowPauseMenu failed: " + ex.Message));
			}
		}

		private void Close()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			_open = false;
			_langOpen = false;
			GUIUtility.keyboardControl = 0;
			if ((Object)(object)_blocker != (Object)null)
			{
				_blocker.SetActive(false);
			}
			if (_pauseShown)
			{
				_pauseShown = false;
				try
				{
					MenuSystem instance = MenuSystem.instance;
					if (instance != null)
					{
						instance.HideMenus();
					}
				}
				catch
				{
				}
			}
			try
			{
				MenuSystem.keyboardState = (KeyboardState)0;
			}
			catch
			{
			}
		}

		private void Toast(string key, params object[] args)
		{
			_toast = LF(key, args);
			_toastUntil = Time.realtimeSinceStartup + 3f;
		}

		private void ToastRaw(string msg)
		{
			_toast = msg;
			_toastUntil = Time.realtimeSinceStartup + 3f;
		}

		private void DrawContent()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Invalid comparison between Unknown and I4
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: 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_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: 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_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: 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)
			ListService instance = ListService.Instance;
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("  MyAwesomeWhitelist v0.1.0", _styles.Header, Array.Empty<GUILayoutOption>());
			Rect lastRect = GUILayoutUtility.GetLastRect();
			GUILayout.FlexibleSpace();
			if (GUILayout.Button(L("BTN_CLOSE"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(28f),
				GUILayout.Height(22f)
			}))
			{
				Close();
			}
			GUILayout.EndHorizontal();
			if ((int)Event.current.type == 3)
			{
				Rect val = new Rect(0f, ((Rect)(ref lastRect)).y - 2f, ((Rect)(ref _rect)).width - 32f, Mathf.Max(((Rect)(ref lastRect)).height + 4f, 24f));
				if (((Rect)(ref val)).Contains(Event.current.mousePosition))
				{
					ref Rect rect = ref _rect;
					((Rect)(ref rect)).position = ((Rect)(ref rect)).position + Event.current.delta;
					ClampToScreen();
					Event.current.Use();
				}
			}
			_tab = GUILayout.Toolbar(_tab, _tabs, _styles.Button, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (!IsHost)
			{
				GUIStyle val2 = new GUIStyle(_styles.Small);
				val2.normal.textColor = new Color(1f, 0.8f, 0.25f);
				GUIStyle val3 = val2;
				GUILayout.Label(L("HOST_NOT"), val3, Array.Empty<GUILayoutOption>());
			}
			else
			{
				GUILayout.Label(L("HOST_IS"), _styles.Small, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndHorizontal();
			bool flag = _tab != 4;
			if (flag)
			{
				_scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			}
			switch (_tab)
			{
			case 0:
				DrawPlayers();
				break;
			case 1:
				DrawList(instance.PermWhite, instance.TempWhite, ref _whiteWhich, ref _whiteInput, ref _whiteListScroll, isWhite: true);
				break;
			case 2:
				DrawList(instance.PermBlack, instance.TempBlack, ref _blackWhich, ref _blackInput, ref _blackListScroll, isWhite: false);
				break;
			case 3:
				DrawFriends();
				break;
			case 4:
				DrawSettings();
				break;
			}
			if (flag)
			{
				GUILayout.EndScrollView();
			}
			if (!string.IsNullOrEmpty(_toast) && Time.realtimeSinceStartup < _toastUntil)
			{
				GUILayout.Label(_toast, _styles.Small, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndVertical();
		}

		private void ClampToScreen()
		{
			((Rect)(ref _rect)).x = Mathf.Clamp(((Rect)(ref _rect)).x, 0f - ((Rect)(ref _rect)).width + 80f, (float)Screen.width - 80f);
			((Rect)(ref _rect)).y = Mathf.Clamp(((Rect)(ref _rect)).y, 0f, (float)Screen.height - 40f);
		}

		private void DrawPlayers()
		{
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			PluginConfig config = Plugin.Config;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(LF("ONLINE_PLAYERS", CurrentPlayers().Count), _styles.Section, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) });
			GUILayout.FlexibleSpace();
			config.WhitelistEnabled.Value = GUILayout.Toggle(config.WhitelistEnabled.Value, L("WHITELIST_TOGGLE"), _styles.Toggle, Array.Empty<GUILayoutOption>());
			GUILayout.Space(12f);
			config.BlacklistEnabled.Value = GUILayout.Toggle(config.BlacklistEnabled.Value, L("BLACKLIST_TOGGLE"), _styles.Toggle, Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (IsHost && config.WhitelistEnabled.Value && GUILayout.Button(L("ENFORCE_WHITELIST"), _styles.Button, Array.Empty<GUILayoutOption>()))
			{
				EnforceWhitelistOnCurrentPlayers();
			}
			if (IsHost && config.BlacklistEnabled.Value)
			{
				GUILayout.Label(L("AUTO_BLACKLIST_NOTE"), _styles.Small, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			GUI.enabled = IsHost;
			foreach (SessionTracker.PlayerInfo item in CurrentPlayers())
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				string text = NameCache.Resolve(item.SteamId);
				if (!string.IsNullOrEmpty(item.Name) && item.Name != ((object)(NetMsgId)4/*cast due to .constrained prefix*/).ToString())
				{
					text = item.Name;
				}
				GUILayout.Label(text, _styles.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) });
				GUILayout.FlexibleSpace();
				if (GUILayout.Button(L("BTN_KICK"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }))
				{
					KickService.EnqueueForceKickBySteamId(item.SteamId, "manual kick");
					ToastRaw(text + " → " + LF("TOAST_KICKED", text));
				}
				if (GUILayout.Button(L("BTN_WHITE_PERM"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
				{
					AddToList(ListService.Instance.PermWhite, item.SteamId, text, "whitelist.json", permanent: true);
				}
				if (GUILayout.Button(L("BTN_WHITE_TEMP"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
				{
					AddToList(ListService.Instance.TempWhite, item.SteamId, text, null, permanent: false);
				}
				if (GUILayout.Button(L("BTN_KICK_PERMBLACK"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
				{
					Blacklist(item.SteamId, text, permanent: true);
					ToastRaw(text + " → " + LF("TOAST_KICKED_BLACKLISTED", L("LIST_PERM"), text));
				}
				if (GUILayout.Button(L("BTN_KICK_TEMPBLACK"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
				{
					Blacklist(item.SteamId, text, permanent: false);
					ToastRaw(text + " → " + LF("TOAST_KICKED_BLACKLISTED", L("LIST_TEMP"), text));
				}
				ListService instance = ListService.Instance;
				WhitelistSnapshot snapshot = instance.Snapshot;
				bool num = snapshot.PermWhite.Contains(item.SteamId) || snapshot.TempWhite.Contains(item.SteamId);
				bool flag = snapshot.PermBlack.Contains(item.SteamId) || snapshot.TempBlack.Contains(item.SteamId);
				if (num && GUILayout.Button(L("BTN_UNWHITE"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
				{
					RemoveEntry(instance.PermWhite, item.SteamId, text, permanent: true, isWhite: true);
					RemoveEntry(instance.TempWhite, item.SteamId, text, permanent: false, isWhite: true);
				}
				if (flag && GUILayout.Button(L("BTN_UNBLACK"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
				{
					RemoveEntry(instance.PermBlack, item.SteamId, text, permanent: true, isWhite: false);
					RemoveEntry(instance.TempBlack, item.SteamId, text, permanent: false, isWhite: false);
				}
				GUILayout.EndHorizontal();
			}
			GUI.enabled = true;
			if (CurrentPlayers().Count == 0)
			{
				GUILayout.Label(IsHost ? L("NO_PLAYERS") : L("WAITING_LOBBY"), _styles.Small, Array.Empty<GUILayoutOption>());
			}
		}

		private void EnforceWhitelistOnCurrentPlayers()
		{
			ListService instance = ListService.Instance;
			if (instance == null)
			{
				return;
			}
			WhitelistSnapshot snapshot = instance.Snapshot;
			int num = 0;
			foreach (SessionTracker.PlayerInfo item in CurrentPlayers())
			{
				if (!snapshot.PermWhite.Contains(item.SteamId) && !snapshot.TempWhite.Contains(item.SteamId))
				{
					KickService.EnqueueForceKickBySteamId(item.SteamId, "whitelist enforcement");
					num++;
				}
			}
			Toast((num > 0) ? "TOAST_ENFORCED_N" : "TOAST_NO_ENFORCED_W", num);
		}

		private void SweepBlacklist()
		{
			try
			{
				PluginConfig config = Plugin.Config;
				ListService instance = ListService.Instance;
				if (config == null || instance == null || !IsHost || !config.BlacklistEnabled.Value)
				{
					return;
				}
				WhitelistSnapshot snapshot = instance.Snapshot;
				foreach (SessionTracker.PlayerInfo item in CurrentPlayers())
				{
					if (snapshot.PermBlack.Contains(item.SteamId) || snapshot.TempBlack.Contains(item.SteamId))
					{
						KickService.EnqueueBanKickBySteamId(item.SteamId, "auto blacklist sweep");
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[AutoBlacklist] sweep failed: " + ex.Message));
			}
		}

		private void DrawList(NameList perm, NameList temp, ref int which, ref string input, ref Vector2 listScroll, bool isWhite)
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			which = GUILayout.Toolbar(which, new string[2]
			{
				L("LIST_PERM"),
				L("LIST_TEMP")
			}, _styles.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) });
			GUILayout.Label(LF("LIST_COUNT", isWhite ? L("TAB_WHITELIST") : L("TAB_BLACKLIST"), perm.Count, temp.Count), _styles.Small, Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			NameList nameList = ((which == 0) ? perm : temp);
			listScroll = GUILayout.BeginScrollView(listScroll, _styles.ListBox, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(220f) });
			if (nameList.Count == 0)
			{
				GUILayout.Space(8f);
				GUILayout.Label(L("LIST_EMPTY"), _styles.Small, Array.Empty<GUILayoutOption>());
			}
			IList<ListEntry> entries = nameList.Entries;
			for (int i = 0; i < entries.Count; i++)
			{
				ListEntry listEntry = entries[i];
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				string text = NameCache.Resolve(listEntry.SteamId);
				if (text != listEntry.Name && !string.IsNullOrEmpty(listEntry.Name) && listEntry.Name != L("UNKNOWN_PLAYER") && listEntry.Name != L("PENDING_IDENTIFY"))
				{
					text = listEntry.Name;
				}
				GUILayout.Label(text, _styles.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) });
				GUILayout.Label(listEntry.AddedAt, _styles.Small, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) });
				GUILayout.FlexibleSpace();
				if (GUILayout.Button(L("BTN_DELETE"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) }))
				{
					RemoveEntry(nameList, listEntry.SteamId, listEntry.Name, which == 0, isWhite);
				}
				GUILayout.EndHorizontal();
			}
			GUILayout.EndScrollView();
			GUILayout.Space(8f);
			GUILayout.Label(L("MANUAL_ADD"), _styles.Section, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			input = GUILayout.TextField(input, _styles.TextField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
			string text2 = input.Trim();
			GUI.enabled = !string.IsNullOrEmpty(text2);
			if (GUILayout.Button(L("MANUAL_ADD_PERM"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }))
			{
				ResolveAndAdd(perm, text2, isWhite ? "whitelist.json" : "blacklist.json", permanent: true);
				input = "";
			}
			if (GUILayout.Button(L("MANUAL_ADD_TEMP"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }))
			{
				ResolveAndAdd(temp, text2, null, permanent: false);
				input = "";
			}
			GUI.enabled = true;
			GUILayout.EndHorizontal();
		}

		private void DrawFriends()
		{
			GUILayout.Label(L("FRIENDS_TITLE"), _styles.Section, Array.Empty<GUILayoutOption>());
			GUILayout.Label(L("FRIENDS_DESC"), _styles.Small, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(L("FRIENDS_SEARCH"), _styles.Small, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(110f),
				GUILayout.Height(24f)
			});
			_friendQuery = GUILayout.TextField(_friendQuery, _styles.TextField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) });
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			if (_friendCache == null || Time.realtimeSinceStartup >= _friendCacheUntil)
			{
				_friendCache = GetSteamFriends();
				_friendCache.Sort((FriendEntry a, FriendEntry b) => string.Compare(a.Name, b.Name, StringComparison.CurrentCulture));
				_friendCacheUntil = Time.realtimeSinceStartup + 5f;
			}
			string text = _friendQuery.Trim();
			int num = 0;
			foreach (FriendEntry item in _friendCache)
			{
				if (text.Length <= 0 || item.Name.StartsWith(text, StringComparison.OrdinalIgnoreCase))
				{
					num++;
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label(item.Name, _styles.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) });
					GUILayout.FlexibleSpace();
					if (GUILayout.Button(L("BTN_WHITE_PERM"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
					{
						AddToList(ListService.Instance.PermWhite, item.SteamId, item.Name, "whitelist.json", permanent: true);
						NameCache.Record(item.SteamId, item.Name);
					}
					if (GUILayout.Button(L("BTN_WHITE_TEMP"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
					{
						AddToList(ListService.Instance.TempWhite, item.SteamId, item.Name, null, permanent: false);
						NameCache.Record(item.SteamId, item.Name);
					}
					if (GUILayout.Button(L("BTN_BLACK_PERM"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
					{
						Blacklist(item.SteamId, item.Name, permanent: true);
						NameCache.Record(item.SteamId, item.Name);
					}
					if (GUILayout.Button(L("BTN_BLACK_TEMP"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }))
					{
						Blacklist(item.SteamId, item.Name, permanent: false);
						NameCache.Record(item.SteamId, item.Name);
					}
					GUILayout.EndHorizontal();
				}
			}
			if (_friendCache.Count == 0)
			{
				GUILayout.Label(L("NO_FRIENDS"), _styles.Small, Array.Empty<GUILayoutOption>());
			}
			else if (num == 0)
			{
				GUILayout.Label(L("LIST_EMPTY"), _styles.Small, Array.Empty<GUILayoutOption>());
			}
		}

		private unsafe static List<FriendEntry> GetSteamFriends()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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)
			List<FriendEntry> list = new List<FriendEntry>();
			try
			{
				int friendCount = SteamFriends.GetFriendCount((EFriendFlags)65535);
				for (int i = 0; i < friendCount; i++)
				{
					CSteamID friendByIndex = SteamFriends.GetFriendByIndex(i, (EFriendFlags)65535);
					string friendPersonaName = SteamFriends.GetFriendPersonaName(friendByIndex);
					if (!string.IsNullOrEmpty(friendPersonaName))
					{
						list.Add(new FriendEntry
						{
							Name = friendPersonaName,
							SteamId = ((object)(*(CSteamID*)(&friendByIndex))/*cast due to .constrained prefix*/).ToString()
						});
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[I18n] Friends fail: " + ex.Message));
			}
			return list;
		}

		private void DrawSettings()
		{
			//IL_0121: 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_0147: Expected O, but got Unknown
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Expected O, but got Unknown
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label(L("SETTINGS_HOTKEY"), _styles.Section, Array.Empty<GUILayoutOption>());
			GUILayout.Label(L("SETTINGS_HOTKEY_DESC"), _styles.Small, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			_hotkeyInput = GUILayout.TextField(_hotkeyInput, _styles.TextField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
			Hotkey hotkey = new Hotkey(_hotkeyInput);
			if (GUILayout.Button(L("SETTINGS_SAVE"), _styles.MiniButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }))
			{
				if (hotkey.Valid)
				{
					Plugin.Config.ToggleWindowHotkey.Value = hotkey.ToString();
					_hotkey = hotkey;
					_hotkeyError = null;
					Toast("SETTINGS_SAVED", hotkey.ToString());
				}
				else
				{
					_hotkeyError = L("SETTINGS_INVALID_KEY");
				}
			}
			GUILayout.EndHorizontal();
			if (_hotkeyError != null)
			{
				GUIStyle val = new GUIStyle(_styles.Small);
				val.normal.textColor = new Color(1f, 0.45f, 0.4f);
				GUIStyle val2 = val;
				GUILayout.Label(_hotkeyError, val2, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(10f);
			GUILayout.Label(L("SETTINGS_LANG"), _styles.Section, Array.Empty<GUILayoutOption>());
			LocalizationService instance = LocalizationService.Instance;
			string[] availableCodes = instance.AvailableCodes;
			int num = Array.IndexOf(availableCodes, instance.CurrentCode);
			if (num < 0)
			{
				num = 0;
			}
			string text = instance.DisplayNameOf(availableCodes[num]) + "  ▾";
			Rect rect = GUILayoutUtility.GetRect(new GUIContent(text), _styles.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
			if (GUI.Button(rect, text, _styles.Button))
			{
				_langOpen = !_langOpen;
			}
			if (_langOpen)
			{
				_langPopupRect = new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax + 2f, 240f, 4f + 24f * (float)availableCodes.Length);
			}
			GUILayout.Space(10f);
			GUILayout.Label(L("SETTINGS_ABOUT"), _styles.Section, Array.Empty<GUILayoutOption>());
			GUILayout.Label(L("SETTINGS_ABOUT_DESC"), _styles.Small, Array.Empty<GUILayoutOption>());
			GUILayout.Label(L("SETTINGS_FILES"), _styles.Path, Array.Empty<GUILayoutOption>());
			GUILayout.Label("  " + DisplayPath(ListStore.FilePath("whitelist.json")), _styles.Path, Array.Empty<GUILayoutOption>());
			GUILayout.Label("  " + DisplayPath(ListStore.FilePath("blacklist.json")), _styles.Path, Array.Empty<GUILayoutOption>());
		}

		private void DrawLangPopup()
		{
			//IL_001b: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			LocalizationService instance = LocalizationService.Instance;
			string[] availableCodes = instance.AvailableCodes;
			int num = Array.IndexOf(availableCodes, instance.CurrentCode);
			GUI.Box(_langPopupRect, GUIContent.none, _styles.Box);
			for (int i = 0; i < availableCodes.Length; i++)
			{
				Rect val = new Rect(((Rect)(ref _langPopupRect)).x + 2f, ((Rect)(ref _langPopupRect)).y + 2f + 24f * (float)i, ((Rect)(ref _langPopupRect)).width - 4f, 22f);
				string text = ((i == num) ? "✓ " : "") + instance.DisplayNameOf(availableCodes[i]);
				if (GUI.Button(val, text, _styles.MiniButton) && i != num)
				{
					SetLanguage(availableCodes[i]);
				}
			}
			if ((int)Event.current.type == 0 && !((Rect)(ref _langPopupRect)).Contains(Event.current.mousePosition))
			{
				_langOpen = false;
			}
		}

		private void SetLanguage(string code)
		{
			LocalizationService.Instance.SetLanguage(code);
			Plugin.Config.LanguageCode.Value = code;
			_langOpen = false;
			_tabs = new string[5]
			{
				L("TAB_PLAYERS"),
				L("TAB_WHITELIST"),
				L("TAB_BLACKLIST"),
				L("TAB_FRIENDS"),
				L("TAB_SETTINGS")
			};
		}

		private static string DisplayPath(string absolute)
		{
			try
			{
				string text = Paths.GameRootPath;
				if (!string.IsNullOrEmpty(text))
				{
					string text2 = text;
					char directorySeparatorChar = Path.DirectorySeparatorChar;
					if (!text2.EndsWith(directorySeparatorChar.ToString(), StringComparison.Ordinal))
					{
						string text3 = text;
						directorySeparatorChar = Path.AltDirectorySeparatorChar;
						if (!text3.EndsWith(directorySeparatorChar.ToString(), StringComparison.Ordinal))
						{
							string text4 = text;
							directorySeparatorChar = Path.DirectorySeparatorChar;
							text = text4 + directorySeparatorChar;
						}
					}
					if (absolute.StartsWith(text, StringComparison.OrdinalIgnoreCase))
					{
						return absolute.Substring(text.Length);
					}
				}
			}
			catch
			{
			}
			return absolute;
		}

		private void RemoveEntry(NameList list, string steamId, string name, bool permanent, bool isWhite)
		{
			string text = NameCache.Resolve(steamId);
			if (!string.IsNullOrEmpty(name) && name != L("UNKNOWN_PLAYER") && name != L("PENDING_IDENTIFY"))
			{
				text = name;
			}
			if (list.Remove(steamId))
			{
				if (permanent)
				{
					ListStore.Save(isWhite ? "whitelist.json" : "blacklist.json", list);
				}
				ListService.Instance.RebuildSnapshot();
				if (!isWhite)
				{
					KickService.UnbanReconnect(steamId);
					ListService.Instance.SyncKickedUsers(Plugin.Config.BlacklistEnabled.Value);
				}
				Toast("TOAST_DELETED", text);
			}
		}

		private static bool ResolveInput(string input, out string steamId, out string displayName)
		{
			steamId = null;
			displayName = null;
			if (!ulong.TryParse(input.Trim(), out var result))
			{
				return false;
			}
			steamId = input.Trim();
			displayName = NameCache.Resolve(steamId);
			if (displayName.StartsWith("…") || ulong.TryParse(displayName, out result))
			{
				displayName = L("PENDING_IDENTIFY");
			}
			return true;
		}

		private void ResolveAndAdd(NameList list, string input, string saveFile, bool permanent)
		{
			if (!ResolveInput(input, out var steamId, out var displayName))
			{
				Toast("INVALID_STEAMID");
			}
			else if (list.Add(ListEntry.Create(steamId, displayName)))
			{
				if (saveFile != null)
				{
					ListStore.Save(saveFile, list);
				}
				ListService.Instance.RebuildSnapshot();
				Toast("TOAST_ADDED", permanent ? L("LIST_PERM") : L("LIST_TEMP"), displayName);
			}
			else
			{
				Toast("ALREADY_IN_LIST");
			}
		}

		private void AddToList(NameList list, string steamId, string name, string saveFile, bool permanent)
		{
			if (list.Add(ListEntry.Create(steamId, name)))
			{
				if (saveFile != null)
				{
					ListStore.Save(saveFile, list);
				}
				ListService.Instance.RebuildSnapshot();
				Toast("TOAST_ADDED", permanent ? L("LIST_PERM") : L("LIST_TEMP"), name);
			}
			else
			{
				Toast("ALREADY_IN_LIST");
			}
		}

		private void Blacklist(string steamId, string name, bool permanent)
		{
			ListService instance = ListService.Instance;
			if ((permanent ? instance.PermBlack : instance.TempBlack).Add(ListEntry.Create(steamId, name)))
			{
				if (permanent)
				{
					instance.SaveBlacklist();
				}
				ListService.Instance.RebuildSnapshot();
				KickService.BanReconnect(steamId);
				Toast("TOAST_BLACKLISTED", permanent ? L("LIST_PERM") : L("LIST_TEMP"), name);
			}
			else
			{
				Toast("ALREADY_IN_LIST");
			}
			KickService.EnqueueBanKickBySteamId(steamId, "blacklisted");
		}

		private static List<SessionTracker.PlayerInfo> CurrentPlayers()
		{
			if (_playerCache == null)
			{
				_playerCache = ((SessionTracker.Instance != null) ? SessionTracker.Instance.Snapshot() : new List<SessionTracker.PlayerInfo>());
			}
			return _playerCache;
		}

		private void LateUpdate()
		{
			_playerCache = null;
		}

		private void OnApplicationQuit()
		{
			NameCache.Save();
		}
	}
}
namespace MyAwesomeWhitelist.Patches
{
	[HarmonyPatch]
	internal static class NetGamePatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(NetGame), "HostGame")]
		private static void HostGame_Prefix()
		{
			if (ListService.Instance != null)
			{
				ListService.Instance.ResetSession();
			}
			if (SessionTracker.Instance != null)
			{
				SessionTracker.Instance.Clear();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(NetGame), "OnServerConnect")]
		private static bool OnServerConnect_Prefix(NetHost client)
		{
			ListService instance = ListService.Instance;
			PluginConfig config = Plugin.Config;
			if (instance == null || config == null || client == null || !NetGame.isServer)
			{
				return true;
			}
			string text = SessionTracker.SteamIdOf(client.connection);
			if (string.IsNullOrEmpty(text))
			{
				return true;
			}
			object obj;
			switch (instance.CheckJoin(text, config.WhitelistEnabled.Value, config.BlacklistEnabled.Value, instance.Snapshot))
			{
			case JoinDecision.Allow:
				return true;
			default:
				obj = "not whitelisted";
				break;
			case JoinDecision.DenyBlacklisted:
				obj = "blacklisted";
				break;
			}
			string text2 = (string)obj;
			Plugin.Logger.LogInfo((object)("Refusing connection from " + text + ": " + text2));
			KickService.EnqueueForceKick(client, text2);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(NetGame), "OnRequestAddPlayerServer")]
		private static bool OnRequestAddPlayerServer_Prefix(NetHost client)
		{
			ListService instance = ListService.Instance;
			PluginConfig config = Plugin.Config;
			if (instance == null || config == null || client == null || !NetGame.isServer)
			{
				return true;
			}
			string text = SessionTracker.SteamIdOf(client.connection);
			if (string.IsNullOrEmpty(text))
			{
				return true;
			}
			JoinDecision joinDecision = instance.CheckJoin(text, config.WhitelistEnabled.Value, config.BlacklistEnabled.Value, instance.Snapshot);
			object obj;
			switch (joinDecision)
			{
			case JoinDecision.Allow:
				return true;
			default:
				obj = "not whitelisted (late)";
				break;
			case JoinDecision.DenyBlacklisted:
				obj = "blacklisted (late)";
				break;
			}
			string text2 = (string)obj;
			Plugin.Logger.LogInfo((object)("Blocking player spawn for " + text + ": " + text2));
			if (joinDecision == JoinDecision.DenyBlacklisted)
			{
				KickService.EnqueueBanKickBySteamId(text, text2);
			}
			else
			{
				KickService.EnqueueForceKick(client, text2);
			}
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(NetGame), "OnClientHelo")]
		private static void OnClientHelo_Postfix(NetHost client)
		{
			if (client != null && NetGame.isServer)
			{
				string steamId = SessionTracker.SteamIdOf(client.connection);
				SessionTracker.Instance?.Remember(steamId, client.name, client.hostId);
				NameCache.Record(steamId, client.name);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(NetGame), "OnDisconnect")]
		private static void OnDisconnect_Postfix(object connection)
		{
			if (NetGame.isServer)
			{
				SessionTracker.Instance?.Forget(SessionTracker.SteamIdOf(connection));
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(NetGame), "Awake")]
		private static void NetGame_Awake_Postfix()
		{
			PluginConfig config = Plugin.Config;
			ListService.Instance?.SyncKickedUsers(config?.BlacklistEnabled.Value ?? true);
		}

		public static void OnBlacklistToggleChanged(object sender, EventArgs _)
		{
			PluginConfig config = Plugin.Config;
			if (config != null && ListService.Instance != null && NetGame.isServer)
			{
				ListService.Instance.SyncKickedUsers(config.BlacklistEnabled.Value);
			}
		}
	}
	internal static class PatchModule
	{
		public static void Apply()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				new Harmony("MyAwesomeWhitelist").PatchAll(typeof(NetGamePatches));
				Plugin.Logger.LogInfo((object)"Harmony patches applied");
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to apply Harmony patches: {arg}");
			}
		}
	}
}
namespace MyAwesomeWhitelist.Lists
{
	internal sealed class ListEntry
	{
		public readonly string SteamId;

		public readonly string Name;

		public readonly string AddedAt;

		public ListEntry(string steamId, string name, string addedAt)
		{
			SteamId = steamId;
			Name = name ?? string.Empty;
			AddedAt = addedAt ?? string.Empty;
		}

		public static ListEntry Create(string steamId, string name)
		{
			return new ListEntry(steamId, name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
		}

		public override string ToString()
		{
			if (!string.IsNullOrEmpty(Name))
			{
				return Name + " (" + SteamId + ")";
			}
			return SteamId;
		}
	}
	internal static class ListStore
	{
		private static string Dir => Path.Combine(Paths.ConfigPath, "MyAwesomeWhitelist");

		public static string FilePath(string fileName)
		{
			return Path.Combine(Dir, fileName);
		}

		public static NameList Load(string fileName)
		{
			NameList nameList = new NameList();
			try
			{
				string path = FilePath(fileName);
				if (!File.Exists(path))
				{
					return nameList;
				}
				foreach (ListEntry item in Parse(File.ReadAllText(path, Encoding.UTF8)))
				{
					nameList.Add(item);
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("Failed to read " + fileName + ": " + ex.Message + " — starting from an empty list"));
				try
				{
					string text = FilePath(fileName);
					if (File.Exists(text))
					{
						File.Copy(text, text + ".bak", overwrite: true);
					}
				}
				catch
				{
				}
			}
			return nameList;
		}

		public static void Save(string fileName, NameList list)
		{
			try
			{
				Directory.CreateDirectory(Dir);
				string text = FilePath(fileName);
				string text2 = text + ".tmp";
				File.WriteAllText(text2, Serialize(list), Encoding.UTF8);
				if (File.Exists(text))
				{
					File.Delete(text);
				}
				File.Move(text2, text);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Failed to save " + fileName + ": " + ex.Message));
			}
		}

		private static string Serialize(NameList list)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('[');
			IList<ListEntry> entries = list.Entries;
			for (int i = 0; i < entries.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append("{\"id\":");
				stringBuilder.Append(Quote(entries[i].SteamId));
				stringBuilder.Append(",\"name\":");
				stringBuilder.Append(Quote(entries[i].Name));
				stringBuilder.Append(",\"at\":");
				stringBuilder.Append(Quote(entries[i].AddedAt));
				stringBuilder.Append('}');
			}
			stringBuilder.Append(']');
			return stringBuilder.ToString();
		}

		private static string Quote(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 2);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				if (c == '"' || c == '\\')
				{
					stringBuilder.Append('\\').Append(c);
				}
				else if (c == '\n')
				{
					stringBuilder.Append("\\n");
				}
				else if (c == '\r')
				{
					stringBuilder.Append("\\r");
				}
				else if (c == '\t')
				{
					stringBuilder.Append("\\t");
				}
				else if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}

		private static IEnumerable<ListEntry> Parse(string json)
		{
			int i = SkipWs(json, 0);
			if (i >= json.Length || json[i] != '[')
			{
				throw new FormatException("expected '[' at start");
			}
			i = SkipWs(json, i + 1);
			while (i < json.Length && json[i] != ']')
			{
				string text = null;
				string name = null;
				string addedAt = null;
				i = SkipWs(json, i);
				if (json[i] != '{')
				{
					throw new FormatException("expected '{'");
				}
				i = SkipWs(json, i + 1);
				while (i < json.Length && json[i] != '}')
				{
					string text2 = ReadString(json, ref i);
					i = SkipWs(json, i);
					if (json[i] != ':')
					{
						throw new FormatException("expected ':'");
					}
					i = SkipWs(json, i + 1);
					string text3 = ReadString(json, ref i);
					switch (text2)
					{
					case "id":
						text = text3;
						break;
					case "name":
						name = text3;
						break;
					case "at":
						addedAt = text3;
						break;
					}
					i = SkipWs(json, i);
					if (i < json.Length && json[i] == ',')
					{
						i = SkipWs(json, i + 1);
					}
				}
				if (text == null)
				{
					throw new FormatException("entry missing \"id\"");
				}
				yield return new ListEntry(text, name, addedAt);
				i = SkipWs(json, i + 1);
				if (i < json.Length && json[i] == ',')
				{
					i = SkipWs(json, i + 1);
				}
			}
		}

		private static int SkipWs(string s, int i)
		{
			while (i < s.Length && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r'))
			{
				i++;
			}
			if (i >= s.Length)
			{
				throw new FormatException("unexpected end of file");
			}
			return i;
		}

		private static string ReadString(string s, ref int i)
		{
			if (s[i] != '"')
			{
				throw new FormatException("expected '\"' at " + i);
			}
			i++;
			StringBuilder stringBuilder = new StringBuilder();
			while (i < s.Length && s[i] != '"')
			{
				char c = s[i];
				if (c == '\\' && i + 1 < s.Length)
				{
					i++;
					char c2 = s[i];
					switch (c2)
					{
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
						if (i + 4 < s.Length)
						{
							stringBuilder.Append((char)Convert.ToInt32(s.Substring(i + 1, 4), 16));
							i += 4;
						}
						break;
					default:
						stringBuilder.Append(c2);
						break;
					}
				}
				else
				{
					stringBuilder.Append(c);
				}
				i++;
			}
			if (i >= s.Length)
			{
				throw new FormatException("unterminated string");
			}
			i++;
			return stringBuilder.ToString();
		}
	}
	internal sealed class NameList
	{
		private readonly List<ListEntry> _entries = new List<ListEntry>();

		private readonly HashSet<string> _index = new HashSet<string>();

		public IList<ListEntry> Entries => _entries;

		public int Count => _entries.Count;

		public bool Contains(string steamId)
		{
			if (steamId != null)
			{
				return _index.Contains(steamId);
			}
			return false;
		}

		public bool Add(ListEntry entry)
		{
			if (entry == null || Contains(entry.SteamId))
			{
				return false;
			}
			_entries.Add(entry);
			_index.Add(entry.SteamId);
			return true;
		}

		public bool Remove(string steamId)
		{
			if (!Contains(steamId))
			{
				return false;
			}
			for (int i = 0; i < _entries.Count; i++)
			{
				if (_entries[i].SteamId == steamId)
				{
					_entries.RemoveAt(i);
					break;
				}
			}
			_index.Remove(steamId);
			return true;
		}

		public void Clear()
		{
			_entries.Clear();
			_index.Clear();
		}

		public void UpdateName(string steamId, string name)
		{
			if (steamId == null || name == null)
			{
				return;
			}
			for (int i = 0; i < _entries.Count; i++)
			{
				if (_entries[i].SteamId == steamId && _entries[i].Name != name)
				{
					_entries[i] = new ListEntry(steamId, name, _entries[i].AddedAt);
					break;
				}
			}
		}
	}
}
namespace MyAwesomeWhitelist.I18n
{
	internal readonly struct LangEntry
	{
		public readonly string Key;

		public readonly string Value;

		public LangEntry(string key, string value)
		{
			Key = key;
			Value = value;
		}
	}
	internal static class LangFile
	{
		private static readonly Regex KeyPattern = new Regex("^[A-Z][A-Z0-9_]*$");

		public static List<LangEntry> Parse(string filePath, out string displayName)
		{
			displayName = null;
			List<LangEntry> result = new List<LangEntry>();
			if (!File.Exists(filePath))
			{
				return result;
			}
			string[] lines;
			try
			{
				lines = File.ReadAllLines(filePath, Encoding.UTF8);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[I18n] Failed to read " + Path.GetFileName(filePath) + ": " + ex.Message));
				return result;
			}
			return ParseLines(lines, Path.GetFileName(filePath), out displayName);
		}

		public static List<LangEntry> ParseLines(IList<string> lines, string sourceName, out string displayName)
		{
			displayName = null;
			List<LangEntry> list = new List<LangEntry>();
			if (lines == null)
			{
				return list;
			}
			for (int i = 0; i < lines.Count; i++)
			{
				int num = i + 1;
				string text = lines[i].Trim();
				if (text.Length == 0 || text[0] == '#')
				{
					continue;
				}
				int num2 = text.IndexOf(':');
				if (num2 <= 0)
				{
					Plugin.Logger.LogWarning((object)$"[I18n] {sourceName}({num}): expected 'KEY:Translation', skipping.");
					continue;
				}
				string text2 = text.Substring(0, num2).Trim();
				string text3 = Unescape(text.Substring(num2 + 1));
				if (text2 == "__LANG_NAME__")
				{
					displayName = text3;
				}
				else if (!KeyPattern.IsMatch(text2))
				{
					Plugin.Logger.LogWarning((object)$"[I18n] {sourceName}({num}): invalid key '{text2}', skipping.");
				}
				else
				{
					list.Add(new LangEntry(text2, text3));
				}
			}
			return list;
		}

		private static string Unescape(string s)
		{
			if (string.IsNullOrEmpty(s) || s.IndexOf('\\') < 0)
			{
				return s;
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			for (int i = 0; i < s.Length; i++)
			{
				char c = s[i];
				if (c == '\\' && i + 1 < s.Length)
				{
					switch (s[i + 1])
					{
					case 'n':
						stringBuilder.Append('\n');
						i++;
						break;
					case 't':
						stringBuilder.Append('\t');
						i++;
						break;
					case '\\':
						stringBuilder.Append('\\');
						i++;
						break;
					case ':':
						stringBuilder.Append(':');
						i++;
						break;
					default:
						stringBuilder.Append(c);
						break;
					}
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
	internal sealed class Language
	{
		public string Code;

		public string DisplayName;

		public readonly Dictionary<string, string> Map = new Dictionary<string, string>();
	}
	internal sealed class LocalizationService
	{
		private static LocalizationService _instance;

		private readonly Dictionary<string, Language> _languages = new Dictionary<string, Language>();

		private Language _current;

		private Language _english;

		public static LocalizationService Instance => _instance;

		public IEnumerable<Language> Languages
		{
			get
			{
				if (_english != null)
				{
					yield return _english;
				}
				foreach (KeyValuePair<string, Language> language in _languages)
				{
					if (language.Value != _english)
					{
						yield return language.Value;
					}
				}
			}
		}

		public string CurrentCode
		{
			get
			{
				if (_current == null)
				{
					return "en";
				}
				return _current.Code;
			}
		}

		public string[] AvailableCodes
		{
			get
			{
				List<string> list = new List<string>(_languages.Keys);
				list.Sort();
				return list.ToArray();
			}
		}

		private static string LangDir => Path.Combine(Paths.ConfigPath, "MyAwesomeWhitelist", "lang");

		public static void Init()
		{
			_instance = new LocalizationService();
			_instance.Reload();
		}

		public void Reload()
		{
			_languages.Clear();
			_english = null;
			_current = null;
			EnsureDefaultFiles();
			string langDir = LangDir;
			if (!Directory.Exists(langDir))
			{
				return;
			}
			string[] files = Directory.GetFiles(langDir, "*.txt");
			foreach (string text in files)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
				string displayName;
				List<LangEntry> list = LangFile.Parse(text, out displayName);
				Language language = new Language
				{
					Code = fileNameWithoutExtension
				};
				foreach (LangEntry item in list)
				{
					language.Map[item.Key] = item.Value;
				}
				language.DisplayName = displayName ?? fileNameWithoutExtension;
				TopUpMissingKeys(text, fileNameWithoutExtension, language);
				_languages[fileNameWithoutExtension] = language;
				if (fileNameWithoutExtension == "en")
				{
					_english = language;
				}
			}
			if (_english == null && _languages.ContainsKey("zh-cn"))
			{
				GenerateEnglishFrom(_languages["zh-cn"]);
			}
			string value = Plugin.Config.LanguageCode.Value;
			string text2 = DetectSystemLocale();
			string language2 = (_languages.ContainsKey(value) ? value : (_languages.ContainsKey(text2) ? text2 : "en"));
			SetLanguage(language2);
			Plugin.Logger.LogInfo((object)string.Format("[I18n] Loaded {0} language(s): {1} · active: {2}", _languages.Count, string.Join(", ", _languages.Keys), CurrentCode));
		}

		public bool SetLanguage(string code)
		{
			if (code == null || !_languages.ContainsKey(code))
			{
				return false;
			}
			_current = _languages[code];
			return true;
		}

		public string Get(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return string.Empty;
			}
			if (_current != null && _current.Map.TryGetValue(key, out var value) && !string.IsNullOrEmpty(value))
			{
				return value;
			}
			if (_english != null && _current != _english && _english.Map.TryGetValue(key, out var value2) && !string.IsNullOrEmpty(value2))
			{
				return value2;
			}
			return key;
		}

		public string DisplayNameOf(string code)
		{
			if (!_languages.TryGetValue(code, out var value))
			{
				return code;
			}
			return value.DisplayName ?? code;
		}

		private static Dictionary<string, string[]> DefaultLines()
		{
			return new Dictionary<string, string[]>
			{
				{
					"zh-cn",
					ZhCnLines()
				},
				{
					"en",
					EnLines()
				},
				{
					"jp",
					JpLines()
				}
			};
		}

		private static void TopUpMissingKeys(string path, string code, Language lang)
		{
			Dictionary<string, string[]> dictionary;
			try
			{
				dictionary = DefaultLines();
			}
			catch
			{
				return;
			}
			if (!dictionary.TryGetValue(code, out var value))
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (string text in value)
			{
				int num = text.IndexOf(':');
				if (num > 0)
				{
					string text2 = text.Substring(0, num);
					if (!(text2 == "__LANG_NAME__") && !lang.Map.ContainsKey(text2))
					{
						lang.Map[text2] = text.Substring(num + 1);
						list.Add(text);
					}
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			try
			{
				File.AppendAllText(path, Environment.NewLine + "# --- added by upgrade (v0.1.0) ---" + Environment.NewLine + string.Join(Environment.NewLine, list.ToArray()) + Environment.NewLine, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				Plugin.Logger.LogInfo((object)$"[I18n] Added {list.Count} new key(s) to {Path.GetFileName(path)}");
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[I18n] Could not append new keys to " + Path.GetFileName(path) + ": " + ex.Message));
			}
		}

		private void EnsureDefaultFiles()
		{
			Directory.CreateDirectory(LangDir);
			if (!File.Exists(Path.Combine(LangDir, "zh-cn.txt")))
			{
				WriteDefaultZhCn();
			}
			if (!File.Exists(Path.Combine(LangDir, "en.txt")))
			{
				WriteDefaultEn();
			}
			if (!File.Exists(Path.Combine(LangDir, "jp.txt")))
			{
				WriteDefaultJp();
			}
		}

		private static void WriteDefaultZhCn()
		{
			File.WriteAllLines(Path.Combine(LangDir, "zh-cn.txt"), ZhCnLines(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static string[] ZhCnLines()
		{
			return new string[87]
			{
				"# MyAwesomeWhitelist 简体中文翻译", "__LANG_NAME__:简体中文", "", "# === 窗口标题 / Tab 名称 ===", "TAB_PLAYERS:玩家列表", "TAB_WHITELIST:白名单", "TAB_BLACKLIST:黑名单", "TAB_FRIENDS:好友", "TAB_SETTINGS:设置", "",
				"# === 状态行 ===", "HOST_IS:本机是否为主机:是", "HOST_NOT:本机是否为主机:否 — 各项操作仅主机生效", "HOST_MODE:本机是否为主机:是", "NOT_HOST:本机是否为主机:否 — 各项操作仅主机生效", "WHITELIST_TOGGLE:白名单", "BLACKLIST_TOGGLE:黑名单", "", "# === 玩家列表 ===", "ONLINE_PLAYERS:在线玩家({0})",
				"NO_PLAYERS:暂无其他玩家", "WAITING_LOBBY:等待主机会话…", "ENFORCE_WHITELIST:执行白名单踢出(踢出当前房间内所有不在白名单中的玩家)", "ENFORCE_BLACKLIST:执行黑名单踢出(踢出当前房间内所有在黑名单中的玩家)", "ENFORCE_BLACKLIST_NONE:当前房间内没有在黑名单中的玩家", "AUTO_BLACKLIST_NOTE:黑名单为自动踢出:名单内玩家每秒检查并强制踢出,无需手动操作", "", "# === 按钮操作 ===", "BTN_KICK:踢出", "BTN_KICK_TEMPBLACK:踢+临黑",
				"BTN_KICK_PERMBLACK:踢+永黑", "BTN_WHITE_TEMP:加白·临", "BTN_WHITE_PERM:加白·永", "BTN_BLACK_TEMP:拉黑·临", "BTN_BLACK_PERM:拉黑·永", "BTN_DELETE:删除", "BTN_UNWHITE:移出白", "BTN_UNBLACK:移出黑", "BTN_CLOSE:✕", "",
				"# === 名单列表 ===", "LIST_PERM:永久", "LIST_TEMP:临时", "LIST_EMPTY:(空)", "SETTINGS_LANG:语言 / Language", "FILTER_ALL:全部", "FILTER_PERM:永久", "FILTER_TEMP:临时", "LIST_COUNT:{0}名单:永久 {1} · 临时 {2}", "MANUAL_ADD:手动添加(SteamID64)",
				"MANUAL_ADD_PERM:永久", "MANUAL_ADD_TEMP:临时", "INVALID_STEAMID:无效的 SteamID64", "ALREADY_IN_LIST:该玩家已在名单中", "UNKNOWN_PLAYER:(未知)", "PENDING_IDENTIFY:(待识别)", "", "# === 好友 tab ===", "FRIENDS_TITLE:Steam 好友列表", "FRIENDS_DESC:从你的 Steam 好友中一键添加到白名单或黑名单。",
				"FRIENDS_SEARCH:搜索(前缀):", "NO_FRIENDS:未获取到好友列表(Steam 未连接?)", "", "# === 设置 tab ===", "SETTINGS_HOTKEY:开窗热键", "SETTINGS_HOTKEY_DESC:组合键格式:修饰键 + 主键,如 Ctrl+Shift+W、Alt+F6。Ctrl 在 macOS 上匹配 Cmd。保存后立即生效。", "SETTINGS_SAVE:保存", "SETTINGS_INVALID_KEY:无效的热键(修饰键 Ctrl/Shift/Alt + 主键,主键如 W、F6、Home)", "SETTINGS_SAVED:热键已保存: {0}", "",
				"SETTINGS_ABOUT:关于", "SETTINGS_ABOUT_DESC:强制踢出在主机端执行(销毁对象 + Steam P2P 拒绝重连),客户端防踢补丁无法拦截。", "SETTINGS_FILES:名单文件:", "", "# === Toast 提示 ===", "TOAST_KICKED:已踢出 {0}", "TOAST_ADDED:已添加到{0}名单:{1}", "TOAST_DELETED:已删除 {0}", "TOAST_BLACKLISTED:已拉黑({0}):{1}", "TOAST_KICKED_BLACKLISTED:已踢出并{0}拉黑:{1}",
				"TOAST_ENFORCED:已踢出黑名单玩家:{0}", "TOAST_ENFORCED_N:已执行白名单踢出:{0} 人被踢出", "TOAST_NO_ENFORCED:当前房间内没有在黑名单中的玩家", "TOAST_NO_ENFORCED_W:当前房间内没有不在白名单中的玩家", "", "# === 名称解析 ===", "NAME_CACHE_FAIL:Steam 名称查询失败"
			};
		}

		private static void WriteDefaultEn()
		{
			File.WriteAllLines(Path.Combine(LangDir, "en.txt"), EnLines(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static string[] EnLines()
		{
			return new string[78]
			{
				"# MyAwesomeWhitelist English translations", "__LANG_NAME__:English", "", "TAB_PLAYERS:Players", "TAB_WHITELIST:Whitelist", "TAB_BLACKLIST:Blacklist", "TAB_FRIENDS:Friends", "TAB_SETTINGS:Settings", "", "HOST_IS:Is this machine the host: Yes",
				"HOST_NOT:Is this machine the host: No — actions only work for the host", "HOST_MODE:Is this machine the host: Yes", "NOT_HOST:Is this machine the host: No — actions only work for the host", "WHITELIST_TOGGLE:Whitelist", "BLACKLIST_TOGGLE:Blacklist", "", "ONLINE_PLAYERS:Online players ({0})", "NO_PLAYERS:No other players", "WAITING_LOBBY:Waiting for lobby…", "ENFORCE_WHITELIST:Enforce whitelist (kick everyone currently in the lobby who is not whitelisted)",
				"ENFORCE_BLACKLIST:Enforce blacklist (kick all blacklisted players currently in lobby)", "ENFORCE_BLACKLIST_NONE:No blacklisted players in current lobby", "AUTO_BLACKLIST_NOTE:Blacklist is enforced automatically: blacklisted players are checked and force-kicked every second, no button needed", "", "BTN_KICK:Kick", "BTN_KICK_TEMPBLACK:Kick+TempBl", "BTN_KICK_PERMBLACK:Kick+PermBl", "BTN_WHITE_TEMP:Wht·Temp", "BTN_WHITE_PERM:Wht·Perm", "BTN_BLACK_TEMP:Blk·Temp",
				"BTN_BLACK_PERM:Blk·Perm", "BTN_DELETE:Del", "BTN_UNWHITE:Unwhite", "BTN_UNBLACK:Unblack", "BTN_CLOSE:✕", "", "LIST_PERM:Perm", "LIST_TEMP:Temp", "LIST_EMPTY:(empty)", "SETTINGS_LANG:Language / 语言",
				"FILTER_ALL:All", "FILTER_PERM:Permanent", "FILTER_TEMP:Temporary", "LIST_COUNT:{0}: Perm {1} · Temp {2}", "MANUAL_ADD:Add player (SteamID64)", "MANUAL_ADD_PERM:Permanent", "MANUAL_ADD_TEMP:Temporary", "INVALID_STEAMID:Invalid SteamID64", "ALREADY_IN_LIST:Player already in list", "UNKNOWN_PLAYER:(unknown)",
				"PENDING_IDENTIFY:(pending)", "", "FRIENDS_TITLE:Steam Friends", "FRIENDS_DESC:One-click add friends to whitelist or blacklist.", "FRIENDS_SEARCH:Search (prefix):", "NO_FRIENDS:No friends found (Steam offline?)", "", "SETTINGS_HOTKEY:Toggle hotkey", "SETTINGS_HOTKEY_DESC:Format: Modifiers + Key, e.g. Ctrl+Shift+W. Ctrl matches Cmd on macOS.", "SETTINGS_SAVE:Save",
				"SETTINGS_INVALID_KEY:Invalid hotkey spec", "SETTINGS_SAVED:Hotkey saved: {0}", "", "SETTINGS_ABOUT:About", "SETTINGS_ABOUT_DESC:Force-kick runs server-side teardown + P2P reconnect refusal — client anti-kick mods cannot block it.", "SETTINGS_FILES:List files:", "", "TOAST_KICKED:Kicked {0}", "TOAST_ADDED:Added to {0} list: {1}", "TOAST_DELETED:Deleted {0}",
				"TOAST_BLACKLISTED:Blacklisted ({0}): {1}", "TOAST_KICKED_BLACKLISTED:Kicked + {0}blacklisted: {1}", "TOAST_ENFORCED:Kicked blacklisted player: {0}", "TOAST_ENFORCED_N:Whitelist enforced: {0} player(s) kicked", "TOAST_NO_ENFORCED:No blacklisted players in lobby", "TOAST_NO_ENFORCED_W:Everyone in the lobby is already whitelisted", "", "NAME_CACHE_FAIL:Steam name lookup failed"
			};
		}

		private static void WriteDefaultJp()
		{
			File.WriteAllLines(Path.Combine(LangDir, "jp.txt"), JpLines(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static string[] JpLines()
		{
			return new string[78]
			{
				"# MyAwesomeWhitelist 日本語翻訳", "__LANG_NAME__:日本語", "", "TAB_PLAYERS:プレイヤーリスト", "TAB_WHITELIST:ホワイトリスト", "TAB_BLACKLIST:ブラックリスト", "TAB_FRIENDS:フレンド", "TAB_SETTINGS:設定", "", "HOST_IS:このPCがホストか: はい",
				"HOST_NOT:このPCがホストか: いいえ — 操作はホストのみ有効", "HOST_MODE:このPCがホストか: はい", "NOT_HOST:このPCがホストか: いいえ — 操作はホストのみ有効", "WHITELIST_TOGGLE:ホワイトリスト", "BLACKLIST_TOGGLE:ブラックリスト", "", "ONLINE_PLAYERS:オンラインプレイヤー({0})", "NO_PLAYERS:他のプレイヤーはいません", "WAITING_LOBBY:ロビーを待っています…", "ENFORCE_WHITELIST:ホワイトリスト実行(ロビー内のホワイトリスト未登録者をキック)",
				"ENFORCE_BLACKLIST:ブラックリスト実行(現在のロビーにいるブラックリスト登録者をキック)", "ENFORCE_BLACKLIST_NONE:現在のロビーにブラックリスト登録者はいません", "AUTO_BLACKLIST_NOTE:ブラックリストは自動キック:登録者は毎秒チェックされ強制キックされます(ボタン不要)", "", "BTN_KICK:キック", "BTN_KICK_TEMPBLACK:キック+一時B", "BTN_KICK_PERMBLACK:キック+恒久B", "BTN_WHITE_TEMP:白・一時", "BTN_WHITE_PERM:白・恒久", "BTN_BLACK_TEMP:黒・一時",
				"BTN_BLACK_PERM:黒・恒久", "BTN_DELETE:削除", "BTN_UNWHITE:白解除", "BTN_UNBLACK:黒解除", "BTN_CLOSE:✕", "", "LIST_PERM:恒久", "LIST_TEMP:一時", "LIST_EMPTY:(空)", "SETTINGS_LANG:言語 / Language",
				"FILTER_ALL:全て", "FILTER_PERM:恒久", "FILTER_TEMP:一時", "LIST_COUNT:{0}リスト:恒久 {1} · 一時 {2}", "MANUAL_ADD:手動追加(SteamID64)", "MANUAL_ADD_PERM:恒久", "MANUAL_ADD_TEMP:一時", "INVALID_STEAMID:無効な SteamID64", "ALREADY_IN_LIST:既にリストに存在します", "UNKNOWN_PLAYER:(不明)",
				"PENDING_IDENTIFY:(識別待ち)", "", "FRIENDS_TITLE:Steam フレンドリスト", "FRIENDS_DESC:Steam フレンドからホワイトリスト/ブラックリストに一括追加。", "FRIENDS_SEARCH:検索(前方一致):", "NO_FRIENDS:フレンドリスト取得失敗(Steam オフライン?)", "", "SETTINGS_HOTKEY:ウィンドウ表示キー", "SETTINGS_HOTKEY_DESC:書式: 修飾キー + メインキー。例: Ctrl+Shift+W。macOS では Ctrl が Cmd に対応。", "SETTINGS_SAVE:保存",
				"SETTINGS_INVALID_KEY:無効なキー指定", "SETTINGS_SAVED:キーを保存: {0}", "", "SETTINGS_ABOUT:情報", "SETTINGS_ABOUT_DESC:強制キックはサーバー側で実行(オブジェクト破棃 + P2P 再接続拒否)。クライアント側の対策MODは阻止できません。", "SETTINGS_FILES:リストファイル:", "", "TOAST_KICKED:{0} をキックしました", "TOAST_ADDED:{0}リストに追加: {1}", "TOAST_DELETED:{0} を削除",
				"TOAST_BLACKLISTED:ブラックリスト追加({0}): {1}", "TOAST_KICKED_BLACKLISTED:キック + {0}ブラックリスト: {1}", "TOAST_ENFORCED:ブラックリストプレイヤーをキック: {0}", "TOAST_ENFORCED_N:ホワイトリスト実行: {0} 人をキックしました", "TOAST_NO_ENFORCED:ブラックリスト登録者はいません", "TOAST_NO_ENFORCED_W:ロビー内の全員がホワイトリスト登録済みです", "", "NAME_CACHE_FAIL:Steam名前取得失敗"
			};
		}

		private void GenerateEnglishFrom(Language source)
		{
			Language language = new Language
			{
				Code = "en",
				DisplayName = "English"
			};
			foreach (KeyValuePair<string, string> item in source.Map)
			{
				language.Map[item.Key] = item.Key;
			}
			_languages["en"] = language;
			_english = language;
			List<string> list = new List<string> { "# Auto-generated English fallback (edit freely)", "__LANG_NAME__:English", "" };
			foreach (KeyValuePair<string, string> item2 in language.Map)
			{
				list.Add(item2.Key + ":" + item2.Value);
			}
			list.Sort();
			File.WriteAllLines(Path.Combine(LangDir, "en.txt"), list, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private string DetectSystemLocale()
		{
			//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)
			try
			{
				string text = ((object)Application.systemLanguage/*cast due to .constrained prefix*/).ToString();
				if (text != null)
				{
					string text2 = text.ToLowerInvariant();
					if (text2.Contains("chinese") || text2.Contains("zh"))
					{
						return "zh-cn";
					}
					if (text2.Contains("japanese") || text2.Contains("jp"))
					{
						return "jp";
					}
					if (text2.Contains("english") || text2.Contains("en"))
					{
						return "en";
					}
				}
			}
			catch
			{
			}
			return "en";
		}
	}
}
namespace MyAwesomeWhitelist.Core
{
	internal static class KickService
	{
		private static readonly ConcurrentQueue<Action> MainThreadQueue = new ConcurrentQueue<Action>();

		public static void RunOnMainThread(Action action)
		{
			if (action != null)
			{
				MainThreadQueue.Enqueue(action);
			}
		}

		public static void DrainMainThreadQueue()
		{
			Action result;
			while (MainThreadQueue.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)$"Main-thread action failed: {arg}");
				}
			}
		}

		public static void EnqueueForceKick(NetHost host, string reason)
		{
			if (host != null)
			{
				string sid = SessionTracker.SteamIdOf(host.connection);
				RunOnMainThread(delegate
				{
					ForceKick(host, sid, reason);
				});
			}
		}

		public static void EnqueueForceKickBySteamId(string steamId, string reason)
		{
			if (string.IsNullOrEmpty(steamId) || (Object)(object)NetGame.instance == (Object)null)
			{
				return;
			}
			RunOnMainThread(delegate
			{
				NetHost val = FindHostBySteamId(steamId);
				if (val != null)
				{
					ForceKick(val, steamId, reason);
				}
				else
				{
					BanReconnect(steamId);
				}
			});
		}

		public static void EnqueueBanKickBySteamId(string steamId, string reason)
		{
			if (string.IsNullOrEmpty(steamId) || NetGame.kickedUsers == null)
			{
				return;
			}
			RunOnMainThread(delegate
			{
				NetHost val = FindHostBySteamId(steamId);
				if (val != null)
				{
					ForceKick(val, steamId, reason, banReconnect: true);
				}
				else
				{
					BanReconnect(steamId);
				}
			});
		}

		public static void BanReconnect(string steamId)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (NetGame.kickedUsers != null && ulong.TryParse(steamId, out var result))
			{
				CSteamID val = default(CSteamID);
				((CSteamID)(ref val))..ctor(result);
				if (!NetGame.kickedUsers.Contains(val))
				{
					NetGame.kickedUsers.Add(val);
				}
			}
		}

		public static void UnbanReconnect(string steamId)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if (NetGame.kickedUsers == null || !ulong.TryParse(steamId, out var result))
			{
				return;
			}
			CSteamID val = default(CSteamID);
			((CSteamID)(ref val))..ctor(result);
			for (int num = NetGame.kickedUsers.Count - 1; num >= 0; num--)
			{
				if (NetGame.kickedUsers[num] is CSteamID && (CSteamID)NetGame.kickedUsers[num] == val)
				{
					NetGame.kickedUsers.RemoveAt(num);
				}
			}
		}

		private static NetHost FindHostBySteamId(string steamId)
		{
			NetGame instance = NetGame.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			List<NetHost> allclients = instance.allclients;
			for (int i = 0; i < allclients.Count; i++)
			{
				if (allclients[i] != null && SessionTracker.SteamIdOf(allclients[i].connection) == steamId)
				{
					return allclients[i];
				}
			}
			return null;
		}

		private static void ForceKick(NetHost host, string steamId, string reason, bool banReconnect = false)
		{
			NetGame instance = NetGame.instance;
			if (!((Object)(object)instance == (Object)null) && host != null)
			{
				Plugin.Logger.LogInfo((object)("Force-kicking " + host.name + " (" + steamId + "): " + (reason ?? "manual")));
				try
				{
					instance.Kick(host);
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogWarning((object)("Kick(" + steamId + ") threw: " + ex.Message));
				}
				try
				{
					instance.OnDisconnect(host.connection, true);
				}
				catch (Exception ex2)
				{
					Plugin.Logger.LogError((object)("OnDisconnect(" + steamId + ") threw: " + ex2.Message));
				}
				if (!banReconnect && steamId != null)
				{
					UnbanReconnect(steamId);
				}
			}
		}
	}
	internal enum JoinDecision
	{
		Allow,
		DenyBlacklisted,
		DenyNotWhitelisted
	}
	internal sealed class ListService
	{
		internal const string WhiteFile = "whitelist.json";

		internal const string BlackFile = "blacklist.json";

		public readonly NameList PermWhite = new NameList();

		public readonly NameList TempWhite = new NameList();

		public readonly NameList PermBlack = new NameList();

		public readonly NameList TempBlack = new NameList();

		private volatile WhitelistSnapshot _snapshot = WhitelistSnapshot.Empty;

		public static ListService Instance { get; private set; }

		public WhitelistSnapshot Snapshot => _snapshot;

		private ListService()
		{
		}

		public static void Init()
		{
			Instance = new ListService();
			Instance.ReloadFromDisk();
		}

		public void ReloadFromDisk()
		{
			CopyInto(ListStore.Load("whitelist.json"), PermWhite);
			CopyInto(ListStore.Load("blacklist.json"), PermBlack);
			RebuildSnapshot();
		}

		public void SaveWhitelist()
		{
			ListStore.Save("whitelist.json", PermWhite);
		}

		public void SaveBlacklist()
		{
			ListStore.Save("blacklist.json", PermBlack);
		}

		public void ResetSession()
		{
			TempWhite.Clear();
			TempBlack.Clear();
			RebuildSnapshot();
			ClearKickedUsers();
			Plugin.Logger.LogInfo((object)"Session lists cleared (new lobby)");
		}

		public void SyncKickedUsers(bool blacklistEnabled = true)
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)NetGame.instance == (Object)null || NetGame.kickedUsers == null)
			{
				return;
			}
			List<object> kickedUsers = NetGame.kickedUsers;
			HashSet<ulong> hashSet = new HashSet<ulong>();
			if (blacklistEnabled)
			{
				IList<ListEntry> entries = PermBlack.Entries;
				for (int i = 0; i < entries.Count; i++)
				{
					if (!ulong.TryParse(entries[i].SteamId, out var result))
					{
						Plugin.Logger.LogWarning((object)("Permanent blacklist entry '" + entries[i].SteamId + "' is not a valid SteamID, skipped"));
					}
					else
					{
						hashSet.Add(result);
					}
				}
			}
			int num = 0;
			int num2 = 0;
			for (int num3 = kickedUsers.Count - 1; num3 >= 0; num3--)
			{
				if (kickedUsers[num3] is CSteamID)
				{
					ulong steamID = ((CSteamID)kickedUsers[num3]).m_SteamID;
					if (!hashSet.Contains(steamID))
					{
						kickedUsers.RemoveAt(num3);
						num2++;
					}
				}
			}
			CSteamID val = default(CSteamID);
			foreach (ulong item in hashSet)
			{
				((CSteamID)(ref val))..ctor(item);
				if (!kickedUsers.Contains(val))
				{
					kickedUsers.Add(val);
					num++;
				}
			}
			if (num > 0 || num2 > 0)
			{
				Plugin.Logger.LogInfo((object)string.Format("NetGame.kickedUsers synced: +{0} / -{1} (blacklist {2})", num, num2, blacklistEnabled ? "on" : "off"));
			}
		}

		public void ClearKickedUsers()
		{
			if (NetGame.kickedUsers == null)
			{
				return;
			}
			List<object> kickedUsers = NetGame.kickedUsers;
			for (int num = kickedUsers.Count - 1; num >= 0; num--)
			{
				if (kickedUsers[num] is CSteamID)
				{
					kickedUsers.RemoveAt(num);
				}
			}
		}

		public JoinDecision CheckJoin(string steamId, bool whitelistEnabled, bool blacklistEnabled, WhitelistSnapshot snapshot)
		{
			if (blacklistEnabled && (snapshot.PermBlack.Contains(steamId) || snapshot.TempBlack.Contains(steamId)))
			{
				return JoinDecision.DenyBlacklisted;
			}
			if (whitelistEnabled && !snapshot.PermWhite.Contains(steamId) && !snapshot.TempWhite.Contains(steamId))
			{
				return JoinDecision.DenyNotWhitelisted;
			}
			return JoinDecision.Allow;
		}

		public void RebuildSnapshot()
		{
			_snapshot = new WhitelistSnapshot(CloneIndex(PermWhite), CloneIndex(TempWhite), CloneIndex(PermBlack), CloneIndex(TempBlack));
		}

		private static HashSet<string> CloneIndex(NameList list)
		{
			HashSet<string> hashSet = new HashSet<string>();
			IList<ListEntry> entries = list.Entries;
			for (int i = 0; i < entries.Count; i++)
			{
				hashSet.Add(entries[i].SteamId);
			}
			return hashSet;
		}

		private static void CopyInto(NameList src, NameList dst)
		{
			dst.Clear();
			IList<ListEntry> entries = src.Entries;
			for (int i = 0; i < entries.Count; i++)
			{
				dst.Add(entries[i]);
			}
		}
	}
	internal sealed class WhitelistSnapshot
	{
		public static readonly WhitelistSnapshot Empty = new WhitelistSnapshot(new HashSet<string>(), new HashSet<string>(), new HashSet<string>(), new HashSet<string>());

		public readonly HashSet<string> PermWhite;

		public readonly HashSet<string> TempWhite;

		public readonly HashSet<string> PermBlack;

		public readonly HashSet<string> TempBlack;

		public WhitelistSnapshot(HashSet<string> permWhite, HashSet<string> tempWhite, HashSet<string> permBlack, HashSet<string> tempBlack)
		{
			PermWhite = permWhite;
			TempWhite = tempWhite;
			PermBlack = permBlack;
			TempBlack = tempBlack;
		}
	}
	internal static class NameCache
	{
		private const string FileName = "names.json";

		private static readonly object _lock = new object();

		private static Dictionary<string, string> _cache = new Dictionary<string, string>();

		private static bool _dirty;

		public static void Init()
		{
			Load();
		}

		public static void Record(string steamId, string name)
		{
			if (string.IsNullOrEmpty(steamId))
			{
				return;
			}
			lock (_lock)
			{
				if (!_cache.TryGetValue(steamId, out var value) || value != name)
				{
					_cache[steamId] = name ?? string.Empty;
					_dirty = true;
				}
			}
		}

		public static string Resolve(string steamId)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(steamId))
			{
				return "(未知)";
			}
			lock (_lock)
			{
				if (_cache.TryGetValue(steamId, out var value) && !string.IsNullOrEmpty(value))
				{
					return value;
				}
			}
			if (!ulong.TryParse(steamId, out var result))
			{
				return steamId;
			}
			try
			{
				string friendPersonaName = SteamFriends.GetFriendPersonaName(new CSteamID(result));
				if (!string.IsNullOrEmpty(friendPersonaName) && !ulong.TryParse(friendPersonaName, out var _))
				{
					Record(steamId, friendPersonaName);
					return friendPersonaName;
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("Steam name lookup failed for " + steamId + ": " + ex.Message));
			}
			if (steamId.Length <= 12)
			{
				return steamId;
			}
			return "…" + steamId.Substring(steamId.Length - 8);
		}

		public static List<string> FindByName(string name)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrEmpty(name))
			{
				return list;
			}
			string text = name.ToLowerInvariant();
			lock (_lock)
			{
				foreach (KeyValuePair<string, string> item in _cache)
				{
					if (!string.IsNullOrEmpty(item.Value) && item.Value.ToLowerInvariant() == text)
					{
						list.Add(item.Key);
					}
				}
				return list;
			}
		}

		public static List<string> FindByNameContains(string name)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrEmpty(name))
			{
				return list;
			}
			string value = name.ToLowerInvariant();
			lock (_lock)
			{
				foreach (KeyValuePair<string, string> item in _cache)
				{
					if (!string.IsNullOrEmpty(item.Value) && item.Value.ToLowerInvariant().Contains(value))
					{
						list.Add(item.Key);
					}
				}
				return list;
			}
		}

		public static void Save()
		{
			lock (_lock)
			{
				if (!_dirty)
				{
					return;
				}
				_dirty = false;
			}
			try
			{
				string text = Path.Combine(Paths.ConfigPath, "MyAwesomeWhitelist");
				Directory.CreateDirectory(text);
				string text2 = Path.Combine(text, "names.json");
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append('{');
				bool flag = true;
				Dictionary<string, string> dictionary;
				lock (_lock)
				{
					dictionary = new Dictionary<string, string>(_cache);
				}
				foreach (KeyValuePair<string, string> item in dictionary)
				{
					if (!flag)
					{
						stringBuilder.Append(',');
					}
					flag = false;
					stringBuilder.Append(JsonQuote(item.Key));
					stringBuilder.Append(':');
					stringBuilder.Append(JsonQuote(item.Value));
				}
				stringBuilder.Append('}');
				string text3 = text2 + ".tmp";
				File.WriteAllText(text3, stringBuilder.ToString(), Encoding.UTF8);
				if (File.Exists(text2))
				{
					File.Delete(text2);
				}
				File.Move(text3, text2);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Failed to save NameCache: " + ex.Message));
			}
		}

		private static void Load()
		{
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "MyAwesomeWhitelist", "names.json");
				if (!File.Exists(path))
				{
					return;
				}
				Dictionary<string, string> dictionary = ParseObject(File.ReadAllText(path, Encoding.UTF8));
				lock (_lock)
				{
					_cache = dictionary ?? new Dictionary<string, string>();
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("Failed to load NameCache: " + ex.Message));
			}
		}

		private static Dictionary<string, string> ParseObject(string json)
		{
			int i = 0;
			int length = json.Length;
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			SkipWs(json, ref i);
			if (i >= length || json[i] != '{')
			{
				return null;
			}
			i++;
			while (true)
			{
				SkipWs(json, ref i);
				if (i >= length || json[i] == '}')
				{
					break;
				}
				if (dictionary.Count > 0)
				{
					if (json[i] != ',')
					{
						break;
					}
					i++;
					SkipWs(json, ref i);
				}
				string key = ReadString(json, ref i);
				SkipWs(json, ref i);
				if (i >= length || json[i] != ':')
				{
					break;
				}
				i++;
				SkipWs(json, ref i);
				string value = ReadString(json, ref i);
				dictionary[key] = value;
				SkipWs(json, ref i);
				if (i < length && json[i] != ',')
				{
				}
			}
			return dictionary;
		}

		private static void SkipWs(string s, ref int i)
		{
			while (i < s.Length && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r'))
			{
				i++;
			}
		}

		private static string ReadString(string s, ref int i)
		{
			if (i >= s.Length || s[i] != '"')
			{
				return "";
			}
			i++;
			StringBuilder stringBuilder = new StringBuilder();
			while (i < s.Length && s[i] != '"')
			{
				char c = s[i];
				if (c == '\\' && i + 1 < s.Length)
				{
					i++;
					c = s[i];
					switch (c)
					{
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					default:
						stringBuilder.Append(c);
						break;
					}
				}
				else
				{
					stringBuilder.Append(c);
				}
				i++;
			}
			if (i < s.Length)
			{
				i++;
			}
			return stringBuilder.ToString();
		}

		private static string JsonQuote(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 2);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				if (c == '"' || c == '\\')
				{
					stringBuilder.Append('\\').Append(c);
				}
				else if (c == '\n')
				{
					stringBuilder.Append("\\n");
				}
				else if (c == '\r')
				{
					stringBuilder.Append("\\r");
				}
				else if (c == '\t')
				{
					stringBuilder.Append("\\t");
				}
				else if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}
	}
	internal sealed class SessionTracker
	{
		public sealed class PlayerInfo
		{
			public string SteamId;

			public string Name;

			public uint HostId;
		}

		private readonly object _lock = new object();

		private readonly Dictionary<string, PlayerInfo> _bySteamId = new Dictionary<string, PlayerInfo>();

		public static SessionTracker Instance { get; private set; }

		private SessionTracker()
		{
		}

		public static void Init()
		{
			Instance = new SessionTracker();
		}

		public void Remember(string steamId, string name, uint hostId)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(steamId))
			{
				return;
			}
			lock (_lock)
			{
				if (!_bySteamId.TryGetValue(steamId, out var value))
				{
					value = new PlayerInfo
					{
						SteamId = steamId
					};
					_bySteamId[steamId] = value;
				}
				value.HostId = hostId;
				if (!string.IsNullOrEmpty(name) && name != ((object)(NetMsgId)4/*cast due to .constrained prefix*/).ToString())
				{
					value.Name = name;
				}
			}
		}

		public void Forget(string steamId)
		{
			if (string.IsNullOrEmpty(steamId))
			{
				return;
			}
			lock (_lock)
			{
				_bySteamId.Remove(steamId);
			}
		}

		public void Clear()
		{
			lock (_lock)
			{
				_bySteamId.Clear();
			}
		}

		public unsafe static string SteamIdOf(object connection)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (connection is CSteamID val)
			{
				return ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
			}
			return connection?.ToString();
		}

		public List<PlayerInfo> Snapshot()
		{
			lock (_lock)
			{
				return new List<PlayerInfo>(_bySteamId.Values);
			}
		}
	}
}
namespace MyAwesomeWhitelist.Config
{
	internal sealed class PluginConfig
	{
		public readonly ConfigEntry<string> ToggleWindowHotkey;

		public readonly ConfigEntry<bool> WhitelistEnabled;

		public readonly ConfigEntry<bool> BlacklistEnabled;

		public readonly ConfigEntry<string> LanguageCode;

		public PluginConfig(ConfigFile cfg)
		{
			ToggleWindowHotkey = cfg.Bind<string>("General", "ToggleWindowHotkey", "Ctrl+Shift+W", "Hotkey to open/close the manager window. Modifiers (Ctrl/Cmd, Shift, Alt) joined with '+', main key last, e.g. Ctrl+Shift+W or Alt+F6.");
			WhitelistEnabled = cfg.Bind<bool>("Whitelist", "Enabled", false, "When enabled (and you are the host), only whitelisted players may join.");
			BlacklistEnabled = cfg.Bind<bool>("Blacklist", "Enabled", true, "When enabled (and you are the host), blacklisted players are refused on join and force-kicked.");
			LanguageCode = cfg.Bind<string>("General", "Language", "auto", "UI language: auto, zh-cn, en, or jp. Auto-detects from system on first launch.");
		}
	}
}