Decompiled source of EmeraudeAdmin v1.3.0

plugins/EmeraudeAdmin.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EmeraudeAdmin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0+73ed6ca8c6254f5ea1772e8735f31dfbfd8a640b")]
[assembly: AssemblyProduct("EmeraudeAdmin")]
[assembly: AssemblyTitle("EmeraudeAdmin")]
[assembly: AssemblyVersion("1.3.0.0")]
[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 EmeraudeAdmin
{
	internal static class Client
	{
		private const float CheckEvery = 1f;

		private static Player tracked;

		private static Dictionary<string, int> last;

		private static string lastSnapshot;

		private static float nextCheck;

		private static float nextSnapshot;

		private static float SnapshotEvery => (float)Plugin.SnapshotEveryMinutes.Value * 60f;

		private static string Key(ItemData item)
		{
			string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name);
			if (item.m_quality <= 1)
			{
				return text;
			}
			return $"{text}*{item.m_quality}";
		}

		private static Dictionary<string, int> Count(Player p)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ItemData allItem in ((Humanoid)p).GetInventory().GetAllItems())
			{
				string key = Key(allItem);
				dictionary.TryGetValue(key, out var value);
				dictionary[key] = value + allItem.m_stack;
			}
			return dictionary;
		}

		private static void Send(string kind, Player p, string data)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc.instance.InvokeRoutedRPC("EA_Log", new object[3]
			{
				kind,
				((Component)p).transform.position,
				data
			});
		}

		public static void Tick()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				tracked = null;
			}
			else
			{
				if (Time.time < nextCheck)
				{
					return;
				}
				nextCheck = Time.time + 1f;
				Dictionary<string, int> dictionary = Count(localPlayer);
				if ((Object)(object)localPlayer != (Object)(object)tracked)
				{
					tracked = localPlayer;
					last = dictionary;
					lastSnapshot = Net.Encode(dictionary);
					nextSnapshot = Time.time + SnapshotEvery;
					Send("apparition", localPlayer, lastSnapshot);
					return;
				}
				Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
				foreach (string item in dictionary.Keys.Union(last.Keys))
				{
					dictionary.TryGetValue(item, out var value);
					last.TryGetValue(item, out var value2);
					if (value != value2)
					{
						dictionary2[item] = value - value2;
					}
				}
				if (dictionary2.Count > 0)
				{
					Send("delta", localPlayer, Net.Encode(dictionary2));
				}
				last = dictionary;
				if (Time.time >= nextSnapshot)
				{
					nextSnapshot = Time.time + SnapshotEvery;
					string text = Net.Encode(dictionary);
					if (text != lastSnapshot)
					{
						Send("inventaire", localPlayer, text);
					}
					lastSnapshot = text;
				}
			}
		}

		public static void OnSaved(Player p)
		{
			if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsDedicated())
			{
				lastSnapshot = Net.Encode(last = Count(p));
				Send("sauvegarde", p, lastSnapshot);
			}
		}

		public static void OnDeath(Player p)
		{
			if (ZRoutedRpc.instance != null)
			{
				Send("mort", p, Net.Encode(Count(p)));
			}
		}

		public static void OnReply(long sender, string text)
		{
			if (Net.FromServer(sender))
			{
				Console instance = Console.instance;
				if (instance != null)
				{
					instance.Print("[Emeraude Admin] " + text);
				}
			}
		}

		public static void OnTeleport(long sender, Vector3 pos, bool exact)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (Net.FromServer(sender) && !((Object)(object)localPlayer == (Object)null))
			{
				((Character)localPlayer).TeleportTo(pos, ((Component)localPlayer).transform.rotation, true);
			}
		}

		public static void OnGive(long sender, string prefab, int amount, int quality)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Net.FromServer(sender) || (Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = Math.Max(1, val.m_itemData.m_shared.m_maxStackSize);
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			for (int num2 = amount; num2 > 0; num2 -= num)
			{
				int num3 = Math.Min(num2, num);
				if (inventory.AddItem(prefab, num3, quality, 0, 0L, "", false, false) == null)
				{
					ItemData obj = val.m_itemData.Clone();
					obj.m_quality = quality;
					obj.m_dropPrefab = itemPrefab;
					ItemDrop.DropItem(obj, num3, ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward + Vector3.up, Quaternion.identity);
				}
			}
			((Character)localPlayer).Message((MessageType)2, L.T($"Received: {amount} {prefab}", $"Reçu : {amount} {prefab}"), 0, (Sprite)null, false);
		}
	}
	internal static class Commands
	{
		public const string Prefix = "adm:";

		public static readonly (string name, string alias, string usageEn, string usageFr)[] All = new(string, string, string, string)[6]
		{
			("players", "joueurs", "lists the players in game and their positions", "liste les joueurs en jeu et leur position"),
			("pos", null, "<player> : position of a player", "<joueur> : position d'un joueur"),
			("inv", null, "<player> : inventory of a player (as known by the server)", "<joueur> : inventaire d'un joueur (connu du serveur)"),
			("tp", null, "<player> <player>  or  <player> <x> <z> : teleports a player", "<joueur> <joueur>  ou  <joueur> <x> <z> : téléporte un joueur"),
			("give", null, "<player> <item> [amount] [quality] : gives an item (English prefab name)", "<joueur> <objet> [quantité] [qualité] : donne un objet (nom anglais du prefab)"),
			("help", "aide", ": lists the commands", ": liste des commandes")
		};

		public static string Help => L.T("Admin commands:", "Commandes admin :") + "\n" + string.Join("\n", All.Select(delegate((string name, string alias, string usageEn, string usageFr) c)
		{
			string text;
			if (!L.Fr || c.alias == null)
			{
				(text, _, _, _) = c;
			}
			else
			{
				text = c.alias;
			}
			return "  adm:" + text + " " + L.T(c.usageEn, c.usageFr);
		}));

		public static string Canonical(string cmd)
		{
			return All.FirstOrDefault(((string name, string alias, string usageEn, string usageFr) c) => c.name == cmd || c.alias == cmd).name ?? cmd;
		}

		public static void Register()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			(string, string, string, string)[] all = All;
			for (int i = 0; i < all.Length; i++)
			{
				(string name, string alias, string usageEn, string usageFr) c = all[i];
				ConsoleEvent val = (ConsoleEvent)delegate(ConsoleEventArgs args)
				{
					if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null)
					{
						Terminal context = args.Context;
						if (context != null)
						{
							context.AddString(L.T("Connect to the server first.", "Connecte-toi d'abord au serveur."));
						}
					}
					else
					{
						string text2 = c.name + ((args.Args.Length > 1) ? (" " + string.Join(" ", args.Args.Skip(1))) : "");
						ZRoutedRpc.instance.InvokeRoutedRPC("EA_Admin", new object[1] { text2 });
					}
				};
				string text = L.T(c.usageEn, c.usageFr) + " (admins, Emeraude Admin)";
				new ConsoleCommand("adm:" + c.name, text, val, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				if (c.alias != null)
				{
					new ConsoleCommand("adm:" + c.alias, text, val, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				}
			}
		}
	}
	public enum Language
	{
		English,
		French
	}
	internal static class L
	{
		public static bool Fr
		{
			get
			{
				if (Plugin.Lang != null)
				{
					return Plugin.Lang.Value == Language.French;
				}
				return false;
			}
		}

		public static string T(string en, string fr)
		{
			if (!Fr)
			{
				return en;
			}
			return fr;
		}
	}
	internal static class Net
	{
		public const string Log = "EA_Log";

		public const string Admin = "EA_Admin";

		public const string Reply = "EA_Reply";

		public const string Teleport = "EA_Teleport";

		public const string Give = "EA_Give";

		public static void Register()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			instance.Register<string, Vector3, string>("EA_Log", (Action<long, string, Vector3, string>)Server.OnLog);
			instance.Register<string>("EA_Admin", (Action<long, string>)Server.OnAdmin);
			instance.Register<string>("EA_Reply", (Action<long, string>)Client.OnReply);
			instance.Register<Vector3, bool>("EA_Teleport", (Action<long, Vector3, bool>)Client.OnTeleport);
			instance.Register<string, int, int>("EA_Give", (Action<long, string, int, int>)Client.OnGive);
		}

		public static bool FromServer(long sender)
		{
			if (ZNet.instance.IsServer())
			{
				return sender == ZNet.GetUID();
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null)
			{
				return serverPeer.m_uid == sender;
			}
			return false;
		}

		public static string Encode(Dictionary<string, int> items)
		{
			return string.Join(";", from kv in items
				where kv.Value != 0
				select kv.Key + ":" + kv.Value);
		}

		public static Dictionary<string, int> Decode(string text)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] array = (text ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				int num = text2.LastIndexOf(':');
				if (num > 0 && int.TryParse(text2.Substring(num + 1), out var result))
				{
					dictionary[text2.Substring(0, num)] = result;
				}
			}
			return dictionary;
		}

		public static string Pos(Vector3 p)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			return $"({p.x:0}, {p.y:0}, {p.z:0})";
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class GameStartPatch
	{
		private static void Postfix()
		{
			Net.Register();
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class TerminalPatch
	{
		private static void Postfix()
		{
			Commands.Register();
		}
	}
	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	internal static class DisconnectPatch
	{
		private static void Prefix(ZNetPeer peer)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				Server.OnDisconnect(peer);
			}
		}
	}
	[HarmonyPatch(typeof(Game), "SavePlayerProfile")]
	internal static class SaveProfilePatch
	{
		private static void Postfix(Game __instance)
		{
			if (__instance.m_saveTimer == 0f && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				Client.OnSaved(Player.m_localPlayer);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class DeathPatch
	{
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Client.OnDeath(__instance);
			}
		}
	}
	[BepInPlugin("emeraude.admin", "Emeraude Admin", "1.3.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "emeraude.admin";

		public const string Name = "Emeraude Admin";

		public const string Version = "1.3.0";

		internal static ConfigEntry<Language> Lang;

		internal static ConfigEntry<int> PositionEverySeconds;

		internal static ConfigEntry<int> SnapshotEveryMinutes;

		internal static ConfigEntry<int> KeptSaves;

		private void Awake()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			Lang = ((BaseUnityPlugin)this).Config.Bind<Language>("General", "Language", Language.English, "Language of the in-game messages, of the replies to admins and of the journal (server side). / Langue des messages en jeu, des réponses aux admins et du journal (côté serveur).");
			PositionEverySeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Journal", "PositionEverySeconds", 60, new ConfigDescription("Server: interval between two logged positions for each player, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 3600), Array.Empty<object>()));
			SnapshotEveryMinutes = ((BaseUnityPlugin)this).Config.Bind<int>("Journal", "SnapshotEveryMinutes", 5, new ConfigDescription("Player: interval between two full inventories sent to the server (only if it changed), in minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 60), Array.Empty<object>()));
			KeptSaves = ((BaseUnityPlugin)this).Config.Bind<int>("Journal", "KeptSaves", 3, new ConfigDescription("Server: number of archived save periods kept per player (<player>.1.log, .2.log…).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			new Harmony("emeraude.admin").PatchAll(typeof(Plugin).Assembly);
			if (Traverse.Create(typeof(Terminal)).Field<bool>("m_terminalInitialized").Value)
			{
				Commands.Register();
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded ({2})", "Emeraude Admin", "1.3.0", Lang.Value));
		}

		private void Update()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZRoutedRpc.instance != null)
			{
				if (ZNet.instance.IsServer())
				{
					Server.Tick();
				}
				Client.Tick();
			}
		}
	}
	internal static class Server
	{
		private static readonly Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

		private static readonly HashSet<long> online = new HashSet<long>();

		private static readonly Dictionary<long, Dictionary<string, int>> inventories = new Dictionary<long, Dictionary<string, int>>();

		private static float nextPosition;

		private static string dir;

		private static string Dir
		{
			get
			{
				if (dir == null)
				{
					dir = Path.Combine(Paths.ConfigPath, "journal");
					Directory.CreateDirectory(dir);
				}
				return dir;
			}
		}

		private static string SteamId(ZNetPeer peer)
		{
			object obj;
			if (peer == null)
			{
				obj = null;
			}
			else
			{
				ISocket socket = peer.m_socket;
				obj = ((socket != null) ? socket.GetHostName() : null);
			}
			if (obj == null)
			{
				obj = "hote";
			}
			return (string)obj;
		}

		private static string Clean(string s)
		{
			return new string((s ?? "").Select((char c) => (!char.IsLetterOrDigit(c) && c != '-' && c != '_') ? '_' : c).ToArray());
		}

		private static string FileOf(ZNetPeer peer, string name)
		{
			return Clean(name) + "_" + Clean(SteamId(peer));
		}

		private static void Write(string file, string line)
		{
			try
			{
				File.AppendAllText(Path.Combine(Dir, file), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " | " + line + "\n", Utf8);
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[Emeraude Admin] cannot write the journal: " + ex.Message));
			}
		}

		private static void Journal(ZNetPeer peer, string name, Vector3 pos, string text)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Write(FileOf(peer, name) + ".log", Net.Pos(pos) + " | " + text);
		}

		private static void Rotate(string file)
		{
			try
			{
				int value = Plugin.KeptSaves.Value;
				File.Delete(PathOf(value));
				for (int num = value - 1; num >= 0; num--)
				{
					if (File.Exists(PathOf(num)))
					{
						File.Move(PathOf(num), PathOf(num + 1));
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[Emeraude Admin] cannot archive the journal: " + ex.Message));
			}
			string PathOf(int i)
			{
				return Path.Combine(Dir, (i == 0) ? (file + ".log") : $"{file}.{i}.log");
			}
		}

		private static string Describe(Dictionary<string, int> inv)
		{
			if (inv.Count != 0)
			{
				return string.Join(", ", from kv in inv
					orderby kv.Key
					select $"{kv.Key} ×{kv.Value}");
			}
			return L.T("empty", "vide");
		}

		private static string DescribeDelta(Dictionary<string, int> delta)
		{
			return string.Join(", ", from kv in delta
				orderby kv.Key
				select ((kv.Value > 0) ? "+" : "") + kv.Value + " " + kv.Key);
		}

		private static IEnumerable<ZNetPeer> InGame()
		{
			return from p in ZNet.instance.GetPeers()
				where p.IsReady() && !((ZDOID)(ref p.m_characterID)).IsNone() && !string.IsNullOrEmpty(p.m_playerName)
				select p;
		}

		public static void Tick()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			foreach (ZNetPeer item in InGame())
			{
				if (online.Add(item.m_uid))
				{
					Journal(item, item.m_playerName, item.m_refPos, L.T("connected", "connexion"));
				}
			}
			if (Time.time < nextPosition)
			{
				return;
			}
			nextPosition = Time.time + (float)Plugin.PositionEverySeconds.Value;
			foreach (ZNetPeer item2 in InGame())
			{
				Journal(item2, item2.m_playerName, item2.m_refPos, L.T("position", "position"));
			}
		}

		public static void OnDisconnect(ZNetPeer peer)
		{
			//IL_001e: 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)
			if (peer != null && online.Remove(peer.m_uid))
			{
				Journal(peer, peer.m_playerName, peer.m_refPos, L.T("disconnected", "déconnexion"));
				if (inventories.TryGetValue(peer.m_uid, out var value))
				{
					Journal(peer, peer.m_playerName, peer.m_refPos, L.T("inventory when leaving: ", "inventaire au départ : ") + Describe(value));
				}
				inventories.Remove(peer.m_uid);
			}
		}

		public static void OnLog(long sender, string kind, Vector3 pos, string data)
		{
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			if (!ZNet.instance.IsServer())
			{
				return;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			object obj = peer?.m_playerName;
			if (obj == null)
			{
				Game instance = Game.instance;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					PlayerProfile playerProfile = instance.GetPlayerProfile();
					obj = ((playerProfile != null) ? playerProfile.GetName() : null);
				}
				if (obj == null)
				{
					obj = "?";
				}
			}
			string name = (string)obj;
			Dictionary<string, int> dictionary = Net.Decode(data);
			if (!inventories.TryGetValue(sender, out var value))
			{
				value = (inventories[sender] = new Dictionary<string, int>());
			}
			switch (kind)
			{
			case "delta":
				foreach (KeyValuePair<string, int> item in dictionary)
				{
					value.TryGetValue(item.Key, out var value2);
					if (value2 + item.Value == 0)
					{
						value.Remove(item.Key);
					}
					else
					{
						value[item.Key] = value2 + item.Value;
					}
				}
				Journal(peer, name, pos, DescribeDelta(dictionary));
				break;
			case "sauvegarde":
				inventories[sender] = dictionary;
				Journal(peer, name, pos, L.T("character saved, inventory: ", "personnage sauvegardé, inventaire : ") + Describe(dictionary));
				Rotate(FileOf(peer, name));
				break;
			case "mort":
				Journal(peer, name, pos, L.T("DIED, inventory left in the tombstone: ", "MORT, inventaire laissé dans la tombe : ") + Describe(dictionary));
				break;
			default:
			{
				inventories[sender] = dictionary;
				string text = ((kind == "apparition") ? L.T("spawned with", "apparition") : L.T("inventory", "inventaire"));
				Journal(peer, name, pos, text + L.T(": ", " : ") + Describe(dictionary));
				break;
			}
			}
		}

		private static void Reply(long to, string text)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(to, "EA_Reply", new object[1] { text });
		}

		private static ZNetPeer Find(string name, out string error)
		{
			List<ZNetPeer> source = InGame().ToList();
			List<ZNetPeer> list = source.Where((ZNetPeer p) => p.m_playerName.Equals(name, StringComparison.OrdinalIgnoreCase)).ToList();
			List<ZNetPeer> list2 = ((list.Count > 0) ? list : source.Where((ZNetPeer p) => p.m_playerName.StartsWith(name, StringComparison.OrdinalIgnoreCase)).ToList());
			error = ((list2.Count == 1) ? null : ((list2.Count == 0) ? (L.T("Player \"" + name + "\" not found. Online: ", "Joueur « " + name + " » introuvable. En ligne : ") + string.Join(", ", source.Select((ZNetPeer p) => p.m_playerName))) : (L.T("\"" + name + "\" is ambiguous: ", "« " + name + " » est ambigu : ") + string.Join(", ", list2.Select((ZNetPeer p) => p.m_playerName)))));
			if (list2.Count != 1)
			{
				return null;
			}
			return list2[0];
		}

		private static GameObject FindItem(string name)
		{
			return ((IEnumerable<GameObject>)ObjectDB.instance?.m_items).FirstOrDefault((Func<GameObject, bool>)((GameObject i) => (Object)(object)i != (Object)null && ((Object)i).name.Equals(name, StringComparison.OrdinalIgnoreCase) && (Object)(object)i.GetComponent<ItemDrop>() != (Object)null));
		}

		public static void OnAdmin(long sender, string line)
		{
			if (ZNet.instance.IsServer())
			{
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				bool num = sender == ZNet.GetUID();
				string text = (num ? "hôte" : (peer?.m_playerName ?? sender.ToString()));
				if (!num && (peer == null || !ZNet.instance.IsAdmin(SteamId(peer))))
				{
					Write("admin.log", L.T("REFUSED ", "REFUSÉ ") + text + " (" + SteamId(peer) + ") : " + line);
					Reply(sender, L.T("You are not an admin of this server (adminlist.txt).", "Tu n'es pas admin du serveur (adminlist.txt)."));
				}
				else
				{
					Write("admin.log", text + " : " + line);
					Reply(sender, Run(line.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries), text));
				}
			}
		}

		private static string Run(string[] a, string admin)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			string error;
			switch (Commands.Canonical((a.Length != 0) ? a[0] : ""))
			{
			case "players":
			{
				List<ZNetPeer> list = InGame().ToList();
				if (list.Count != 0)
				{
					return string.Join("\n", list.Select((ZNetPeer p) => p.m_playerName + " " + Net.Pos(p.m_refPos)));
				}
				return L.T("Nobody in game.", "Personne en jeu.");
			}
			case "pos":
			{
				if (a.Length < 2)
				{
					return L.T("Usage: adm:pos <player>", "Usage : adm:pos <joueur>");
				}
				ZNetPeer val4 = Find(a[1], out error);
				if (val4 != null)
				{
					return val4.m_playerName + L.T(" is at ", " est en ") + Net.Pos(val4.m_refPos);
				}
				return error;
			}
			case "inv":
			{
				if (a.Length < 2)
				{
					return L.T("Usage: adm:inv <player>", "Usage : adm:inv <joueur>");
				}
				ZNetPeer val3 = Find(a[1], out error);
				if (val3 == null)
				{
					return error;
				}
				if (!inventories.TryGetValue(val3.m_uid, out var value))
				{
					return val3.m_playerName + L.T(": inventory unknown (Emeraude Admin not installed on their game?)", " : inventaire inconnu (Emeraude Admin absent chez lui ?)");
				}
				return val3.m_playerName + L.T(": ", " : ") + Describe(value);
			}
			case "tp":
			{
				if (a.Length != 3 && a.Length != 4)
				{
					return L.T("Usage: adm:tp <player> <player>  or  adm:tp <player> <x> <z>", "Usage : adm:tp <joueur> <joueur>  ou  adm:tp <joueur> <x> <z>");
				}
				ZNetPeer val5 = Find(a[1], out error);
				if (val5 == null)
				{
					return error;
				}
				Vector3 val6 = default(Vector3);
				bool flag;
				string text;
				if (a.Length == 4)
				{
					if (!float.TryParse(a[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) || !float.TryParse(a[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4))
					{
						return L.T("Invalid coordinates: adm:tp <player> <x> <z>", "Coordonnées invalides : adm:tp <joueur> <x> <z>");
					}
					((Vector3)(ref val6))..ctor(result3, 30f, result4);
					flag = false;
					text = $"({result3:0}, {result4:0})";
				}
				else
				{
					ZNetPeer val7 = Find(a[2], out error);
					if (val7 == null)
					{
						return error;
					}
					val6 = val7.m_refPos + new Vector3(1.5f, 0.5f, 0f);
					flag = true;
					text = val7.m_playerName + " " + Net.Pos(val7.m_refPos);
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(val5.m_uid, "EA_Teleport", new object[2] { val6, flag });
				Journal(val5, val5.m_playerName, val5.m_refPos, L.T("teleported by " + admin + " to " + text, "téléporté par " + admin + " vers " + text));
				return val5.m_playerName + L.T(" teleported to " + text + ".", " téléporté vers " + text + ".");
			}
			case "give":
			{
				if (a.Length < 3)
				{
					return L.T("Usage: adm:give <player> <item> [amount] [quality]", "Usage : adm:give <joueur> <objet> [quantité] [qualité]");
				}
				ZNetPeer val = Find(a[1], out error);
				if (val == null)
				{
					return error;
				}
				GameObject val2 = FindItem(a[2]);
				if ((Object)(object)val2 == (Object)null)
				{
					return L.T("Unknown item \"" + a[2] + "\" (English prefab name, e.g. Copper, Tin, Bronze, Wood).", "Objet « " + a[2] + " » inconnu (nom anglais du prefab, ex. Copper, Tin, Bronze, Wood).");
				}
				int result = 1;
				int result2 = 1;
				if (a.Length > 3 && (!int.TryParse(a[3], out result) || result < 1 || result > 9999))
				{
					return L.T("Invalid amount (1 to 9999).", "Quantité invalide (1 à 9999).");
				}
				if (a.Length > 4 && (!int.TryParse(a[4], out result2) || result2 < 1 || result2 > 10))
				{
					return L.T("Invalid quality (1 to 10).", "Qualité invalide (1 à 10).");
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, "EA_Give", new object[3]
				{
					((Object)val2).name,
					result,
					result2
				});
				Journal(val, val.m_playerName, val.m_refPos, L.T($"receives from {admin}: {result} {((Object)val2).name}", $"reçoit de {admin} : {result} {((Object)val2).name}") + ((result2 > 1) ? L.T($" (quality {result2})", $" (qualité {result2})") : ""));
				return L.T($"{result} {((Object)val2).name} given to {val.m_playerName}.", $"{result} {((Object)val2).name} donné(s) à {val.m_playerName}.");
			}
			default:
				return Commands.Help;
			}
		}
	}
}