Decompiled source of Valheim DevCommandsGuard v1.0.2

plugins/Valheim_DevCommandsGuard.dll

Decompiled a week ago
using 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}");
			}
		}
	}
}