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.");
}
}
}