Decompiled source of NarcLab DevTools v0.1.4

BepInEx/plugins/NarcLabDevTools/NarcLabDevTools.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using UnityEngine;

[assembly: AssemblyCompany("NarcLab")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("NarcLab DevTools")]
[assembly: AssemblyDescription("Private server-authorized Valheim administration tools")]
[assembly: AssemblyProduct("NarcLab DevTools")]
[assembly: AssemblyFileVersion("0.1.4.0")]
[assembly: ComVisible(false)]
[assembly: Guid("e320fba1-d90f-4b9c-8ec4-3a3bc074b78c")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.1.4.0")]
namespace NarcLab.Valheim.DevTools;

internal enum CommandTarget
{
	Unsupported,
	ClientLocal,
	ServerWorld,
	ServerSpawn
}
internal enum RequestError
{
	None,
	Disabled,
	InvalidProtocol,
	InvalidRequestId,
	CommandTooLong,
	InvalidCommand,
	CommandNotRegistered,
	NotAuthorized,
	RateLimited,
	DuplicateRequest,
	SpawnSyntax,
	SpawnAmount,
	SpawnLevel,
	SpawnRadius,
	UnsupportedSpawnOption
}
internal enum AuthorizationMode
{
	Invalid,
	DevToolsAdmins,
	ValheimAdmins,
	Either,
	Both
}
internal sealed class AuthorizationDecision
{
	internal bool Allowed;

	internal string Source;

	internal string DenialMessage;
}
internal sealed class AdminListLoadResult
{
	internal HashSet<string> Ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	internal bool Created;

	internal int InvalidCount;

	internal string ErrorType;
}
internal sealed class CommandDescriptor
{
	internal string Name;

	internal bool IsCheat;

	internal bool OnlyAdmin;

	internal bool OnlyServer;

	internal bool RemoteCommand;

	internal bool HideBehindDevCommands;
}
internal sealed class SpawnRequest
{
	internal string Prefab;

	internal int Amount;

	internal int Level;

	internal float Radius;
}
internal static class DevToolsPolicy
{
	internal const int ProtocolVersion = 1;

	internal const int MaxCommandLength = 512;

	internal const int MaxRequestIdLength = 48;

	internal const int MaximumSpawnAmountCeiling = 1000;

	internal static bool ShouldWrap(CommandDescriptor command)
	{
		if (command == null || string.IsNullOrWhiteSpace(command.Name))
		{
			return false;
		}
		string text = command.Name.Trim().ToLowerInvariant();
		if (text == "devcommands" || text == "confirmcheats")
		{
			return false;
		}
		if (!command.IsCheat && !command.OnlyAdmin)
		{
			return command.HideBehindDevCommands;
		}
		return true;
	}

	internal static CommandTarget Classify(CommandDescriptor command)
	{
		if (!ShouldWrap(command))
		{
			return CommandTarget.Unsupported;
		}
		if (string.Equals(command.Name, "spawn", StringComparison.OrdinalIgnoreCase))
		{
			return CommandTarget.ServerSpawn;
		}
		if (command.RemoteCommand)
		{
			return CommandTarget.ServerWorld;
		}
		return CommandTarget.ClientLocal;
	}

	internal static CommandTarget ClassifyRuntimeCommand(CommandDescriptor command, bool isQualifiedResourcesSpawn)
	{
		if (!isQualifiedResourcesSpawn)
		{
			return Classify(command);
		}
		return CommandTarget.ServerSpawn;
	}

	internal static bool IsRegisteredReplacement(object current, object replacement)
	{
		if (replacement != null)
		{
			return object.ReferenceEquals(current, replacement);
		}
		return false;
	}

	internal static RequestError ValidateRequest(int protocolVersion, string requestId, string command, string fullLine)
	{
		if (protocolVersion != 1)
		{
			return RequestError.InvalidProtocol;
		}
		if (!IsSafeRequestId(requestId))
		{
			return RequestError.InvalidRequestId;
		}
		if (string.IsNullOrWhiteSpace(command) || !IsSafeCommandName(command))
		{
			return RequestError.InvalidCommand;
		}
		if (string.IsNullOrWhiteSpace(fullLine) || fullLine.Length > 512 || HasControlCharacters(fullLine))
		{
			return RequestError.CommandTooLong;
		}
		string a = FirstToken(fullLine);
		if (!string.Equals(a, command, StringComparison.OrdinalIgnoreCase))
		{
			return RequestError.InvalidCommand;
		}
		return RequestError.None;
	}

	internal static RequestError ParseSpawn(string fullLine, int maximumAmount, out SpawnRequest request)
	{
		request = null;
		if (string.IsNullOrWhiteSpace(fullLine) || fullLine.Length > 512)
		{
			return RequestError.SpawnSyntax;
		}
		string[] array = fullLine.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length < 2 || array.Length > 5 || !string.Equals(array[0], "spawn", StringComparison.OrdinalIgnoreCase))
		{
			return RequestError.SpawnSyntax;
		}
		string text = array[1].Trim();
		if (!IsSafePrefabName(text) || text.EndsWith("*", StringComparison.Ordinal))
		{
			return RequestError.SpawnSyntax;
		}
		int result = 1;
		int result2 = 1;
		float result3 = 0.5f;
		if (array.Length >= 3 && !int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
		{
			return RequestError.SpawnAmount;
		}
		if (result < 1 || result > maximumAmount)
		{
			return RequestError.SpawnAmount;
		}
		if (array.Length >= 4 && !int.TryParse(array[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
		{
			return RequestError.SpawnLevel;
		}
		if (result2 < 1 || result2 > 9)
		{
			return RequestError.SpawnLevel;
		}
		if (array.Length >= 5 && !float.TryParse(array[4], NumberStyles.Float, CultureInfo.InvariantCulture, out result3))
		{
			return RequestError.SpawnRadius;
		}
		if (result3 < 0f || result3 > 20f)
		{
			return RequestError.SpawnRadius;
		}
		request = new SpawnRequest
		{
			Prefab = text,
			Amount = result,
			Level = result2,
			Radius = result3
		};
		return RequestError.None;
	}

	internal static bool IsPlayersQuery(string fullLine)
	{
		if (string.IsNullOrWhiteSpace(fullLine) || HasControlCharacters(fullLine))
		{
			return false;
		}
		string[] array = fullLine.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length == 1)
		{
			return string.Equals(array[0], "players", StringComparison.OrdinalIgnoreCase);
		}
		return false;
	}

	internal static string FormatPlayerList(IList<string> playerNames)
	{
		int num = playerNames?.Count ?? 0;
		if (num == 0)
		{
			return "Players online: 0.";
		}
		List<string> list = new List<string>();
		for (int i = 0; i < playerNames.Count; i++)
		{
			string text = SanitizeForLog(playerNames[i], 32);
			if (!string.IsNullOrWhiteSpace(text) && text != "-")
			{
				list.Add(text);
			}
		}
		string text2 = ((list.Count == 0) ? "names unavailable" : string.Join(", ", list.ToArray()));
		return SanitizeForLog("Players online: " + num + " - " + text2, 150);
	}

	internal static bool TryParseAuthorizationMode(string value, out AuthorizationMode mode)
	{
		mode = AuthorizationMode.Invalid;
		string a = ((value == null) ? string.Empty : value.Trim());
		if (string.Equals(a, "DevToolsAdmins", StringComparison.OrdinalIgnoreCase))
		{
			mode = AuthorizationMode.DevToolsAdmins;
			return true;
		}
		if (string.Equals(a, "ValheimAdmins", StringComparison.OrdinalIgnoreCase))
		{
			mode = AuthorizationMode.ValheimAdmins;
			return true;
		}
		if (string.Equals(a, "Either", StringComparison.OrdinalIgnoreCase))
		{
			mode = AuthorizationMode.Either;
			return true;
		}
		if (string.Equals(a, "Both", StringComparison.OrdinalIgnoreCase))
		{
			mode = AuthorizationMode.Both;
			return true;
		}
		return false;
	}

	internal static bool ModeUsesValheimAdmins(AuthorizationMode mode)
	{
		if (mode != AuthorizationMode.ValheimAdmins && mode != AuthorizationMode.Either)
		{
			return mode == AuthorizationMode.Both;
		}
		return true;
	}

	internal static bool ModeUsesDevToolsAdmins(AuthorizationMode mode)
	{
		if (mode != AuthorizationMode.DevToolsAdmins && mode != AuthorizationMode.Either)
		{
			return mode == AuthorizationMode.Both;
		}
		return true;
	}

	internal static AuthorizationDecision EvaluateAuthorization(AuthorizationMode mode, bool nativeAdmin, bool devToolsAdmin)
	{
		switch (mode)
		{
		case AuthorizationMode.DevToolsAdmins:
			return Decision(devToolsAdmin, "devtools_adminlist", "You are not authorized in the DevTools admin list.");
		case AuthorizationMode.ValheimAdmins:
			return Decision(nativeAdmin, "valheim_admin", "Valheim server administrator access is required.");
		case AuthorizationMode.Either:
		{
			bool allowed = devToolsAdmin || nativeAdmin;
			string source = (devToolsAdmin ? "either_devtools" : "either_valheim");
			return Decision(allowed, source, "DevTools or Valheim administrator access is required.");
		}
		case AuthorizationMode.Both:
			return Decision(devToolsAdmin && nativeAdmin, "both", "Both Valheim admin and DevTools authorization are required.");
		default:
			return Decision(allowed: false, "invalid_mode", "DevTools authorization is unavailable because the server configuration is invalid.");
		}
	}

	internal static bool TryNormalizePlatformId(string value, out string normalized)
	{
		normalized = ((value == null) ? string.Empty : value.Trim());
		if (normalized.Length < 3 || normalized.Length > 128 || !char.IsLetter(normalized[0]))
		{
			normalized = string.Empty;
			return false;
		}
		bool flag = false;
		for (int i = 0; i < normalized.Length; i++)
		{
			char c = normalized[i];
			if (c == '_' || c == ':' || c == '/')
			{
				flag = true;
			}
			else if (!char.IsLetterOrDigit(c) && c != '-')
			{
				normalized = string.Empty;
				return false;
			}
		}
		if (!flag)
		{
			normalized = string.Empty;
			return false;
		}
		return true;
	}

	internal static bool HasStrongPeerEvidence(bool peerFound, bool connected, bool ready, bool hasCharacter, bool rpcConnected, bool hasStablePlatformIdentity)
	{
		if (peerFound && connected && ready && hasCharacter && rpcConnected)
		{
			return hasStablePlatformIdentity;
		}
		return false;
	}

	internal static string ResolveCanonicalName(IList<string> availableNames, string requested)
	{
		if (availableNames == null || string.IsNullOrWhiteSpace(requested))
		{
			return null;
		}
		for (int i = 0; i < availableNames.Count; i++)
		{
			if (string.Equals(availableNames[i], requested, StringComparison.OrdinalIgnoreCase))
			{
				return availableNames[i];
			}
		}
		return null;
	}

	internal static bool IsResourcesFallbackCandidate(bool resourcesLoaded, CommandDescriptor command, string description)
	{
		if (resourcesLoaded && command != null && string.Equals(command.Name, "spawn", StringComparison.OrdinalIgnoreCase) && !command.IsCheat && !command.OnlyAdmin && !command.RemoteCommand && !string.IsNullOrEmpty(description))
		{
			return description.IndexOf("server-authoritative NarcLab resource drop", StringComparison.Ordinal) >= 0;
		}
		return false;
	}

	internal static string SanitizeForLog(string value, int maxLength)
	{
		if (string.IsNullOrEmpty(value))
		{
			return "-";
		}
		char[] array = value.ToCharArray();
		for (int i = 0; i < array.Length; i++)
		{
			if (char.IsControl(array[i]))
			{
				array[i] = '?';
			}
		}
		string text = new string(array);
		if (text.Length > maxLength)
		{
			return text.Substring(0, maxLength);
		}
		return text;
	}

	private static string FirstToken(string value)
	{
		string[] array = value.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length != 0)
		{
			return array[0];
		}
		return string.Empty;
	}

	private static bool IsSafeRequestId(string value)
	{
		if (string.IsNullOrWhiteSpace(value) || value.Length > 48)
		{
			return false;
		}
		foreach (char c in value)
		{
			if (!char.IsLetterOrDigit(c) && c != '-' && c != '_')
			{
				return false;
			}
		}
		return true;
	}

	private static bool IsSafeCommandName(string value)
	{
		if (value.Length > 64)
		{
			return false;
		}
		foreach (char c in value)
		{
			if (!char.IsLetterOrDigit(c) && c != '_' && c != '-')
			{
				return false;
			}
		}
		return true;
	}

	private static bool IsSafePrefabName(string value)
	{
		if (string.IsNullOrWhiteSpace(value) || value.Length > 128)
		{
			return false;
		}
		foreach (char c in value)
		{
			if (!char.IsLetterOrDigit(c) && c != '_' && c != '-' && c != '$')
			{
				return false;
			}
		}
		return true;
	}

	private static AuthorizationDecision Decision(bool allowed, string source, string denialMessage)
	{
		AuthorizationDecision authorizationDecision = new AuthorizationDecision();
		authorizationDecision.Allowed = allowed;
		authorizationDecision.Source = source;
		authorizationDecision.DenialMessage = denialMessage;
		return authorizationDecision;
	}

	private static bool HasControlCharacters(string value)
	{
		for (int i = 0; i < value.Length; i++)
		{
			if (char.IsControl(value[i]))
			{
				return true;
			}
		}
		return false;
	}
}
internal static class DevToolsAdminList
{
	internal static AdminListLoadResult LoadOrCreate(string path)
	{
		AdminListLoadResult adminListLoadResult = new AdminListLoadResult();
		try
		{
			string directoryName = Path.GetDirectoryName(path);
			if (string.IsNullOrWhiteSpace(directoryName))
			{
				throw new ArgumentException("admin list directory is missing");
			}
			Directory.CreateDirectory(directoryName);
			if (!File.Exists(path))
			{
				File.WriteAllText(path, "# One stable Valheim platform identity per line.\r\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				adminListLoadResult.Created = true;
			}
			AdminListLoadResult adminListLoadResult2 = ParseLines(File.ReadAllLines(path));
			adminListLoadResult2.Created = adminListLoadResult.Created;
			return adminListLoadResult2;
		}
		catch (Exception ex)
		{
			adminListLoadResult.ErrorType = ex.GetType().Name;
			return adminListLoadResult;
		}
	}

	internal static AdminListLoadResult ParseLines(IEnumerable<string> lines)
	{
		AdminListLoadResult adminListLoadResult = new AdminListLoadResult();
		if (lines == null)
		{
			return adminListLoadResult;
		}
		foreach (string line in lines)
		{
			string text = ((line == null) ? string.Empty : line.Trim());
			if (text.Length != 0 && !text.StartsWith("#", StringComparison.Ordinal))
			{
				if (!DevToolsPolicy.TryNormalizePlatformId(text, out var normalized))
				{
					adminListLoadResult.InvalidCount++;
				}
				else
				{
					adminListLoadResult.Ids.Add(normalized);
				}
			}
		}
		return adminListLoadResult;
	}
}
internal sealed class ReplayGuard
{
	private readonly int maximumEntries;

	private readonly Queue<string> order = new Queue<string>();

	private readonly HashSet<string> seen = new HashSet<string>(StringComparer.Ordinal);

	internal ReplayGuard(int maximumEntries)
	{
		this.maximumEntries = Math.Max(1, maximumEntries);
	}

	internal bool TryAccept(string requestId)
	{
		if (!seen.Add(requestId))
		{
			return false;
		}
		order.Enqueue(requestId);
		while (order.Count > maximumEntries)
		{
			seen.Remove(order.Dequeue());
		}
		return true;
	}
}
internal static class SpawnPlan
{
	internal static bool TryCreate(int amount, int maxStack, out List<int> stacks)
	{
		stacks = null;
		if (amount < 1 || maxStack < 1)
		{
			return false;
		}
		List<int> list = new List<int>();
		int num = amount;
		while (num > 0)
		{
			int num2 = Math.Min(num, maxStack);
			list.Add(num2);
			num -= num2;
		}
		stacks = list;
		return true;
	}
}
internal static class SpawnTransaction
{
	internal static bool TryCreate<T>(IList<int> units, Func<int, int, T> create, Action<T> rollback, out List<T> created, out Exception error)
	{
		created = new List<T>();
		error = null;
		try
		{
			for (int i = 0; i < units.Count; i++)
			{
				T val = create(units[i], i);
				if (object.Equals(val, default(T)))
				{
					throw new InvalidOperationException("spawn creation returned no object");
				}
				created.Add(val);
			}
			return true;
		}
		catch (Exception ex)
		{
			error = ex;
			for (int num = created.Count - 1; num >= 0; num--)
			{
				try
				{
					rollback(created[num]);
				}
				catch
				{
				}
			}
			created.Clear();
			return false;
		}
	}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("ca.narclab.valheim.devtools", "NarcLab DevTools", "0.1.4")]
public sealed class NarcLabDevToolsPlugin : BaseUnityPlugin
{
	private sealed class WrappedCommand
	{
		internal string Name;

		internal ConsoleCommand Original;

		internal ConsoleCommand Replacement;

		internal CommandTarget Target;

		internal bool IsResourcesSpawn;
	}

	private sealed class PendingCommand
	{
		internal WrappedCommand Entry;

		internal string FullLine;

		internal Terminal Context;

		internal DateTime CreatedAt;
	}

	public const string PluginGuid = "ca.narclab.valheim.devtools";

	public const string PluginName = "NarcLab DevTools";

	public const string PluginVersion = "0.1.4";

	public const string ResourcesGuid = "ca.narclab.valheim.resources";

	private const string RequestRpc = "NarcLabDevTools_Request_v1";

	private const string ResponseRpc = "NarcLabDevTools_Response_v1";

	private const int PendingTimeoutSeconds = 12;

	private const int RateWindowSeconds = 5;

	private const int RateWindowMaximum = 12;

	private static readonly FieldInfo CommandsField = typeof(Terminal).GetField("commands", BindingFlags.Static | BindingFlags.NonPublic);

	private static readonly FieldInfo ActionField = typeof(ConsoleCommand).GetField("action", BindingFlags.Instance | BindingFlags.NonPublic);

	private static readonly FieldInfo ActionFailableField = typeof(ConsoleCommand).GetField("actionFailable", BindingFlags.Instance | BindingFlags.NonPublic);

	private static readonly MethodInfo ServerPeerIdMethod = typeof(ZRoutedRpc).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.NonPublic);

	private readonly Dictionary<string, WrappedCommand> wrapped = new Dictionary<string, WrappedCommand>(StringComparer.OrdinalIgnoreCase);

	private readonly Dictionary<string, PendingCommand> pending = new Dictionary<string, PendingCommand>(StringComparer.Ordinal);

	private readonly Dictionary<long, ReplayGuard> replayByPeer = new Dictionary<long, ReplayGuard>();

	private readonly Dictionary<long, Queue<DateTime>> rateByPeer = new Dictionary<long, Queue<DateTime>>();

	private ConfigEntry<bool> featureEnabled;

	private ConfigEntry<string> authorizationMode;

	private ConfigEntry<bool> logCommands;

	private ConfigEntry<int> maximumSpawnAmount;

	private HashSet<string> devToolsAdminIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private ZRoutedRpc registeredRpc;

	private ZRoutedRpc failedRpc;

	private bool adminListLoaded;

	private bool invalidAuthorizationModeLogged;

	private bool registryLogged;

	private void Awake()
	{
		featureEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable NarcLab DevTools.");
		authorizationMode = ((BaseUnityPlugin)this).Config.Bind<string>("Authorization", "AuthorizationMode", "DevToolsAdmins", "Authorization source: DevToolsAdmins, ValheimAdmins, Either, or Both.");
		logCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Audit", "LogCommands", true, "Log accepted and rejected command requests.");
		maximumSpawnAmount = ((BaseUnityPlugin)this).Config.Bind<int>("Safety", "MaximumSpawnAmount", 1000, "Maximum number of entities or item units created by one spawn request (1-1000).");
		((BaseUnityPlugin)this).Logger.LogInfo((object)("NarcLab DevTools 0.1.4 initialized for Valheim " + Version.GetVersionString(false) + " network=" + 40u));
	}

	private void Update()
	{
		EnsureRpcRegistration();
		EnsureCommandRegistration();
		ExpirePendingCommands();
	}

	private void OnDestroy()
	{
		RestoreCommands();
	}

	private void EnsureRpcRegistration()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
		{
			EnsureDevToolsAdminListLoaded();
		}
		if (instance == null || object.ReferenceEquals(instance, registeredRpc) || object.ReferenceEquals(instance, failedRpc))
		{
			return;
		}
		try
		{
			instance.Register<int, string, string, string>("NarcLabDevTools_Request_v1", (Method<int, string, string, string>)HandleRequest);
			instance.Register<int, string, string, bool, string, string>("NarcLabDevTools_Response_v1", (Method<int, string, string, bool, string, string>)HandleResponse);
			registeredRpc = instance;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"NarcLab DevTools authenticated RPC contract registered");
		}
		catch (Exception ex)
		{
			failedRpc = instance;
			((BaseUnityPlugin)this).Logger.LogError((object)("RPC registration failed: " + ex.GetType().Name));
		}
	}

	private void EnsureCommandRegistration()
	{
		if (wrapped.Count > 0 || !featureEnabled.Value)
		{
			return;
		}
		Dictionary<string, ConsoleCommand> commands = GetCommands();
		if (commands == null || commands.Count == 0 || (IsResourcesLoaded() && commands.TryGetValue("spawn", out var value) && value.IsCheat))
		{
			return;
		}
		List<KeyValuePair<string, ConsoleCommand>> list = new List<KeyValuePair<string, ConsoleCommand>>(commands);
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		foreach (KeyValuePair<string, ConsoleCommand> item in list)
		{
			ConsoleCommand value2 = item.Value;
			CommandDescriptor command = Describe(value2);
			bool flag = IsResourcesSpawn(value2);
			CommandTarget commandTarget = DevToolsPolicy.ClassifyRuntimeCommand(command, flag);
			if (commandTarget == CommandTarget.Unsupported)
			{
				continue;
			}
			WrappedCommand wrappedCommand = new WrappedCommand();
			wrappedCommand.Name = item.Key;
			wrappedCommand.Original = value2;
			wrappedCommand.Target = commandTarget;
			wrappedCommand.IsResourcesSpawn = flag;
			WrappedCommand wrappedCommand2 = wrappedCommand;
			wrappedCommand2.Replacement = CreateWrapper(wrappedCommand2);
			if (commands.TryGetValue(item.Key, out var value3) && DevToolsPolicy.IsRegisteredReplacement(value3, wrappedCommand2.Replacement))
			{
				wrapped.Add(item.Key, wrappedCommand2);
				if (commandTarget == CommandTarget.ClientLocal)
				{
					num++;
				}
				if (commandTarget == CommandTarget.ServerWorld)
				{
					num2++;
				}
				if (commandTarget == CommandTarget.ServerSpawn)
				{
					num3++;
				}
			}
		}
		if (!registryLogged && wrapped.Count > 0)
		{
			registryLogged = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Current-build command registry wrapped: total=" + wrapped.Count + " client_local=" + num + " server_world=" + num2 + " server_spawn=" + num3));
		}
	}

	private ConsoleCommand CreateWrapper(WrappedCommand entry)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Expected O, but got Unknown
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Expected O, but got Unknown
		ConsoleOptionsFetcher val = (ConsoleOptionsFetcher)delegate
		{
			try
			{
				return entry.Original.GetTabOptions();
			}
			catch
			{
				return (List<string>)null;
			}
		};
		return new ConsoleCommand(entry.Name, entry.Original.Description, (ConsoleEvent)delegate(ConsoleEventArgs args)
		{
			SubmitCommand(entry, args);
		}, false, false, false, entry.Original.IsSecret, true, false, val, true, false, false);
	}

	private void SubmitCommand(WrappedCommand entry, ConsoleEventArgs args)
	{
		if (!featureEnabled.Value)
		{
			AddConsole(args?.Context, "[NarcLab DevTools] Disabled by server configuration.");
			return;
		}
		if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
		{
			AddConsole(args?.Context, "[NarcLab DevTools] A connected player session is required.");
			return;
		}
		string text = args?.FullLine;
		string text2 = Guid.NewGuid().ToString("N");
		if (DevToolsPolicy.ValidateRequest(1, text2, entry.Name, text) != RequestError.None)
		{
			AddConsole(args.Context, "[NarcLab DevTools] Invalid command request.");
			return;
		}
		if (!TryGetServerPeerId(out var peerId))
		{
			AddConsole(args.Context, "[NarcLab DevTools] Server connection is not ready.");
			return;
		}
		pending[text2] = new PendingCommand
		{
			Entry = entry,
			FullLine = text,
			Context = args.Context,
			CreatedAt = DateTime.UtcNow
		};
		try
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "NarcLabDevTools_Request_v1", new object[4] { 1, text2, entry.Name, text });
		}
		catch (Exception ex)
		{
			pending.Remove(text2);
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Command request send failed: " + ex.GetType().Name));
			AddConsole(args.Context, "[NarcLab DevTools] Command request failed.");
		}
	}

	private void HandleRequest(long sender, int protocol, string requestId, string command, string fullLine)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		RequestError requestError = DevToolsPolicy.ValidateRequest(protocol, requestId, command, fullLine);
		if (requestError != RequestError.None)
		{
			AuditReject(sender, command, requestError.ToString());
			SendResponse(sender, requestId, command, approved: false, ErrorCode(requestError), "Command request rejected.");
			return;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(sender);
		if (!IsActivePeer(sender, peer))
		{
			AuditReject(sender, command, "peer_not_active");
			SendResponse(sender, requestId, command, approved: false, "peer_not_active", "Connected player session required.");
			return;
		}
		if (!AcceptRate(sender))
		{
			AuditReject(sender, command, "rate_limited");
			SendResponse(sender, requestId, command, approved: false, "rate_limited", "Too many command requests.");
			return;
		}
		if (!replayByPeer.TryGetValue(sender, out var value))
		{
			value = new ReplayGuard(128);
			replayByPeer[sender] = value;
		}
		if (!value.TryAccept(requestId))
		{
			AuditReject(sender, command, "duplicate_request");
			SendResponse(sender, requestId, command, approved: false, "duplicate_request", "Duplicate command request.");
			return;
		}
		if (!wrapped.TryGetValue(command, out var value2))
		{
			AuditReject(sender, command, "command_not_registered");
			SendResponse(sender, requestId, command, approved: false, "command_not_registered", "Command is unavailable on this build.");
			return;
		}
		string text = SafePlatformId(peer);
		string command2 = (value2.Original.IsSecret ? (value2.Name + " <redacted>") : fullLine);
		EnsureDevToolsAdminListLoaded();
		if (!DevToolsPolicy.TryParseAuthorizationMode(authorizationMode.Value, out var mode) && !invalidAuthorizationModeLogged)
		{
			invalidAuthorizationModeLogged = true;
			((BaseUnityPlugin)this).Logger.LogError((object)"Invalid AuthorizationMode; DevTools requests will be denied.");
		}
		bool nativeAdmin = false;
		if (DevToolsPolicy.ModeUsesValheimAdmins(mode))
		{
			try
			{
				nativeAdmin = !string.IsNullOrEmpty(text) && ZNet.instance.IsAdmin(text);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Native admin lookup failed: " + ex.GetType().Name));
			}
		}
		bool devToolsAdmin = DevToolsPolicy.ModeUsesDevToolsAdmins(mode) && !string.IsNullOrEmpty(text) && devToolsAdminIds.Contains(text);
		AuthorizationDecision authorizationDecision = DevToolsPolicy.EvaluateAuthorization(mode, nativeAdmin, devToolsAdmin);
		if (!authorizationDecision.Allowed)
		{
			AuditReject(sender, command2, "not_authorized", text, authorizationDecision.Source);
			SendResponse(sender, requestId, command, approved: false, "not_authorized", authorizationDecision.DenialMessage);
		}
		else if (value2.Target == CommandTarget.ServerSpawn)
		{
			ExecuteSpawn(sender, peer, requestId, value2, fullLine, text, authorizationDecision.Source);
		}
		else if (string.Equals(value2.Name, "players", StringComparison.OrdinalIgnoreCase))
		{
			ExecutePlayers(sender, peer, requestId, value2, fullLine, text, authorizationDecision.Source);
		}
		else if (value2.Target == CommandTarget.ServerWorld)
		{
			if (InvokeOriginal(value2.Original, fullLine, (Terminal)(object)Console.instance, out var failure))
			{
				AuditAccept(peer, text, command2, value2.Target, "executed_server", authorizationDecision.Source);
				SendResponse(sender, requestId, command, approved: true, "executed_server", "Command executed by the server.");
			}
			else
			{
				AuditReject(sender, command2, "server_execution_failed_" + failure, text);
				SendResponse(sender, requestId, command, approved: false, "execution_failed", "Server command execution failed.");
			}
		}
		else if (value2.Target == CommandTarget.ClientLocal)
		{
			AuditAccept(peer, text, command2, value2.Target, "authorized_client_dispatch", authorizationDecision.Source);
			SendResponse(sender, requestId, command, approved: true, "execute_client", "Command authorized.");
		}
		else
		{
			AuditReject(sender, command2, "unsupported_command_context", text);
			SendResponse(sender, requestId, command, approved: false, "unsupported_command_context", "Command is unsupported in this context.");
		}
	}

	private void HandleResponse(long sender, int protocol, string requestId, string command, bool approved, string code, string message)
	{
		if (protocol != 1 || !TryGetServerPeerId(out var peerId) || sender != peerId)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Ignored DevTools response from non-server peer=" + sender));
			return;
		}
		if (!pending.TryGetValue(requestId, out var value) || !string.Equals(value.Entry.Name, command, StringComparison.OrdinalIgnoreCase))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Ignored unmatched DevTools response request=" + DevToolsPolicy.SanitizeForLog(requestId, 48)));
			return;
		}
		pending.Remove(requestId);
		if (approved && string.Equals(code, "execute_client", StringComparison.Ordinal))
		{
			if (!InvokeOriginal(value.Entry.Original, value.FullLine, value.Context, out var failure))
			{
				AddConsole(value.Context, "[NarcLab DevTools] Local command failed: " + failure + ".");
			}
		}
		else if (approved)
		{
			AddConsole(value.Context, "[NarcLab DevTools] " + SafeUserMessage(message));
		}
		else if (value.Entry.IsResourcesSpawn && string.Equals(code, "not_authorized", StringComparison.Ordinal))
		{
			if (!InvokeOriginal(value.Entry.Original, value.FullLine, value.Context, out var _))
			{
				AddConsole(value.Context, "[NarcLab] Resource command failed.");
			}
		}
		else
		{
			AddConsole(value.Context, "[NarcLab DevTools] Denied: " + SafeUserMessage(message));
		}
	}

	private void ExecuteSpawn(long sender, ZNetPeer peer, string requestId, WrappedCommand entry, string fullLine, string platformId, string authorizationSource)
	{
		int num = Math.Max(1, Math.Min(1000, maximumSpawnAmount.Value));
		SpawnRequest request;
		RequestError requestError = DevToolsPolicy.ParseSpawn(fullLine, num, out request);
		string error;
		Exception exception;
		if (requestError != RequestError.None)
		{
			AuditReject(sender, entry.Name, ErrorCode(requestError), platformId);
			SendResponse(sender, requestId, entry.Name, approved: false, ErrorCode(requestError), SpawnErrorMessage(requestError, num));
		}
		else if (!TrySpawn(peer, request, out error, out exception))
		{
			AuditReject(sender, entry.Name + " " + request.Prefab + " " + request.Amount, error, platformId);
			if (exception != null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Spawn diagnostic exception=" + exception.GetType().Name));
			}
			SendResponse(sender, requestId, entry.Name, approved: false, "spawn_failed", "Spawn failed: " + error + ".");
		}
		else
		{
			AuditAccept(peer, platformId, entry.Name + " " + request.Prefab + " " + request.Amount, entry.Target, "spawned_server_network_objects", authorizationSource);
			SendResponse(sender, requestId, entry.Name, approved: true, "executed_server", "Spawned " + request.Amount + " " + request.Prefab + ".");
		}
	}

	private void ExecutePlayers(long sender, ZNetPeer peer, string requestId, WrappedCommand entry, string fullLine, string platformId, string authorizationSource)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		if (!DevToolsPolicy.IsPlayersQuery(fullLine))
		{
			AuditReject(sender, entry.Name, "invalid_players_syntax", platformId);
			SendResponse(sender, requestId, entry.Name, approved: false, "invalid_command", "Usage: players");
			return;
		}
		try
		{
			List<PlayerInfo> playerList = ZNet.instance.GetPlayerList();
			List<string> list = new List<string>();
			if (playerList != null)
			{
				for (int i = 0; i < playerList.Count; i++)
				{
					list.Add(playerList[i].m_name);
				}
			}
			string message = DevToolsPolicy.FormatPlayerList(list);
			AuditAccept(peer, platformId, entry.Name, entry.Target, "reported_player_list", authorizationSource);
			SendResponse(sender, requestId, entry.Name, approved: true, "players_result", message);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Player list query failed: " + ex.GetType().Name));
			AuditReject(sender, entry.Name, "player_list_unavailable", platformId);
			SendResponse(sender, requestId, entry.Name, approved: false, "execution_failed", "Player list is unavailable.");
		}
	}

	private bool TrySpawn(ZNetPeer peer, SpawnRequest request, out string error, out Exception exception)
	{
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		error = null;
		exception = null;
		if ((Object)(object)ZNetScene.instance == (Object)null)
		{
			error = "runtime_not_ready";
			return false;
		}
		string text = ResolvePrefabName(request.Prefab);
		GameObject prefab = ((text == null) ? null : ZNetScene.instance.GetPrefab(text));
		ZNetView val = (((Object)(object)prefab == (Object)null) ? null : prefab.GetComponent<ZNetView>());
		if ((Object)(object)prefab == (Object)null || (Object)(object)val == (Object)null)
		{
			error = "network_prefab_not_found";
			return false;
		}
		ItemDrop component = prefab.GetComponent<ItemDrop>();
		List<int> units;
		if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_shared != null)
		{
			if (!SpawnPlan.TryCreate(request.Amount, component.m_itemData.m_shared.m_maxStackSize, out units))
			{
				error = "invalid_stack_plan";
				return false;
			}
		}
		else
		{
			units = new List<int>();
			for (int i = 0; i < request.Amount; i++)
			{
				units.Add(1);
			}
		}
		Vector3 origin = peer.GetRefPos() + Vector3.up * 0.75f;
		List<GameObject> created;
		bool flag = SpawnTransaction.TryCreate(units, delegate(int stack, int index)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			float num = (float)index * 2.399963f;
			float num2 = ((request.Radius <= 0f) ? 0f : (request.Radius * Mathf.Sqrt(((float)index + 0.5f) / (float)units.Count)));
			Vector3 val2 = new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * num2;
			return CreateNetworkObject(prefab, stack, request.Level, origin + val2);
		}, DestroyNetworkObject, out created, out exception);
		if (!flag)
		{
			error = "transaction_rolled_back";
		}
		return flag;
	}

	private static GameObject CreateNetworkObject(GameObject prefab, int stack, int level, Vector3 position)
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = null;
		try
		{
			val = Object.Instantiate<GameObject>(prefab, position, Quaternion.identity);
			if ((Object)(object)val == (Object)null)
			{
				throw new InvalidOperationException("instantiation returned null");
			}
			ZNetView component = val.GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || component.GetZDO() == null)
			{
				throw new InvalidOperationException("spawned object is not a server-owned network object");
			}
			component.GetZDO().Persistent = true;
			ItemDrop component2 = val.GetComponent<ItemDrop>();
			if ((Object)(object)component2 != (Object)null && component2.m_itemData != null)
			{
				component2.m_itemData.m_stack = stack;
				ItemDrop.SaveToZDO(component2.m_itemData, component.GetZDO(), -1);
			}
			Character component3 = val.GetComponent<Character>();
			if ((Object)(object)component3 != (Object)null && level > 1)
			{
				component3.SetLevel(level);
			}
			return val;
		}
		catch
		{
			DestroyNetworkObject(val);
			throw;
		}
	}

	private static void DestroyNetworkObject(GameObject value)
	{
		if (!((Object)(object)value == (Object)null))
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				ZNetScene.instance.Destroy(value);
			}
			else
			{
				Object.Destroy((Object)(object)value);
			}
		}
	}

	private static string ResolvePrefabName(string requested)
	{
		GameObject prefab = ZNetScene.instance.GetPrefab(requested);
		if ((Object)(object)prefab != (Object)null)
		{
			return requested;
		}
		return DevToolsPolicy.ResolveCanonicalName(ZNetScene.instance.GetPrefabNames(), requested);
	}

	private static bool InvokeOriginal(ConsoleCommand command, string fullLine, Terminal context, out string failure)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected O, but got Unknown
		failure = null;
		if (command == null || string.IsNullOrWhiteSpace(fullLine))
		{
			failure = "invalid_command";
			return false;
		}
		try
		{
			ConsoleEventArgs e = new ConsoleEventArgs(fullLine, context, command);
			Delegate obj = ((ActionField == null) ? null : (ActionField.GetValue(command) as Delegate));
			if ((object)obj != null)
			{
				obj.DynamicInvoke(e);
				return true;
			}
			Delegate obj2 = ((ActionFailableField == null) ? null : (ActionFailableField.GetValue(command) as Delegate));
			if ((object)obj2 == null)
			{
				failure = "action_unavailable";
				return false;
			}
			object obj3 = obj2.DynamicInvoke(e);
			if (obj3 is bool && !(bool)obj3)
			{
				failure = "parameters_or_context";
				return false;
			}
			if (obj3 is string && !string.IsNullOrWhiteSpace((string)obj3))
			{
				failure = DevToolsPolicy.SanitizeForLog((string)obj3, 80);
				return false;
			}
			return true;
		}
		catch (TargetInvocationException ex)
		{
			Exception ex2 = ex.InnerException ?? ex;
			failure = ex2.GetType().Name;
			return false;
		}
		catch (Exception ex3)
		{
			failure = ex3.GetType().Name;
			return false;
		}
	}

	private static bool IsActivePeer(long sender, ZNetPeer peer)
	{
		bool flag = peer != null;
		bool connected = flag && ZNet.instance.IsConnected(sender);
		bool ready = flag && peer.IsReady();
		bool hasCharacter = flag && !((ZDOID)(ref peer.m_characterID)).IsNone();
		bool rpcConnected = flag && peer.m_rpc != null && peer.m_rpc.IsConnected();
		bool hasStablePlatformIdentity = flag && peer.m_socket != null && !string.IsNullOrWhiteSpace(SafePlatformId(peer));
		return DevToolsPolicy.HasStrongPeerEvidence(flag, connected, ready, hasCharacter, rpcConnected, hasStablePlatformIdentity);
	}

	private bool AcceptRate(long sender)
	{
		if (!rateByPeer.TryGetValue(sender, out var value))
		{
			value = new Queue<DateTime>();
			rateByPeer[sender] = value;
		}
		DateTime utcNow = DateTime.UtcNow;
		while (value.Count > 0 && (utcNow - value.Peek()).TotalSeconds > 5.0)
		{
			value.Dequeue();
		}
		if (value.Count >= 12)
		{
			return false;
		}
		value.Enqueue(utcNow);
		return true;
	}

	private void EnsureDevToolsAdminListLoaded()
	{
		if (adminListLoaded)
		{
			return;
		}
		adminListLoaded = true;
		string path = Path.Combine(Paths.ConfigPath, "NarcLabDevTools", "adminlist.txt");
		AdminListLoadResult adminListLoadResult = DevToolsAdminList.LoadOrCreate(path);
		devToolsAdminIds = adminListLoadResult.Ids;
		if (!string.IsNullOrEmpty(adminListLoadResult.ErrorType))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DevTools admin list unavailable; loaded=0 error=" + DevToolsPolicy.SanitizeForLog(adminListLoadResult.ErrorType, 64)));
			return;
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("DevTools admin list loaded: authorized_ids=" + devToolsAdminIds.Count + " created=" + adminListLoadResult.Created.ToString().ToLowerInvariant()));
		if (adminListLoadResult.InvalidCount > 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Ignored malformed DevTools admin list entries: count=" + adminListLoadResult.InvalidCount));
		}
	}

	private static string SafePlatformId(ZNetPeer peer)
	{
		try
		{
			return (peer == null || peer.m_socket == null) ? string.Empty : peer.m_socket.GetHostName();
		}
		catch
		{
			return string.Empty;
		}
	}

	private void AuditAccept(ZNetPeer peer, string platformId, string command, CommandTarget target, string result, string authorizationSource)
	{
		if (logCommands.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("ACCEPT actor=" + DevToolsPolicy.SanitizeForLog(platformId, 128) + " peer=" + ((peer == null) ? "-" : peer.m_uid.ToString()) + " authorization=" + DevToolsPolicy.SanitizeForLog(authorizationSource, 40) + " target=" + target.ToString() + " command=" + DevToolsPolicy.SanitizeForLog(command, 160) + " result=" + DevToolsPolicy.SanitizeForLog(result, 80)));
		}
	}

	private void AuditReject(long sender, string command, string reason, string platformId = null, string authorizationSource = null)
	{
		if (logCommands.Value)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("REJECT actor=" + DevToolsPolicy.SanitizeForLog(platformId, 128) + " peer=" + sender + " authorization=" + DevToolsPolicy.SanitizeForLog(authorizationSource, 40) + " command=" + DevToolsPolicy.SanitizeForLog(command, 80) + " reason=" + DevToolsPolicy.SanitizeForLog(reason, 80)));
		}
	}

	private void SendResponse(long peerId, string requestId, string command, bool approved, string code, string message)
	{
		try
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "NarcLabDevTools_Response_v1", new object[6] { 1, requestId, command, approved, code, message });
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Command response send failed: " + ex.GetType().Name));
		}
	}

	private void ExpirePendingCommands()
	{
		if (pending.Count == 0)
		{
			return;
		}
		DateTime utcNow = DateTime.UtcNow;
		List<string> list = new List<string>();
		foreach (KeyValuePair<string, PendingCommand> item in pending)
		{
			if ((utcNow - item.Value.CreatedAt).TotalSeconds > 12.0)
			{
				list.Add(item.Key);
			}
		}
		foreach (string item2 in list)
		{
			PendingCommand pendingCommand = pending[item2];
			pending.Remove(item2);
			AddConsole(pendingCommand.Context, "[NarcLab DevTools] Server authorization timed out.");
		}
	}

	private void RestoreCommands()
	{
		Dictionary<string, ConsoleCommand> commands = GetCommands();
		if (commands == null)
		{
			return;
		}
		foreach (WrappedCommand value2 in wrapped.Values)
		{
			if (commands.TryGetValue(value2.Name, out var value) && object.ReferenceEquals(value, value2.Replacement))
			{
				commands[value2.Name] = value2.Original;
			}
		}
		wrapped.Clear();
	}

	private static Dictionary<string, ConsoleCommand> GetCommands()
	{
		try
		{
			return (CommandsField == null) ? null : (CommandsField.GetValue(null) as Dictionary<string, ConsoleCommand>);
		}
		catch
		{
			return null;
		}
	}

	private static CommandDescriptor Describe(ConsoleCommand command)
	{
		CommandDescriptor commandDescriptor = new CommandDescriptor();
		commandDescriptor.Name = command.Command;
		commandDescriptor.IsCheat = command.IsCheat;
		commandDescriptor.OnlyAdmin = command.OnlyAdmin;
		commandDescriptor.OnlyServer = command.OnlyServer;
		commandDescriptor.RemoteCommand = command.RemoteCommand;
		commandDescriptor.HideBehindDevCommands = command.HideBehindDevCommands;
		return commandDescriptor;
	}

	private static bool IsResourcesLoaded()
	{
		if (Chainloader.PluginInfos != null)
		{
			return Chainloader.PluginInfos.ContainsKey("ca.narclab.valheim.resources");
		}
		return false;
	}

	private static bool IsResourcesSpawn(ConsoleCommand command)
	{
		if (command != null)
		{
			return DevToolsPolicy.IsResourcesFallbackCandidate(IsResourcesLoaded(), Describe(command), command.Description);
		}
		return false;
	}

	private static bool TryGetServerPeerId(out long peerId)
	{
		peerId = 0L;
		if (ZRoutedRpc.instance == null || ServerPeerIdMethod == null)
		{
			return false;
		}
		try
		{
			object obj = ServerPeerIdMethod.Invoke(ZRoutedRpc.instance, null);
			if (!(obj is long))
			{
				return false;
			}
			peerId = (long)obj;
			return peerId != 0;
		}
		catch
		{
			return false;
		}
	}

	private static void AddConsole(Terminal context, string message)
	{
		if ((Object)(object)context != (Object)null)
		{
			context.AddString(message);
		}
		else if ((Object)(object)Console.instance != (Object)null)
		{
			((Terminal)Console.instance).AddString(message);
		}
	}

	private static string SafeUserMessage(string message)
	{
		return DevToolsPolicy.SanitizeForLog(message, 160);
	}

	private static string ErrorCode(RequestError error)
	{
		return error.ToString().ToLowerInvariant();
	}

	private static string SpawnErrorMessage(RequestError error, int limit)
	{
		return error switch
		{
			RequestError.SpawnAmount => "Amount must be between 1 and " + limit + ".", 
			RequestError.SpawnLevel => "Level must be between 1 and 9.", 
			RequestError.SpawnRadius => "Radius must be between 0 and 20.", 
			_ => "Usage: spawn <prefab> [amount] [level] [radius].", 
		};
	}
}