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(); } } 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 NamedKeys = new Dictionary(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(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 _friendCache; private string _friendQuery = string.Empty; private GameObject _blocker; private BlockCursorRaycasts _blockerBehaviour; private static List _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(); if (!((Object)(object)val == (Object)null)) { _blocker = new GameObject("MyAwesomeWhitelist.ClickBlocker"); _blocker.transform.SetParent(((Component)val).transform, false); Image obj = _blocker.AddComponent(); ((Graphic)obj).color = new Color(0f, 0f, 0f, 0f); ((Graphic)obj).raycastTarget = true; _blockerBehaviour = _blocker.AddComponent(); 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()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(" MyAwesomeWhitelist v0.1.0", _styles.Header, Array.Empty()); 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()); GUILayout.BeginHorizontal(Array.Empty()); 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()); } else { GUILayout.Label(L("HOST_IS"), _styles.Small, Array.Empty()); } 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()); } 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()); 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()); GUILayout.Space(12f); config.BlacklistEnabled.Value = GUILayout.Toggle(config.BlacklistEnabled.Value, L("BLACKLIST_TOGGLE"), _styles.Toggle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (IsHost && config.WhitelistEnabled.Value && GUILayout.Button(L("ENFORCE_WHITELIST"), _styles.Button, Array.Empty())) { EnforceWhitelistOnCurrentPlayers(); } if (IsHost && config.BlacklistEnabled.Value) { GUILayout.Label(L("AUTO_BLACKLIST_NOTE"), _styles.Small, Array.Empty()); } GUILayout.EndHorizontal(); GUILayout.Space(4f); GUI.enabled = IsHost; foreach (SessionTracker.PlayerInfo item in CurrentPlayers()) { GUILayout.BeginHorizontal(Array.Empty()); 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()); } } 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()); 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()); 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()); } IList entries = nameList.Entries; for (int i = 0; i < entries.Count; i++) { ListEntry listEntry = entries[i]; GUILayout.BeginHorizontal(Array.Empty()); 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()); GUILayout.BeginHorizontal(Array.Empty()); 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()); GUILayout.Label(L("FRIENDS_DESC"), _styles.Small, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); 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()); 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()); } else if (num == 0) { GUILayout.Label(L("LIST_EMPTY"), _styles.Small, Array.Empty()); } } private unsafe static List 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 list = new List(); 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()); GUILayout.Label(L("SETTINGS_HOTKEY_DESC"), _styles.Small, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); _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()); } GUILayout.Space(10f); GUILayout.Label(L("SETTINGS_LANG"), _styles.Section, Array.Empty()); 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()); GUILayout.Label(L("SETTINGS_ABOUT_DESC"), _styles.Small, Array.Empty()); GUILayout.Label(L("SETTINGS_FILES"), _styles.Path, Array.Empty()); GUILayout.Label(" " + DisplayPath(ListStore.FilePath("whitelist.json")), _styles.Path, Array.Empty()); GUILayout.Label(" " + DisplayPath(ListStore.FilePath("blacklist.json")), _styles.Path, Array.Empty()); } 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 CurrentPlayers() { if (_playerCache == null) { _playerCache = ((SessionTracker.Instance != null) ? SessionTracker.Instance.Snapshot() : new List()); } 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 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 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 _entries = new List(); private readonly HashSet _index = new HashSet(); public IList 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 Parse(string filePath, out string displayName) { displayName = null; List result = new List(); 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 ParseLines(IList lines, string sourceName, out string displayName) { displayName = null; List list = new List(); 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 Map = new Dictionary(); } internal sealed class LocalizationService { private static LocalizationService _instance; private readonly Dictionary _languages = new Dictionary(); private Language _current; private Language _english; public static LocalizationService Instance => _instance; public IEnumerable Languages { get { if (_english != null) { yield return _english; } foreach (KeyValuePair 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 list = new List(_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 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 DefaultLines() { return new Dictionary { { "zh-cn", ZhCnLines() }, { "en", EnLines() }, { "jp", JpLines() } }; } private static void TopUpMissingKeys(string path, string code, Language lang) { Dictionary dictionary; try { dictionary = DefaultLines(); } catch { return; } if (!dictionary.TryGetValue(code, out var value)) { return; } List list = new List(); 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 item in source.Map) { language.Map[item.Key] = item.Key; } _languages["en"] = language; _english = language; List list = new List { "# Auto-generated English fallback (edit freely)", "__LANG_NAME__:English", "" }; foreach (KeyValuePair 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 MainThreadQueue = new ConcurrentQueue(); 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 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 kickedUsers = NetGame.kickedUsers; HashSet hashSet = new HashSet(); if (blacklistEnabled) { IList 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 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 CloneIndex(NameList list) { HashSet hashSet = new HashSet(); IList 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 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(), new HashSet(), new HashSet(), new HashSet()); public readonly HashSet PermWhite; public readonly HashSet TempWhite; public readonly HashSet PermBlack; public readonly HashSet TempBlack; public WhitelistSnapshot(HashSet permWhite, HashSet tempWhite, HashSet permBlack, HashSet 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 _cache = new Dictionary(); 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 FindByName(string name) { List list = new List(); if (string.IsNullOrEmpty(name)) { return list; } string text = name.ToLowerInvariant(); lock (_lock) { foreach (KeyValuePair item in _cache) { if (!string.IsNullOrEmpty(item.Value) && item.Value.ToLowerInvariant() == text) { list.Add(item.Key); } } return list; } } public static List FindByNameContains(string name) { List list = new List(); if (string.IsNullOrEmpty(name)) { return list; } string value = name.ToLowerInvariant(); lock (_lock) { foreach (KeyValuePair 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 dictionary; lock (_lock) { dictionary = new Dictionary(_cache); } foreach (KeyValuePair 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 dictionary = ParseObject(File.ReadAllText(path, Encoding.UTF8)); lock (_lock) { _cache = dictionary ?? new Dictionary(); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to load NameCache: " + ex.Message)); } } private static Dictionary ParseObject(string json) { int i = 0; int length = json.Length; Dictionary dictionary = new Dictionary(); 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 _bySteamId = new Dictionary(); 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 Snapshot() { lock (_lock) { return new List(_bySteamId.Values); } } } } namespace MyAwesomeWhitelist.Config { internal sealed class PluginConfig { public readonly ConfigEntry ToggleWindowHotkey; public readonly ConfigEntry WhitelistEnabled; public readonly ConfigEntry BlacklistEnabled; public readonly ConfigEntry LanguageCode; public PluginConfig(ConfigFile cfg) { ToggleWindowHotkey = cfg.Bind("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("Whitelist", "Enabled", false, "When enabled (and you are the host), only whitelisted players may join."); BlacklistEnabled = cfg.Bind("Blacklist", "Enabled", true, "When enabled (and you are the host), blacklisted players are refused on join and force-kicked."); LanguageCode = cfg.Bind("General", "Language", "auto", "UI language: auto, zh-cn, en, or jp. Auto-detects from system on first launch."); } } }