Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Valheim DevCommandsGuard v1.0.2
plugins/Valheim_DevCommandsGuard.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Valheim_DevCommandsGuard")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2+bbb54046015ec05cbbf37cec68f216bd1647573c")] [assembly: AssemblyProduct("Valheim_DevCommandsGuard")] [assembly: AssemblyTitle("Valheim_DevCommandsGuard")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Valheim.DevCommandsGuard { public static class MyPluginInfo { public const string PLUGIN_GUID = "Valheim_DevCommandsGuard"; public const string PLUGIN_NAME = "Valheim_DevCommandsGuard"; public const string PLUGIN_VERSION = "1.0.2"; } } namespace DevCommandsGuard { public enum VisibilityMode { All, Admin, Disabled } [BepInPlugin("com.valheim.devcommandsguard", "Valheim_DevCommandsGuard", "1.0.2")] public class DevCommandsGuardPlugin : BaseUnityPlugin { public const string PluginGUID = "com.valheim.devcommandsguard"; public const string PluginName = "Valheim_DevCommandsGuard"; public const string PluginVersion = "1.0.2"; internal static DevCommandsGuardPlugin Instance = null; internal static ManualLogSource Log = null; internal static ConfigEntry<bool> RejectClientsWithDevCommands = null; internal static ConfigEntry<bool> RejectClientsWithoutPlugin = null; internal static ConfigEntry<string> CustomTagFormat = null; internal static ConfigEntry<string> CustomTagColor = null; internal static ConfigEntry<VisibilityMode> HighlightVisibility = null; internal static ConfigEntry<VisibilityMode> TagVisibility = null; internal static ConfigEntry<bool> GuardConsoleArg = null; internal static ConfigEntry<bool> GuardDevCommands = null; internal static ConfigEntry<bool> GuardGod = null; internal static ConfigEntry<bool> GuardDebugMode = null; internal static ConfigEntry<bool> GuardFly = null; internal static ConfigEntry<bool> GuardSpawn = null; internal static ConfigEntry<bool> GuardTp = null; internal static ConfigEntry<VisibilityMode> ChatLogConsoleArg = null; internal static ConfigEntry<VisibilityMode> ChatLogDevCommands = null; internal static ConfigEntry<VisibilityMode> ChatLogGod = null; internal static ConfigEntry<VisibilityMode> ChatLogDebugMode = null; internal static ConfigEntry<VisibilityMode> ChatLogFly = null; internal static ConfigEntry<VisibilityMode> ChatLogSpawn = null; internal static ConfigEntry<VisibilityMode> ChatLogTp = null; internal const string RpcClientHandshake = "DCG_ClientHandshake"; internal const string RpcServerSyncState = "DCG_ServerSyncState"; internal const string RpcAdminCommand = "DCG_AdminCommand"; internal const string RpcAdminCommandFeedback = "DCG_AdminCommandFeedback"; internal const string RpcClientGuardEvent = "DCG_ClientGuardEvent"; internal const string RpcServerChatLog = "DCG_ServerChatLog"; internal const string ZdoDebugOrGodKey = "DCG_DebugOrGod"; internal const string ZdoIsAdminKey = "DCG_IsAdmin"; internal static readonly HashSet<string> HandshakedPeers = new HashSet<string>(); internal static readonly HashSet<string> RejectedPeers = new HashSet<string>(); internal static readonly Dictionary<string, (bool HasConsoleArg, bool HasDevCommands, bool ConsoleJoinLogged)> PeerClientStates = new Dictionary<string, (bool, bool, bool)>(); internal static readonly HashSet<string> ActivePeerViolations = new HashSet<string>(); internal static bool LocalHasConsoleArg; internal static bool LocalHasUsedActionGuardCommand; internal static bool SynchronizedAdmin; internal static string ActiveTagFormat = "[DEV] {name}"; internal static string ActiveTagColor = "red"; internal static VisibilityMode ActiveHighlightVisibility = VisibilityMode.Admin; internal static VisibilityMode ActiveTagVisibility = VisibilityMode.All; internal static bool ActiveGuardGod = true; internal static bool ActiveGuardDebugMode = true; internal static bool ActiveGuardFly = true; internal static bool ActiveGuardSpawn = true; internal static bool ActiveGuardTp = true; private readonly Harmony _harmony = new Harmony("com.valheim.devcommandsguard"); private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; RejectClientsWithDevCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Server", "RejectClientsWithDevCommands", true, "If true, rejects connections from non-admin clients launched with -console or with devcommands enabled."); RejectClientsWithoutPlugin = ((BaseUnityPlugin)this).Config.Bind<bool>("Server", "RejectClientsWithoutPlugin", true, "If true, rejects connections from non-admin clients that do not have DevCommandsGuard installed."); CustomTagFormat = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "TagFormat", "[DEV] {name}", "Tag format for non-admin players with guarded commands enabled. Use {name} as placeholder for the player's name."); CustomTagColor = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "TagColor", "red", "Color of the tag name (e.g. red, #FF0000, yellow, orange) for non-admin players with guarded commands enabled."); HighlightVisibility = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("Visibility", "HighlightVisibility", VisibilityMode.Admin, "Who can see the 3D red character highlight on non-admin players using guarded commands (All, Admin, Disabled)."); TagVisibility = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("Visibility", "TagVisibility", VisibilityMode.All, "Who can see the red tag on overhead names and in chat for non-admin players using guarded commands (All, Admin, Disabled)."); GuardConsoleArg = ((BaseUnityPlugin)this).Config.Bind<bool>("Guards", "GuardConsoleArg", true, "Track and guard -console launch argument."); GuardDevCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Guards", "GuardDevCommands", true, "Track and guard devcommands toggle."); GuardGod = ((BaseUnityPlugin)this).Config.Bind<bool>("Guards", "GuardGod", true, "Track and guard god mode."); GuardDebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Guards", "GuardDebugMode", true, "Track and guard debugmode."); GuardFly = ((BaseUnityPlugin)this).Config.Bind<bool>("Guards", "GuardFly", true, "Track and guard fly mode."); GuardSpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Guards", "GuardSpawn", true, "Track and guard spawn command."); GuardTp = ((BaseUnityPlugin)this).Config.Bind<bool>("Guards", "GuardTp", true, "Track and guard tp / goto command."); ChatLogConsoleArg = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("ChatLogs", "ChatLogConsoleArg", VisibilityMode.Admin, "Game chat log visibility when a user joins with -console (All, Admin, Disabled)."); ChatLogDevCommands = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("ChatLogs", "ChatLogDevCommands", VisibilityMode.Admin, "Game chat log visibility when a user toggles devcommands (All, Admin, Disabled)."); ChatLogGod = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("ChatLogs", "ChatLogGod", VisibilityMode.Admin, "Game chat log visibility when a user toggles god mode (All, Admin, Disabled)."); ChatLogDebugMode = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("ChatLogs", "ChatLogDebugMode", VisibilityMode.Admin, "Game chat log visibility when a user toggles debugmode (All, Admin, Disabled)."); ChatLogFly = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("ChatLogs", "ChatLogFly", VisibilityMode.Admin, "Game chat log visibility when a user toggles fly mode (All, Admin, Disabled)."); ChatLogSpawn = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("ChatLogs", "ChatLogSpawn", VisibilityMode.Admin, "Game chat log visibility when a user executes spawn (All, Admin, Disabled)."); ChatLogTp = ((BaseUnityPlugin)this).Config.Bind<VisibilityMode>("ChatLogs", "ChatLogTp", VisibilityMode.Admin, "Game chat log visibility when a user executes tp / goto (All, Admin, Disabled)."); SyncLocalFieldsFromConfig(); RejectClientsWithDevCommands.SettingChanged += delegate { EnforceActiveConnections(); }; RejectClientsWithoutPlugin.SettingChanged += delegate { EnforceActiveConnections(); }; CustomTagFormat.SettingChanged += delegate { BroadcastConfigToAllClients(); }; CustomTagColor.SettingChanged += delegate { BroadcastConfigToAllClients(); }; HighlightVisibility.SettingChanged += delegate { BroadcastConfigToAllClients(); }; TagVisibility.SettingChanged += delegate { BroadcastConfigToAllClients(); }; GuardGod.SettingChanged += delegate { BroadcastConfigToAllClients(); }; GuardDebugMode.SettingChanged += delegate { BroadcastConfigToAllClients(); }; GuardFly.SettingChanged += delegate { BroadcastConfigToAllClients(); }; GuardSpawn.SettingChanged += delegate { BroadcastConfigToAllClients(); }; GuardTp.SettingChanged += delegate { BroadcastConfigToAllClients(); }; LocalHasConsoleArg = HasConsoleCommandLineArg(); Log.LogInfo((object)string.Format("{0} {1} loaded. Local -console arg: {2}", "Valheim_DevCommandsGuard", "1.0.2", LocalHasConsoleArg)); _harmony.PatchAll(); } private void OnDestroy() { _harmony.UnpatchSelf(); } internal static void SyncLocalFieldsFromConfig() { ActiveTagFormat = CustomTagFormat.Value; ActiveTagColor = CustomTagColor.Value; ActiveHighlightVisibility = HighlightVisibility.Value; ActiveTagVisibility = TagVisibility.Value; ActiveGuardGod = GuardGod.Value; ActiveGuardDebugMode = GuardDebugMode.Value; ActiveGuardFly = GuardFly.Value; ActiveGuardSpawn = GuardSpawn.Value; ActiveGuardTp = GuardTp.Value; } internal static bool HasConsoleCommandLineArg() { try { return Environment.GetCommandLineArgs().Any((string a) => string.Equals(a, "-console", StringComparison.OrdinalIgnoreCase)); } catch { return false; } } internal static bool IsPeerAdmin(ZNet znet, ZNetPeer peer) { if ((Object)(object)znet == (Object)null || peer?.m_socket == null) { return false; } string hostName = peer.m_socket.GetHostName(); if (znet.m_adminList != null) { return znet.ListContainsId(znet.m_adminList, hostName); } return false; } internal static bool IsLocalAdmin() { if ((Object)(object)ZNet.instance == (Object)null) { return false; } if (ZNet.instance.IsServer()) { return true; } return SynchronizedAdmin; } internal static bool CanLocalSee(VisibilityMode mode) { return mode switch { VisibilityMode.All => true, VisibilityMode.Admin => IsLocalAdmin(), _ => false, }; } internal static string PackSyncSettings() { return $"{(int)HighlightVisibility.Value}|{(int)TagVisibility.Value}|{(GuardGod.Value ? 1 : 0)},{(GuardDebugMode.Value ? 1 : 0)},{(GuardFly.Value ? 1 : 0)},{(GuardSpawn.Value ? 1 : 0)},{(GuardTp.Value ? 1 : 0)}"; } internal static void UnpackSyncSettings(string packed) { if (string.IsNullOrEmpty(packed)) { return; } string[] array = packed.Split(new char[1] { '|' }); if (array.Length >= 3) { if (int.TryParse(array[0], out var result)) { ActiveHighlightVisibility = (VisibilityMode)result; } if (int.TryParse(array[1], out var result2)) { ActiveTagVisibility = (VisibilityMode)result2; } string[] array2 = array[2].Split(new char[1] { ',' }); if (array2.Length >= 5) { ActiveGuardGod = array2[0] == "1"; ActiveGuardDebugMode = array2[1] == "1"; ActiveGuardFly = array2[2] == "1"; ActiveGuardSpawn = array2[3] == "1"; ActiveGuardTp = array2[4] == "1"; } } } internal static void BroadcastConfigToAllClients() { SyncLocalFieldsFromConfig(); if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } string text = PackSyncSettings(); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady()) { bool flag = IsPeerAdmin(ZNet.instance, peer); peer.m_rpc.Invoke("DCG_ServerSyncState", new object[4] { flag, CustomTagFormat.Value, CustomTagColor.Value, text }); } } } internal static bool IsGuardEnabledForEvent(string eventType) { return eventType.ToLowerInvariant() switch { "console" => GuardConsoleArg.Value, "devcommands" => GuardDevCommands.Value, "god" => GuardGod.Value, "debugmode" => GuardDebugMode.Value, "fly" => GuardFly.Value, "spawn" => GuardSpawn.Value, "tp" => GuardTp.Value, _ => true, }; } internal static VisibilityMode GetChatLogVisibilityForEvent(string eventType) { return eventType.ToLowerInvariant() switch { "console" => ChatLogConsoleArg.Value, "devcommands" => ChatLogDevCommands.Value, "god" => ChatLogGod.Value, "debugmode" => ChatLogDebugMode.Value, "fly" => ChatLogFly.Value, "spawn" => ChatLogSpawn.Value, "tp" => ChatLogTp.Value, _ => VisibilityMode.Admin, }; } internal static void RecordAndBroadcastGuardEvent(string playerName, string peerAddress, bool isAdmin, string eventType, string stateOrDetail) { if (!IsGuardEnabledForEvent(eventType)) { return; } string text = (isAdmin ? "Admin" : "User"); string text2 = (string.IsNullOrWhiteSpace(playerName) ? peerAddress : (playerName + " (" + peerAddress + ")")); string text3; switch (eventType.ToLowerInvariant()) { case "console": text3 = "[DCG] " + text + " " + text2 + " joined with -console argument."; break; case "spawn": case "tp": text3 = "[DCG] " + text + " " + text2 + " executed guarded command '" + eventType + "': " + stateOrDetail; break; default: text3 = "[DCG] " + text + " " + text2 + " " + stateOrDetail + " guarded command '" + eventType + "'."; break; } Log.LogWarning((object)text3); Console instance = Console.instance; if (instance != null) { instance.Print(text3); } VisibilityMode chatLogVisibilityForEvent = GetChatLogVisibilityForEvent(eventType); if (chatLogVisibilityForEvent == VisibilityMode.Disabled) { return; } string text4 = "<color=#FF6B6B>" + text3 + "</color>"; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && CanLocalSee(chatLogVisibilityForEvent)) { DisplayChatLogMessage(text4); } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer == null || !peer.IsReady()) { continue; } bool flag = IsPeerAdmin(ZNet.instance, peer); if (chatLogVisibilityForEvent == VisibilityMode.All || (chatLogVisibilityForEvent == VisibilityMode.Admin && flag)) { ZRpc rpc = peer.m_rpc; if (rpc != null) { rpc.Invoke("DCG_ServerChatLog", new object[1] { text4 }); } } } } internal static void DisplayChatLogMessage(string message) { try { if ((Object)(object)Chat.instance != (Object)null) { ((Terminal)Chat.instance).AddString(message); Chat.instance.m_hideTimer = 0f; } } catch (Exception arg) { Log.LogError((object)$"Failed to display chat log message: {arg}"); } } internal static string FormatTaggedPlayerName(string rawName) { if (string.IsNullOrEmpty(rawName) || rawName.StartsWith("<color=", StringComparison.Ordinal)) { return rawName; } string text = (string.IsNullOrEmpty(ActiveTagFormat) ? "{name}" : ActiveTagFormat); string text2 = (string.IsNullOrEmpty(ActiveTagColor) ? "red" : ActiveTagColor); string text3 = (text.Contains("{name}") ? text.Replace("{name}", rawName) : (text + " " + rawName)); return "<color=" + text2 + ">" + text3 + "</color>"; } internal static int EnforceActiveConnections() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return 0; } int num = 0; foreach (ZNetPeer item in ZNet.instance.GetPeers().ToList()) { if (item?.m_socket == null || IsPeerAdmin(ZNet.instance, item)) { continue; } string endPointString = item.m_socket.GetEndPointString(); (bool, bool, bool) value; if (RejectClientsWithoutPlugin.Value && !HandshakedPeers.Contains(endPointString)) { Log.LogWarning((object)("Disconnecting active non-admin peer " + endPointString + ": missing Valheim_DevCommandsGuard plugin.")); ZRpc rpc = item.m_rpc; if (rpc != null) { rpc.Invoke("Error", new object[1] { 3 }); } ZNet.instance.Disconnect(item); num++; } else if (RejectClientsWithDevCommands.Value && PeerClientStates.TryGetValue(endPointString, out value) && ((GuardConsoleArg.Value && value.Item1) || (GuardDevCommands.Value && value.Item2))) { Log.LogWarning((object)$"Disconnecting active non-admin peer {endPointString} (consoleArg={value.Item1}, devCommands={value.Item2})."); RejectedPeers.Add(endPointString); ZRpc rpc2 = item.m_rpc; if (rpc2 != null) { rpc2.Invoke("Error", new object[1] { 5 }); } ZNet.instance.Disconnect(item); num++; } } return num; } internal static string ExecuteAdminConfigCommand(string[] args, out bool changed) { changed = false; if (args.Length == 0 || string.Equals(args[0], "status", StringComparison.OrdinalIgnoreCase)) { return $"[DCG] RejectDev={RejectClientsWithDevCommands.Value}, RequirePlugin={RejectClientsWithoutPlugin.Value}, " + $"Highlight={HighlightVisibility.Value}, Tag={TagVisibility.Value}, TagFormat=\"{CustomTagFormat.Value}\", TagColor=\"{CustomTagColor.Value}\" | " + $"Guards(console={GuardConsoleArg.Value}, devcommands={GuardDevCommands.Value}, god={GuardGod.Value}, debugmode={GuardDebugMode.Value}, fly={GuardFly.Value}, spawn={GuardSpawn.Value}, tp={GuardTp.Value}) | " + $"ChatLogs(console={ChatLogConsoleArg.Value}, devcommands={ChatLogDevCommands.Value}, god={ChatLogGod.Value}, debugmode={ChatLogDebugMode.Value}, fly={ChatLogFly.Value}, spawn={ChatLogSpawn.Value}, tp={ChatLogTp.Value})"; } string text = args[0].ToLowerInvariant(); switch (text) { case "reject": case "rejectdev": case "devcommands": { bool flag2 = ((args.Length > 1) ? ParseBool(args[1], !RejectClientsWithDevCommands.Value) : (!RejectClientsWithDevCommands.Value)); RejectClientsWithDevCommands.Value = flag2; ((BaseUnityPlugin)Instance).Config.Save(); int num3 = (flag2 ? EnforceActiveConnections() : 0); changed = true; return $"[DCG] RejectClientsWithDevCommands set to {flag2} (saved, disconnected {num3} non-admin peer(s))."; } case "rejectmissing": case "requireplugin": { bool flag = ((args.Length > 1) ? ParseBool(args[1], !RejectClientsWithoutPlugin.Value) : (!RejectClientsWithoutPlugin.Value)); RejectClientsWithoutPlugin.Value = flag; ((BaseUnityPlugin)Instance).Config.Save(); int num2 = (flag ? EnforceActiveConnections() : 0); changed = true; return $"[DCG] RejectClientsWithoutPlugin set to {flag} (saved, disconnected {num2} non-admin peer(s))."; } case "highlight": case "highlightvisibility": { if (args.Length < 2 || !TryParseVisibility(args[1], out var mode)) { return "Usage: dcg highlight <all | admin | disabled>"; } HighlightVisibility.Value = mode; ((BaseUnityPlugin)Instance).Config.Save(); BroadcastConfigToAllClients(); changed = true; return $"[DCG] HighlightVisibility set to {mode} (saved)."; } case "tagvisibility": case "tagvis": case "chattag": { if (args.Length < 2 || !TryParseVisibility(args[1], out var mode2)) { return "Usage: dcg tagvis <all | admin | disabled>"; } TagVisibility.Value = mode2; ((BaseUnityPlugin)Instance).Config.Save(); BroadcastConfigToAllClients(); changed = true; return $"[DCG] TagVisibility (overhead & chat tag) set to {mode2} (saved)."; } case "guard": { if (args.Length < 3 || !ParseBoolExact(args[2], out var result2)) { return "Usage: dcg guard <all | console | devcommands | god | debugmode | fly | spawn | tp> <on | off>"; } string text2 = args[1].ToLowerInvariant(); if (!SetGuardConfig(text2, result2)) { return "Unknown guard target. Valid targets: all, console, devcommands, god, debugmode, fly, spawn, tp"; } ((BaseUnityPlugin)Instance).Config.Save(); BroadcastConfigToAllClients(); changed = true; return $"[DCG] Guard '{text2}' set to {result2} (saved)."; } case "log": case "chatlog": { if (args.Length < 3 || !TryParseVisibility(args[2], out var mode4)) { return "Usage: dcg log <all | console | devcommands | god | debugmode | fly | spawn | tp> <all | admin | disabled>"; } string text3 = args[1].ToLowerInvariant(); if (!SetChatLogConfig(text3, mode4)) { return "Unknown log target. Valid targets: all, console, devcommands, god, debugmode, fly, spawn, tp"; } ((BaseUnityPlugin)Instance).Config.Save(); changed = true; return $"[DCG] ChatLog visibility for '{text3}' set to {mode4} (saved)."; } case "tagcolor": case "color": if (args.Length < 2) { return "Usage: dcg tagcolor <color>"; } CustomTagColor.Value = args[1]; ((BaseUnityPlugin)Instance).Config.Save(); BroadcastConfigToAllClients(); changed = true; return "[DCG] TagColor set to \"" + CustomTagColor.Value + "\" (saved)."; case "tag": case "tagformat": { if (args.Length < 2) { return "Usage: dcg tagformat <format with {name}>"; } if (args.Length == 2 && TryParseVisibility(args[1], out var mode3)) { TagVisibility.Value = mode3; ((BaseUnityPlugin)Instance).Config.Save(); BroadcastConfigToAllClients(); changed = true; return $"[DCG] TagVisibility (overhead & chat tag) set to {mode3} (saved)."; } CustomTagFormat.Value = string.Join(" ", args.Skip(1)); ((BaseUnityPlugin)Instance).Config.Save(); BroadcastConfigToAllClients(); changed = true; return "[DCG] TagFormat set to \"" + CustomTagFormat.Value + "\" (saved)."; } default: { if (ParseBoolExact(text, out var result)) { RejectClientsWithDevCommands.Value = result; ((BaseUnityPlugin)Instance).Config.Save(); int num = (result ? EnforceActiveConnections() : 0); changed = true; return $"[DCG] RejectClientsWithDevCommands set to {result} (saved, disconnected {num} non-admin peer(s))."; } return "Usage: dcg [status | reject <on|off> | requireplugin <on|off> | highlight <all|admin|disabled> | tagvis <all|admin|disabled> | guard <cmd|all> <on|off> | log <cmd|all> <all|admin|disabled> | tagcolor <color> | tagformat <format>]"; } } } private static bool SetGuardConfig(string target, bool enabled) { switch (target) { case "all": GuardConsoleArg.Value = enabled; GuardDevCommands.Value = enabled; GuardGod.Value = enabled; GuardDebugMode.Value = enabled; GuardFly.Value = enabled; GuardSpawn.Value = enabled; GuardTp.Value = enabled; return true; case "console": case "-console": GuardConsoleArg.Value = enabled; return true; case "devcommands": GuardDevCommands.Value = enabled; return true; case "god": GuardGod.Value = enabled; return true; case "debug": case "debugmode": GuardDebugMode.Value = enabled; return true; case "fly": GuardFly.Value = enabled; return true; case "spawn": GuardSpawn.Value = enabled; return true; case "tp": case "goto": GuardTp.Value = enabled; return true; default: return false; } } private static bool SetChatLogConfig(string target, VisibilityMode mode) { switch (target) { case "all": ChatLogConsoleArg.Value = mode; ChatLogDevCommands.Value = mode; ChatLogGod.Value = mode; ChatLogDebugMode.Value = mode; ChatLogFly.Value = mode; ChatLogSpawn.Value = mode; ChatLogTp.Value = mode; return true; case "console": case "-console": ChatLogConsoleArg.Value = mode; return true; case "devcommands": ChatLogDevCommands.Value = mode; return true; case "god": ChatLogGod.Value = mode; return true; case "debug": case "debugmode": ChatLogDebugMode.Value = mode; return true; case "fly": ChatLogFly.Value = mode; return true; case "spawn": ChatLogSpawn.Value = mode; return true; case "tp": case "goto": ChatLogTp.Value = mode; return true; default: return false; } } private static bool TryParseVisibility(string input, out VisibilityMode mode) { switch (input.ToLowerInvariant()) { case "all": case "everyone": case "public": case "everybody": mode = VisibilityMode.All; return true; case "admins": case "admin": mode = VisibilityMode.Admin; return true; case "off": case "disabled": case "false": case "disable": case "none": mode = VisibilityMode.Disabled; return true; default: mode = VisibilityMode.Admin; return false; } } private static bool ParseBool(string input, bool fallback) { if (!ParseBoolExact(input, out var result)) { return fallback; } return result; } private static bool ParseBoolExact(string input, out bool result) { switch (input.ToLowerInvariant()) { case "1": case "on": case "enabled": case "yes": case "true": case "enable": result = true; return true; case "0": case "no": case "disable": case "off": case "false": case "disabled": result = false; return true; default: result = false; return false; } } } [HarmonyPatch(typeof(ZNet))] internal static class ZNetPatches { [HarmonyPatch("OnNewConnection")] [HarmonyPostfix] private static void OnNewConnection_Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { peer.m_rpc.Register<bool, bool>("DCG_ClientHandshake", (Action<ZRpc, bool, bool>)delegate(ZRpc rpc, bool hasConsoleArg, bool hasDevCommands) { OnServerReceiveHandshake(__instance, peer, hasConsoleArg, hasDevCommands); }); peer.m_rpc.Register<string>("DCG_AdminCommand", (Action<ZRpc, string>)delegate(ZRpc rpc, string commandLine) { OnServerReceiveAdminCommand(__instance, peer, commandLine); }); peer.m_rpc.Register<string, string>("DCG_ClientGuardEvent", (Action<ZRpc, string, string>)delegate(ZRpc rpc, string eventType, string detail) { OnServerReceiveGuardEvent(__instance, peer, eventType, detail); }); return; } peer.m_rpc.Register<bool, string, string, string>("DCG_ServerSyncState", (Method<bool, string, string, string>)delegate(ZRpc rpc, bool isAdmin, string tagFormat, string tagColor, string syncSettings) { DevCommandsGuardPlugin.SynchronizedAdmin = isAdmin; DevCommandsGuardPlugin.ActiveTagFormat = (string.IsNullOrEmpty(tagFormat) ? "{name}" : tagFormat); DevCommandsGuardPlugin.ActiveTagColor = (string.IsNullOrEmpty(tagColor) ? "red" : tagColor); DevCommandsGuardPlugin.UnpackSyncSettings(syncSettings); }); peer.m_rpc.Register<string>("DCG_AdminCommandFeedback", (Action<ZRpc, string>)delegate(ZRpc rpc, string message) { Console instance = Console.instance; if (instance != null) { instance.Print(message); } DevCommandsGuardPlugin.DisplayChatLogMessage(message); }); peer.m_rpc.Register<string>("DCG_ServerChatLog", (Action<ZRpc, string>)delegate(ZRpc rpc, string chatLogMessage) { DevCommandsGuardPlugin.DisplayChatLogMessage(chatLogMessage); }); } [HarmonyPatch("RPC_ClientHandshake")] [HarmonyPostfix] private static void RPC_ClientHandshake_Postfix(ZNet __instance, ZRpc rpc) { if (!__instance.IsServer()) { DevCommandsGuardPlugin.LocalHasUsedActionGuardCommand = false; bool localHasConsoleArg = DevCommandsGuardPlugin.LocalHasConsoleArg; bool flag = (Object)(object)Console.instance != (Object)null && ((Terminal)Console.instance).IsCheatsEnabled(); rpc.Invoke("DCG_ClientHandshake", new object[2] { localHasConsoleArg, flag }); } } private static void OnServerReceiveHandshake(ZNet znet, ZNetPeer peer, bool hasConsoleArg, bool hasDevCommands) { string endPointString = peer.m_socket.GetEndPointString(); DevCommandsGuardPlugin.HandshakedPeers.Add(endPointString); (bool, bool, bool) value; bool flag = DevCommandsGuardPlugin.PeerClientStates.TryGetValue(endPointString, out value) && value.Item3; bool flag2 = DevCommandsGuardPlugin.IsPeerAdmin(znet, peer); if (hasConsoleArg && !flag) { flag = true; DevCommandsGuardPlugin.RecordAndBroadcastGuardEvent(peer.m_playerName, endPointString, flag2, "console", "enabled"); } if (value.Item2 != hasDevCommands && hasDevCommands) { DevCommandsGuardPlugin.RecordAndBroadcastGuardEvent(peer.m_playerName, endPointString, flag2, "devcommands", "enabled"); } DevCommandsGuardPlugin.PeerClientStates[endPointString] = (hasConsoleArg, hasDevCommands, flag); peer.m_rpc.Invoke("DCG_ServerSyncState", new object[4] { flag2, DevCommandsGuardPlugin.CustomTagFormat.Value, DevCommandsGuardPlugin.CustomTagColor.Value, DevCommandsGuardPlugin.PackSyncSettings() }); bool flag3 = DevCommandsGuardPlugin.GuardConsoleArg.Value && hasConsoleArg; bool flag4 = DevCommandsGuardPlugin.GuardDevCommands.Value && hasDevCommands; if (!flag2 && DevCommandsGuardPlugin.RejectClientsWithDevCommands.Value && (flag3 || flag4)) { DevCommandsGuardPlugin.Log.LogWarning((object)$"Rejecting non-admin peer {endPointString} (consoleArg={hasConsoleArg}, devCommands={hasDevCommands})"); DevCommandsGuardPlugin.RejectedPeers.Add(endPointString); peer.m_rpc.Invoke("Error", new object[1] { 5 }); znet.Disconnect(peer); } } private static void OnServerReceiveGuardEvent(ZNet znet, ZNetPeer peer, string eventType, string detail) { if (peer?.m_socket != null) { string endPointString = peer.m_socket.GetEndPointString(); bool flag = DevCommandsGuardPlugin.IsPeerAdmin(znet, peer); if (!flag && (eventType == "spawn" || eventType == "tp") && DevCommandsGuardPlugin.IsGuardEnabledForEvent(eventType)) { DevCommandsGuardPlugin.ActivePeerViolations.Add(endPointString); } DevCommandsGuardPlugin.RecordAndBroadcastGuardEvent(peer.m_playerName, endPointString, flag, eventType, detail); } } private static void OnServerReceiveAdminCommand(ZNet znet, ZNetPeer peer, string commandLine) { if (!DevCommandsGuardPlugin.IsPeerAdmin(znet, peer)) { peer.m_rpc.Invoke("DCG_AdminCommandFeedback", new object[1] { "[DCG] Permission denied: you are not a server admin." }); return; } bool changed; string text = DevCommandsGuardPlugin.ExecuteAdminConfigCommand((commandLine ?? string.Empty).Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries), out changed); DevCommandsGuardPlugin.Log.LogInfo((object)("Admin " + peer.m_socket.GetHostName() + " executed dcg: " + text)); peer.m_rpc.Invoke("DCG_AdminCommandFeedback", new object[1] { text }); } [HarmonyPatch("SendPeerInfo")] [HarmonyPrefix] private static bool SendPeerInfo_Prefix(ZNet __instance, ZRpc rpc) { if (!__instance.IsServer()) { return true; } ZNetPeer peer = __instance.GetPeer(rpc); if (peer == null) { return true; } string endPointString = peer.m_socket.GetEndPointString(); bool flag = DevCommandsGuardPlugin.IsPeerAdmin(__instance, peer); if (!flag && DevCommandsGuardPlugin.RejectedPeers.Contains(endPointString)) { return false; } if (!flag && DevCommandsGuardPlugin.RejectClientsWithoutPlugin.Value && !DevCommandsGuardPlugin.HandshakedPeers.Contains(endPointString)) { DevCommandsGuardPlugin.Log.LogWarning((object)("Rejecting non-admin peer " + endPointString + ": missing Valheim_DevCommandsGuard handshake.")); rpc.Invoke("Error", new object[1] { 3 }); __instance.Disconnect(peer); return false; } return true; } [HarmonyPatch("Disconnect")] [HarmonyPostfix] private static void Disconnect_Postfix(ZNetPeer peer) { if (peer?.m_socket != null) { string endPointString = peer.m_socket.GetEndPointString(); DevCommandsGuardPlugin.HandshakedPeers.Remove(endPointString); DevCommandsGuardPlugin.RejectedPeers.Remove(endPointString); DevCommandsGuardPlugin.PeerClientStates.Remove(endPointString); DevCommandsGuardPlugin.ActivePeerViolations.Remove(endPointString); } } } [HarmonyPatch(typeof(Terminal))] internal static class TerminalPatches { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__4_0; internal void <InitTerminal_Postfix>b__4_0(ConsoleEventArgs args) { if (args?.Args == null) { return; } string[] array = args.Args.Skip(1).ToArray(); if ((Object)(object)ZNet.instance == (Object)null) { Terminal context = args.Context; if (context != null) { context.AddString("[DCG] Must be connected to a world/server."); } } else if (ZNet.instance.IsServer()) { bool changed; string text = DevCommandsGuardPlugin.ExecuteAdminConfigCommand(array, out changed); Terminal context2 = args.Context; if (context2 != null) { context2.AddString(text); } } else if (!DevCommandsGuardPlugin.IsLocalAdmin()) { Terminal context3 = args.Context; if (context3 != null) { context3.AddString("[DCG] Permission denied: you are not a server admin."); } } else { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { serverPeer.m_rpc.Invoke("DCG_AdminCommand", new object[1] { string.Join(" ", array) }); } } } } private static bool _prevCheats; private static bool _prevGod; private static bool _prevDebug; private static bool _prevFly; [HarmonyPatch("InitTerminal")] [HarmonyPostfix] private static void InitTerminal_Postfix() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown try { if (Terminal.commands != null && Terminal.commands.ContainsKey("dcg")) { return; } object obj = <>c.<>9__4_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { if (args?.Args != null) { string[] array2 = args.Args.Skip(1).ToArray(); if ((Object)(object)ZNet.instance == (Object)null) { Terminal context = args.Context; if (context != null) { context.AddString("[DCG] Must be connected to a world/server."); } } else if (ZNet.instance.IsServer()) { bool changed; string text2 = DevCommandsGuardPlugin.ExecuteAdminConfigCommand(array2, out changed); Terminal context2 = args.Context; if (context2 != null) { context2.AddString(text2); } } else if (!DevCommandsGuardPlugin.IsLocalAdmin()) { Terminal context3 = args.Context; if (context3 != null) { context3.AddString("[DCG] Permission denied: you are not a server admin."); } } else { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { serverPeer.m_rpc.Invoke("DCG_AdminCommand", new object[1] { string.Join(" ", array2) }); } } } }; <>c.<>9__4_0 = val; obj = (object)val; } ConsoleEvent val2 = (ConsoleEvent)obj; string text = "DevCommandsGuard admin config: dcg [status | reject | requireplugin | highlight | tagvis | guard | log | tagcolor | tagformat]"; ConstructorInfo[] constructors = typeof(ConsoleCommand).GetConstructors(); foreach (ConstructorInfo constructorInfo in constructors) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameters.Length >= 3 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(string) && parameters[2].ParameterType == typeof(ConsoleEvent)) { object[] array = new object[parameters.Length]; array[0] = "dcg"; array[1] = text; array[2] = val2; for (int num2 = 3; num2 < parameters.Length; num2++) { array[num2] = (parameters[num2].HasDefaultValue ? parameters[num2].DefaultValue : (parameters[num2].ParameterType.IsValueType ? Activator.CreateInstance(parameters[num2].ParameterType) : null)); } constructorInfo.Invoke(array); break; } } } catch (Exception arg) { DevCommandsGuardPlugin.Log.LogError((object)$"Failed to register dcg terminal command: {arg}"); } } [HarmonyPatch("TryRunCommand")] [HarmonyPrefix] private static void TryRunCommand_Prefix() { _prevCheats = (Object)(object)Console.instance != (Object)null && ((Terminal)Console.instance).IsCheatsEnabled(); _prevGod = (Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).InGodMode(); _prevDebug = Player.m_debugMode; _prevFly = (Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.InDebugFlyMode(); } [HarmonyPatch("TryRunCommand")] [HarmonyPostfix] private static void TryRunCommand_Postfix(string text) { try { if ((Object)(object)ZNet.instance == (Object)null) { return; } string text2 = (text ?? string.Empty).Trim(); string[] array = text2.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string text3 = ((array.Length != 0) ? array[0].ToLowerInvariant() : string.Empty); if (!ZNet.instance.IsServer() && text3 == "dcg" && (Terminal.commands == null || !Terminal.commands.ContainsKey("dcg"))) { string[] value = array.Skip(1).ToArray(); if (!DevCommandsGuardPlugin.IsLocalAdmin()) { Console instance = Console.instance; if (instance != null) { instance.Print("[DCG] Permission denied: you are not a server admin."); } } else { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { serverPeer.m_rpc.Invoke("DCG_AdminCommand", new object[1] { string.Join(" ", value) }); } } } bool flag = (Object)(object)Console.instance != (Object)null && ((Terminal)Console.instance).IsCheatsEnabled(); bool flag2 = (Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).InGodMode(); bool debugMode = Player.m_debugMode; bool flag3 = (Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.InDebugFlyMode(); if (_prevCheats != flag) { SendOrRecordLocalEvent("devcommands", flag ? "enabled" : "disabled"); } if (_prevGod != flag2) { SendOrRecordLocalEvent("god", flag2 ? "enabled" : "disabled"); } if (_prevDebug != debugMode) { SendOrRecordLocalEvent("debugmode", debugMode ? "enabled" : "disabled"); } if (_prevFly != flag3) { SendOrRecordLocalEvent("fly", flag3 ? "enabled" : "disabled"); } if (flag) { switch (text3) { case "spawn": case "tp": case "goto": { string obj = ((text3 == "goto") ? "tp" : text3); if ((obj == "spawn") ? DevCommandsGuardPlugin.ActiveGuardSpawn : DevCommandsGuardPlugin.ActiveGuardTp) { DevCommandsGuardPlugin.LocalHasUsedActionGuardCommand = true; } SendOrRecordLocalEvent(obj, text2); break; } } } if (!ZNet.instance.IsServer()) { ZNetPeer serverPeer2 = ZNet.instance.GetServerPeer(); if (serverPeer2 != null && flag) { serverPeer2.m_rpc.Invoke("DCG_ClientHandshake", new object[2] { DevCommandsGuardPlugin.LocalHasConsoleArg, true }); } } } catch (Exception arg) { DevCommandsGuardPlugin.Log.LogError((object)$"Error in TryRunCommand_Postfix: {arg}"); } } internal static void SendOrRecordLocalEvent(string eventType, string detail) { if ((Object)(object)ZNet.instance == (Object)null) { return; } if (ZNet.instance.IsServer()) { DevCommandsGuardPlugin.RecordAndBroadcastGuardEvent(((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerName() : "ServerHost", "local", isAdmin: true, eventType, detail); return; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { serverPeer.m_rpc.Invoke("DCG_ClientGuardEvent", new object[2] { eventType, detail }); } } } [HarmonyPatch(typeof(Player), "FixedUpdate")] internal static class PlayerStatePatches { private static readonly Dictionary<long, Color[]> OriginalColors = new Dictionary<long, Color[]>(); private static bool _lastTrackedDebugMode; private static bool _lastTrackedFlyMode; private static bool _lastTrackedGodMode; private static void Postfix(Player __instance) { if ((Object)(object)((Character)__instance).m_nview == (Object)null || !((Character)__instance).m_nview.IsValid()) { return; } ZDO zDO = ((Character)__instance).m_nview.GetZDO(); if (zDO == null) { return; } if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && ((Character)__instance).m_nview.IsOwner()) { bool flag = ((Character)__instance).InGodMode(); bool debugMode = Player.m_debugMode; bool flag2 = __instance.InDebugFlyMode(); if (flag != _lastTrackedGodMode) { _lastTrackedGodMode = flag; } if (debugMode != _lastTrackedDebugMode) { _lastTrackedDebugMode = debugMode; } if (flag2 != _lastTrackedFlyMode) { _lastTrackedFlyMode = flag2; TerminalPatches.SendOrRecordLocalEvent("fly", flag2 ? "enabled" : "disabled"); } bool flag3 = (DevCommandsGuardPlugin.ActiveGuardGod && flag) || (DevCommandsGuardPlugin.ActiveGuardDebugMode && debugMode) || (DevCommandsGuardPlugin.ActiveGuardFly && flag2) || DevCommandsGuardPlugin.LocalHasUsedActionGuardCommand; bool flag4 = DevCommandsGuardPlugin.IsLocalAdmin(); if (zDO.GetBool("DCG_DebugOrGod", false) != flag3) { zDO.Set("DCG_DebugOrGod", flag3); } if (zDO.GetBool("DCG_IsAdmin", false) != flag4) { zDO.Set("DCG_IsAdmin", flag4); } } bool highlight = zDO.GetBool("DCG_DebugOrGod", false) && !zDO.GetBool("DCG_IsAdmin", false) && DevCommandsGuardPlugin.CanLocalSee(DevCommandsGuardPlugin.ActiveHighlightVisibility); ApplyModelHighlight(__instance, ((ZDOID)(ref zDO.m_uid)).ID, highlight); } private static void ApplyModelHighlight(Player player, long id, bool highlight) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) Renderer[] componentsInChildren = ((Component)player).GetComponentsInChildren<Renderer>(); if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } if (highlight) { Renderer[] array; if (!OriginalColors.ContainsKey(id)) { List<Color> list = new List<Color>(); array = componentsInChildren; for (int i = 0; i < array.Length; i++) { Material[] materials = array[i].materials; foreach (Material val in materials) { list.Add(val.HasProperty("_Color") ? val.color : Color.white); } } OriginalColors[id] = list.ToArray(); } array = componentsInChildren; for (int i = 0; i < array.Length; i++) { Material[] materials = array[i].materials; foreach (Material val2 in materials) { if (val2.HasProperty("_Color")) { val2.color = Color.red; } if (val2.HasProperty("_EmissionColor")) { val2.EnableKeyword("_EMISSION"); val2.SetColor("_EmissionColor", Color.red * 0.6f); } } } } else { if (!OriginalColors.TryGetValue(id, out var value)) { return; } int num = 0; Renderer[] array = componentsInChildren; for (int i = 0; i < array.Length; i++) { Material[] materials = array[i].materials; foreach (Material val3 in materials) { if (val3.HasProperty("_Color") && num < value.Length) { val3.color = value[num]; } if (val3.HasProperty("_EmissionColor")) { val3.SetColor("_EmissionColor", Color.black); } num++; } } OriginalColors.Remove(id); } } } [HarmonyPatch(typeof(Player), "GetHoverName")] internal static class PlayerHoverNamePatch { private static void Postfix(Player __instance, ref string __result) { if (!((Object)(object)((Character)__instance).m_nview == (Object)null) && ((Character)__instance).m_nview.IsValid()) { ZDO zDO = ((Character)__instance).m_nview.GetZDO(); if (zDO != null && zDO.GetBool("DCG_DebugOrGod", false) && !zDO.GetBool("DCG_IsAdmin", false) && DevCommandsGuardPlugin.CanLocalSee(DevCommandsGuardPlugin.ActiveTagVisibility)) { __result = DevCommandsGuardPlugin.FormatTaggedPlayerName(__result); } } } } [HarmonyPatch(typeof(Chat), "OnNewChatMessage")] internal static class ChatTagPatch { private static void Prefix(GameObject go, ref UserInfo sender) { try { if (!DevCommandsGuardPlugin.CanLocalSee(DevCommandsGuardPlugin.ActiveTagVisibility) || (Object)(object)go == (Object)null) { return; } Player component = go.GetComponent<Player>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)((Character)component).m_nview == (Object)null) && ((Character)component).m_nview.IsValid()) { ZDO zDO = ((Character)component).m_nview.GetZDO(); if (zDO != null && zDO.GetBool("DCG_DebugOrGod", false) && !zDO.GetBool("DCG_IsAdmin", false) && !string.IsNullOrEmpty(sender.Name)) { sender.Name = DevCommandsGuardPlugin.FormatTaggedPlayerName(sender.Name); } } } catch (Exception arg) { DevCommandsGuardPlugin.Log.LogError((object)$"Error in ChatTagPatch: {arg}"); } } } }