Decompiled source of Valheim ServerGuard Client v1.4.0

Valheim-ServerGuard-Client.dll

Decompiled a week ago
using System;
using System.Collections;
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.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using ValheimServerGuard.Shared;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("yesu0725")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim ServerGuard Client - companion plugin that attests the client's mod list to a ServerGuard-protected server")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0+aec5ff4e1c5c57d0d11bef9eaeebb37e9dabfb4d")]
[assembly: AssemblyProduct("Valheim-ServerGuard-Client")]
[assembly: AssemblyTitle("Valheim-ServerGuard-Client")]
[assembly: AssemblyVersion("1.3.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 ValheimServerGuard.Shared
{
	public static class ModsetFingerprint
	{
		public static string ComputeStrict(IEnumerable<KeyValuePair<string, string>> entries)
		{
			return Sha256Hex(Canonicalize(entries, withHash: true));
		}

		public static string ComputeLoose(IEnumerable<KeyValuePair<string, string>> entries)
		{
			return Sha256Hex(Canonicalize(entries, withHash: false));
		}

		public static string Short(string fullHex)
		{
			if (!string.IsNullOrEmpty(fullHex) && fullHex.Length >= 8)
			{
				return fullHex.Substring(0, 8);
			}
			return fullHex ?? "";
		}

		private static string Canonicalize(IEnumerable<KeyValuePair<string, string>> entries, bool withHash)
		{
			if (entries == null)
			{
				return "";
			}
			List<string> list = (from e in entries
				where !string.IsNullOrWhiteSpace(e.Key)
				select (!withHash) ? (e.Key ?? "").ToLowerInvariant() : ((e.Key ?? "").ToLowerInvariant() + "|" + (e.Value ?? "").ToLowerInvariant())).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string s) => s, StringComparer.Ordinal).ToList();
			if (list.Count != 0)
			{
				return string.Join("\n", list);
			}
			return "";
		}

		private static string Sha256Hex(string input)
		{
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(input ?? ""))).Replace("-", "").ToLowerInvariant();
		}
	}
	[Serializable]
	public class ModManifestEntry
	{
		public string Guid;

		public string Name;

		public string Version;

		public string Sha256;
	}
	[Serializable]
	public class ModManifest
	{
		public string SchemaVersion = "1";

		public string Challenge;

		public long TimestampUtc;

		public List<ModManifestEntry> Mods = new List<ModManifestEntry>();

		public string Hmac;

		public string CanonicalForHmac()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(SchemaVersion ?? "").Append('|');
			stringBuilder.Append(Challenge ?? "").Append('|');
			stringBuilder.Append(TimestampUtc).Append('|');
			List<ModManifestEntry> list = new List<ModManifestEntry>(Mods ?? new List<ModManifestEntry>());
			list.Sort(delegate(ModManifestEntry a, ModManifestEntry b)
			{
				string strA = ((!string.IsNullOrEmpty(a?.Guid)) ? a.Guid : (a?.Name ?? ""));
				string strB = ((!string.IsNullOrEmpty(b?.Guid)) ? b.Guid : (b?.Name ?? ""));
				return string.CompareOrdinal(strA, strB);
			});
			foreach (ModManifestEntry item in list)
			{
				stringBuilder.Append(item?.Guid ?? "").Append(':');
				stringBuilder.Append(item?.Name ?? "").Append(':');
				stringBuilder.Append(item?.Version ?? "").Append(':');
				stringBuilder.Append(item?.Sha256 ?? "").Append(';');
			}
			return stringBuilder.ToString();
		}

		public static string ComputeHmac(string canonical, string secret)
		{
			if (string.IsNullOrEmpty(secret))
			{
				return "";
			}
			using HMACSHA256 hMACSHA = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
			return Convert.ToBase64String(hMACSHA.ComputeHash(Encoding.UTF8.GetBytes(canonical ?? "")));
		}

		public static bool ConstantTimeEquals(string a, string b)
		{
			if (a == null || b == null)
			{
				return false;
			}
			if (a.Length != b.Length)
			{
				return false;
			}
			int num = 0;
			for (int i = 0; i < a.Length; i++)
			{
				num |= a[i] ^ b[i];
			}
			return num == 0;
		}
	}
}
namespace ValheimServerGuardClient
{
	[BepInPlugin("com.taeguk.valheim.serverguard.client", "Valheim ServerGuard Client", "1.4.0")]
	public class ClientPlugin : BaseUnityPlugin
	{
		private class ClientSettings
		{
			public string SharedSecret { get; set; } = "";

		}

		[HarmonyPatch(typeof(Player), "PlacePiece")]
		public static class Patch_PlacePiece_Report
		{
			public static void Postfix(Player __instance, Piece piece, Vector3 pos)
			{
				//IL_007b: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (IsActiveMultiplayerClient() && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)piece == (Object)null))
					{
						GameObject gameObject = ((Component)piece).gameObject;
						string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? "unknown";
						int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
						if (num > 0)
						{
							text = text.Substring(0, num).Trim();
						}
						Instance?.SendBuildPlace(text, pos);
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] PlacePiece hook error: " + ex.Message));
					}
				}
			}
		}

		private sealed class LastHitInfo
		{
			public Character Attacker;

			public DateTime At;
		}

		[HarmonyPatch(typeof(WearNTear), "Damage")]
		public static class Patch_WearNTear_Damage_TrackClient
		{
			public static void Prefix(WearNTear __instance, HitData hit)
			{
				try
				{
					if (IsActiveMultiplayerClient() && !((Object)(object)__instance == (Object)null) && hit != null)
					{
						Character attacker = null;
						try
						{
							attacker = hit.GetAttacker();
						}
						catch
						{
						}
						LastHitInfo value = new LastHitInfo
						{
							Attacker = attacker,
							At = DateTime.UtcNow
						};
						_clientLastHitOnPiece.Remove(__instance);
						_clientLastHitOnPiece.Add(__instance, value);
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "Destroy")]
		public static class Patch_WearNTear_Destroy_ClientReport
		{
			public static void Prefix(WearNTear __instance)
			{
				//IL_0188: 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_0061: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!IsActiveMultiplayerClient() || (Object)(object)__instance == (Object)null)
					{
						return;
					}
					GameObject gameObject = ((Component)__instance).gameObject;
					string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? "unknown";
					int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
					if (num > 0)
					{
						text = text.Substring(0, num).Trim();
					}
					Vector3 position;
					try
					{
						position = ((Component)__instance).transform.position;
					}
					catch
					{
						return;
					}
					string text2 = "unknown";
					string text3 = "";
					if (_clientLastHitOnPiece.TryGetValue(__instance, out var value) && value != null)
					{
						_clientLastHitOnPiece.Remove(__instance);
						Character attacker = value.Attacker;
						if ((Object)(object)attacker != (Object)null)
						{
							Player val = (Player)(object)((attacker is Player) ? attacker : null);
							if (val != null)
							{
								if ((Object)(object)val == (Object)(object)Player.m_localPlayer)
								{
									text2 = "self";
									text3 = "";
								}
								else
								{
									text2 = "player";
									try
									{
										text3 = val.GetPlayerName() ?? "";
									}
									catch
									{
										text3 = "";
									}
								}
							}
							else
							{
								text2 = "creature";
								try
								{
									text3 = attacker.GetHoverName() ?? ((Object)attacker).name ?? "";
								}
								catch
								{
									text3 = ((Object)attacker).name ?? "";
								}
								int num2 = text3.IndexOf("(Clone)", StringComparison.Ordinal);
								if (num2 > 0)
								{
									text3 = text3.Substring(0, num2).Trim();
								}
							}
						}
					}
					if (text2 == "unknown")
					{
						text2 = "self";
						text3 = "";
					}
					Instance?.SendBuildDestroy(text, position, text2, text3);
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] WearNTear.Destroy hook error: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		public static class Patch_Player_OnDeath_Report
		{
			public static void Prefix(Player __instance)
			{
				try
				{
					if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && IsActiveMultiplayerClient())
					{
						Instance?.SendDeathReport(__instance);
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Death hook error: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		public static class Patch_RegisterClientHandler
		{
			public static void Postfix(ZNetPeer peer)
			{
				try
				{
					if (peer == null || peer.m_rpc == null || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()))
					{
						return;
					}
					peer.m_rpc.Register<string>("ServerGuard_RequestManifest", (Action<ZRpc, string>)delegate(ZRpc rpc, string challenge)
					{
						try
						{
							string text2 = Instance.BuildManifestJson(challenge);
							rpc.Invoke("ServerGuard_Manifest", new object[1] { text2 });
							LogS.LogInfo((object)$"[ServerGuard.Client] Sent manifest ({text2.Length} bytes, {Instance._cachedManifest?.Count ?? 0} mods).");
						}
						catch (Exception ex3)
						{
							LogS.LogError((object)("[ServerGuard.Client] Manifest send failed: " + ex3.Message));
						}
					});
					if ((Object)(object)Instance != (Object)null)
					{
						Instance._serverRpc = peer.m_rpc;
					}
					peer.m_rpc.Register<string>("ServerGuard_AdminCommandReply", (Action<ZRpc, string>)delegate(ZRpc rpc, string text)
					{
						try
						{
							Instance?.DisplayAdminReply(text);
						}
						catch (Exception ex2)
						{
							ManualLogSource logS2 = LogS;
							if (logS2 != null)
							{
								logS2.LogWarning((object)("[ServerGuard.Client] Admin reply display failed: " + ex2.Message));
							}
						}
					});
					LogS.LogInfo((object)"[ServerGuard.Client] Registered manifest request handler on server peer.");
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogError((object)("[ServerGuard.Client] Register handler failed: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "StartEmote")]
		public static class Patch_Player_StartEmote_BlockDuringAttack
		{
			public static bool Prefix(Player __instance)
			{
				try
				{
					if (ShouldBlockAnimationCancel(__instance))
					{
						Instance?.ReportAnimationCancel("emote");
						return false;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Emote gate error: " + ex.Message));
					}
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "HideHandItems")]
		public static class Patch_Humanoid_HideHandItems_BlockDuringAttack
		{
			public static bool Prefix(Humanoid __instance)
			{
				try
				{
					Player val = (Player)(object)((__instance is Player) ? __instance : null);
					if ((Object)(object)val == (Object)null)
					{
						return true;
					}
					if (ShouldBlockAnimationCancel(val))
					{
						Instance?.ReportAnimationCancel("sheathe");
						return false;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Sheathe gate error: " + ex.Message));
					}
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Terminal), "TryRunCommand")]
		public static class Patch_TryRunCommand
		{
			public static bool Prefix(string text)
			{
				try
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						return true;
					}
					string text2 = text.TrimStart(Array.Empty<char>());
					int num = text2.IndexOf(' ');
					string text3 = ((num >= 0) ? text2.Substring(0, num) : text2);
					string text4 = (text3.StartsWith("/", StringComparison.Ordinal) ? text3.Substring(1) : text3);
					if (string.Equals(text4, "sg", StringComparison.OrdinalIgnoreCase))
					{
						string command = ((num >= 0) ? text2.Substring(num + 1).TrimStart(Array.Empty<char>()) : "");
						Instance?.SendAdminCommand(command);
						return false;
					}
					if (!IsActiveMultiplayerClient())
					{
						return true;
					}
					bool num2 = BlockedCommands.Contains(text4);
					bool flag = !num2 && IsRegisteredCheatCommand(text4);
					if (num2 || flag)
					{
						Instance?.ReportDevcommand(text4);
						return false;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Devcommand gate error: " + ex.Message));
					}
				}
				return true;
			}
		}

		[CompilerGenerated]
		private sealed class <DeferredInit>d__15 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public ClientPlugin <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DeferredInit>d__15(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Expected O, but got Unknown
				int num = <>1__state;
				ClientPlugin clientPlugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(2f);
					<>1__state = 2;
					return true;
				case 2:
				{
					<>1__state = -1;
					clientPlugin.BuildManifestCache();
					clientPlugin.ExportAllowedModsSnippet();
					((MonoBehaviour)clientPlugin).StartCoroutine(clientPlugin.SkillReportLoop());
					string text = "";
					string text2 = "";
					try
					{
						List<KeyValuePair<string, string>> entries = (clientPlugin._cachedManifest ?? new List<ModManifestEntry>()).Select((ModManifestEntry m) => new KeyValuePair<string, string>((!string.IsNullOrEmpty(m.Guid)) ? m.Guid : (m.Name ?? ""), m.Sha256 ?? "")).ToList();
						text = ModsetFingerprint.Short(ModsetFingerprint.ComputeLoose(entries));
						text2 = ModsetFingerprint.Short(ModsetFingerprint.ComputeStrict(entries));
					}
					catch (Exception ex)
					{
						LogS.LogWarning((object)("[ServerGuard.Client] Fingerprint compute failed: " + ex.Message));
					}
					LogS.LogInfo((object)string.Format("[ServerGuard.Client] Loaded v{0}. Manifest entries: {1}. HMAC: {2}", "1.4.0", clientPlugin._cachedManifest?.Count ?? 0, string.IsNullOrEmpty(clientPlugin._sharedSecret) ? "OFF (no shared_secret configured)" : "ON"));
					LogS.LogInfo((object)("[ServerGuard.Client] Modset fingerprint  loose=" + text + "  strict=" + text2));
					return false;
				}
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <EnumerateSkills>d__47 : IEnumerable<KeyValuePair<string, float>>, IEnumerable, IEnumerator<KeyValuePair<string, float>>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private KeyValuePair<string, float> <>2__current;

			private int <>l__initialThreadId;

			private Skills skills;

			public Skills <>3__skills;

			private IDictionaryEnumerator <>7__wrap1;

			KeyValuePair<string, float> IEnumerator<KeyValuePair<string, float>>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EnumerateSkills>d__47(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				bool result;
				try
				{
					switch (<>1__state)
					{
					default:
						result = false;
						goto end_IL_0000;
					case 0:
						<>1__state = -1;
						if ((Object)(object)skills == (Object)null)
						{
							result = false;
						}
						else
						{
							if (!(_skillsDataField == null))
							{
								goto IL_00a0;
							}
							FieldInfo[] fields = typeof(Skills).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							foreach (FieldInfo fieldInfo in fields)
							{
								if (typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType))
								{
									_skillsDataField = fieldInfo;
									break;
								}
							}
							if (!(_skillsDataField == null))
							{
								goto IL_00a0;
							}
							result = false;
						}
						goto end_IL_0000;
					case 1:
						{
							<>1__state = -3;
							break;
						}
						IL_00a0:
						if (_skillsDataField.GetValue(skills) is IDictionary dictionary)
						{
							<>7__wrap1 = dictionary.GetEnumerator();
							<>1__state = -3;
							break;
						}
						result = false;
						goto end_IL_0000;
					}
					while (true)
					{
						if (<>7__wrap1.MoveNext())
						{
							DictionaryEntry dictionaryEntry = (DictionaryEntry)<>7__wrap1.Current;
							string text = dictionaryEntry.Key?.ToString();
							if (string.IsNullOrEmpty(text))
							{
								continue;
							}
							object value = dictionaryEntry.Value;
							if (value == null)
							{
								continue;
							}
							if (_skillLevelField == null)
							{
								FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
								foreach (FieldInfo fieldInfo2 in fields)
								{
									if (fieldInfo2.FieldType == typeof(float) && fieldInfo2.Name.IndexOf("level", StringComparison.OrdinalIgnoreCase) >= 0)
									{
										_skillLevelField = fieldInfo2;
										break;
									}
								}
								if (_skillLevelField == null)
								{
									result = false;
									<>m__Finally1();
									break;
								}
							}
							float num = 0f;
							try
							{
								num = (float)_skillLevelField.GetValue(value);
							}
							catch
							{
								continue;
							}
							<>2__current = new KeyValuePair<string, float>(text, num);
							<>1__state = 1;
							result = true;
							break;
						}
						<>m__Finally1();
						<>7__wrap1 = null;
						result = false;
						break;
					}
					end_IL_0000:;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>7__wrap1 is IDisposable disposable)
				{
					disposable.Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<KeyValuePair<string, float>> IEnumerable<KeyValuePair<string, float>>.GetEnumerator()
			{
				<EnumerateSkills>d__47 <EnumerateSkills>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<EnumerateSkills>d__ = this;
				}
				else
				{
					<EnumerateSkills>d__ = new <EnumerateSkills>d__47(0);
				}
				<EnumerateSkills>d__.skills = <>3__skills;
				return <EnumerateSkills>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<KeyValuePair<string, float>>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <SkillReportLoop>d__42 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public ClientPlugin <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SkillReportLoop>d__42(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Expected O, but got Unknown
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0059: Expected O, but got Unknown
				int num = <>1__state;
				ClientPlugin clientPlugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(15f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					break;
				case 2:
					<>1__state = -1;
					try
					{
						clientPlugin.SendSkillReportNow();
					}
					catch (Exception ex)
					{
						ManualLogSource logS = LogS;
						if (logS != null)
						{
							logS.LogWarning((object)("[ServerGuard.Client] Skill report tick error: " + ex.Message));
						}
					}
					break;
				}
				<>2__current = (object)new WaitForSeconds(60f);
				<>1__state = 2;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string GUID = "com.taeguk.valheim.serverguard.client";

		public const string NAME = "Valheim ServerGuard Client";

		public const string VERSION = "1.4.0";

		internal static ClientPlugin Instance;

		internal static ManualLogSource LogS;

		private Harmony _harmony;

		private string _sharedSecret = "";

		private List<ModManifestEntry> _cachedManifest;

		internal ZRpc _serverRpc;

		private static readonly HashSet<string> BlockedCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"devcommands", "debugmode", "imacheater", "god", "ghost", "fly", "nocost", "noplacementcost", "spawn", "pos",
			"goto", "tame", "killall", "event", "stopevent", "tod", "skiptime", "sleep", "raiseskill", "resetcharacter",
			"heal", "puke", "damage", "setkey", "resetkeys", "removedrops", "freefly"
		};

		private static readonly string ConfDir = Path.Combine(Paths.ConfigPath, "ServerGuard");

		private static readonly string ClientYaml = Path.Combine(ConfDir, "client.yaml");

		private static readonly string ExportYaml = Path.Combine(ConfDir, "mods_for_allowed_mods.yaml");

		private static MethodInfo _consoleWriteMethod;

		private static int _consoleWriteArity;

		private static object _consoleInstance;

		private static readonly ConditionalWeakTable<WearNTear, LastHitInfo> _clientLastHitOnPiece = new ConditionalWeakTable<WearNTear, LastHitInfo>();

		private static FieldInfo _playerLastHitField;

		private static MethodInfo _hitGetAttackerMethod;

		private static FieldInfo _hitDamageField;

		private const float SkillReportIntervalSeconds = 60f;

		private static FieldInfo _playerSkillsField;

		private static FieldInfo _skillsDataField;

		private static FieldInfo _skillLevelField;

		private void Awake()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Instance = this;
			LogS = ((BaseUnityPlugin)this).Logger;
			EnsureConfig();
			_harmony = new Harmony("com.taeguk.valheim.serverguard.client");
			_harmony.PatchAll();
			((MonoBehaviour)this).StartCoroutine(DeferredInit());
		}

		[IteratorStateMachine(typeof(<DeferredInit>d__15))]
		private IEnumerator DeferredInit()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DeferredInit>d__15(0)
			{
				<>4__this = this
			};
		}

		private void ExportAllowedModsSnippet()
		{
			try
			{
				if (File.Exists(ExportYaml))
				{
					LogS.LogInfo((object)("[ServerGuard.Client] Allowed-mods export already present at " + ExportYaml + ". Delete the file to regenerate."));
					return;
				}
				List<ModManifestEntry> list = _cachedManifest ?? new List<ModManifestEntry>();
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("# ServerGuard - allowed_mods snippet generated by ServerGuard.Client v1.4.0");
				stringBuilder.AppendLine($"# Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}Z   Mods on this client: {list.Count}");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# How to use:");
				stringBuilder.AppendLine("#   1. Open <server>/BepInEx/config/ServerGuard/conf/allowed_mods.yaml");
				stringBuilder.AppendLine("#   2. Replace the `allowed_mods:` block with the one below");
				stringBuilder.AppendLine("#      (or merge if you already have entries you want to keep).");
				stringBuilder.AppendLine("#   3. Save. The server hot-reloads within ~1 second.");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# Each entry is `<GUID>|<sha256>` (GUID-keyed, hash-pinned).");
				stringBuilder.AppendLine("# To loosen, drop the `|<sha256>` suffix - the entry will then accept any hash.");
				stringBuilder.AppendLine("# To tighten further, leave it as-is - the server will require an exact DLL match.");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# The companion plugin (this DLL) is intentionally listed under required_mods,");
				stringBuilder.AppendLine("# NOT allowed_mods - the server demands its presence.");
				stringBuilder.AppendLine();
				ModManifestEntry modManifestEntry = list.FirstOrDefault((ModManifestEntry m) => string.Equals(m.Guid, "com.taeguk.valheim.serverguard.client", StringComparison.OrdinalIgnoreCase));
				stringBuilder.AppendLine("required_mods:");
				if (modManifestEntry != null && !string.IsNullOrEmpty(modManifestEntry.Sha256))
				{
					stringBuilder.AppendLine("  - " + modManifestEntry.Guid + "|" + modManifestEntry.Sha256 + "    # " + modManifestEntry.Name + " v" + modManifestEntry.Version);
				}
				else
				{
					stringBuilder.AppendLine("  - com.taeguk.valheim.serverguard.client                                                # Valheim ServerGuard Client v1.4.0");
				}
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("allowed_mods:");
				List<ModManifestEntry> list2 = list.Where((ModManifestEntry m) => !string.Equals(m.Guid, "com.taeguk.valheim.serverguard.client", StringComparison.OrdinalIgnoreCase)).OrderBy<ModManifestEntry, string>((ModManifestEntry m) => m.Name ?? "", StringComparer.OrdinalIgnoreCase).ToList();
				if (list2.Count == 0)
				{
					stringBuilder.AppendLine("  []");
				}
				else
				{
					int num = 0;
					foreach (ModManifestEntry item in list2)
					{
						int num2 = (((!string.IsNullOrEmpty(item.Guid)) ? item.Guid : item.Name) ?? "").Length + ((!string.IsNullOrEmpty(item.Sha256)) ? (1 + item.Sha256.Length) : 0);
						if (num2 > num)
						{
							num = num2;
						}
					}
					foreach (ModManifestEntry item2 in list2)
					{
						string text = ((!string.IsNullOrEmpty(item2.Guid)) ? item2.Guid : (item2.Name ?? ""));
						string text2 = (string.IsNullOrEmpty(item2.Sha256) ? text : (text + "|" + item2.Sha256));
						string text3 = new string(' ', Math.Max(1, num - text2.Length + 2));
						string text4 = (string.IsNullOrEmpty(item2.Name) ? "" : (item2.Name + " v" + item2.Version));
						if (string.IsNullOrEmpty(item2.Guid))
						{
							stringBuilder.AppendLine("  - " + text2 + text3 + "# " + text4 + " (no GUID; consider replacing the key with the mod's BepInPlugin GUID)");
						}
						else
						{
							stringBuilder.AppendLine("  - " + text2 + text3 + "# " + text4);
						}
					}
				}
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("banned_mods: []");
				stringBuilder.AppendLine();
				Directory.CreateDirectory(ConfDir);
				File.WriteAllText(ExportYaml, stringBuilder.ToString());
				LogS.LogWarning((object)"[ServerGuard.Client] First-run mod export written:");
				LogS.LogWarning((object)("[ServerGuard.Client]   " + ExportYaml));
				LogS.LogWarning((object)$"[ServerGuard.Client]   ({list.Count} plugins). Paste its contents into the server's allowed_mods.yaml.");
			}
			catch (Exception ex)
			{
				LogS.LogError((object)("[ServerGuard.Client] ExportAllowedModsSnippet failed: " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
		}

		private void EnsureConfig()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			try
			{
				Directory.CreateDirectory(ConfDir);
				if (!File.Exists(ClientYaml))
				{
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.AppendLine("# Valheim ServerGuard - Client config");
					stringBuilder.AppendLine("# sharedSecret MUST match the server's settings.yaml `sharedSecret` value");
					stringBuilder.AppendLine("# verbatim. The server will reject manifests whose HMAC does not match.");
					stringBuilder.AppendLine("# Leave empty only if the server has `requireHmac: false` (insecure).");
					stringBuilder.AppendLine("sharedSecret: \"\"");
					File.WriteAllText(ClientYaml, stringBuilder.ToString());
				}
				ClientSettings clientSettings = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).IgnoreUnmatchedProperties().Build()
					.Deserialize<ClientSettings>(File.ReadAllText(ClientYaml)) ?? new ClientSettings();
				_sharedSecret = clientSettings.SharedSecret ?? "";
			}
			catch (Exception ex)
			{
				LogS.LogWarning((object)("[ServerGuard.Client] EnsureConfig failed: " + ex.Message));
			}
		}

		private void BuildManifestCache()
		{
			_cachedManifest = new List<ModManifestEntry>();
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					PluginInfo value = pluginInfo.Value;
					BepInPlugin val = ((value != null) ? value.Metadata : null);
					string sha = "";
					try
					{
						string text = ((value != null) ? value.Location : null);
						if (!string.IsNullOrEmpty(text) && File.Exists(text))
						{
							using SHA256 sHA = SHA256.Create();
							using FileStream inputStream = File.OpenRead(text);
							sha = BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "").ToLowerInvariant();
						}
					}
					catch
					{
					}
					_cachedManifest.Add(new ModManifestEntry
					{
						Guid = (((val != null) ? val.GUID : null) ?? ""),
						Name = (((val != null) ? val.Name : null) ?? ""),
						Version = (((val == null) ? null : val.Version?.ToString()) ?? ""),
						Sha256 = sha
					});
				}
			}
			catch (Exception ex)
			{
				LogS.LogError((object)("[ServerGuard.Client] BuildManifestCache failed: " + ex.Message));
			}
		}

		public string BuildManifestJson(string challenge)
		{
			BuildManifestCache();
			ModManifest obj = new ModManifest
			{
				SchemaVersion = "1",
				Challenge = (challenge ?? ""),
				TimestampUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
				Mods = (_cachedManifest ?? new List<ModManifestEntry>())
			};
			obj.Hmac = ModManifest.ComputeHmac(obj.CanonicalForHmac(), _sharedSecret);
			return JsonConvert.SerializeObject((object)obj);
		}

		internal void SendAdminCommand(string command)
		{
			if (!IsActiveMultiplayerClient())
			{
				DisplayAdminReply("[ServerGuard] sg commands only work while connected to a multiplayer server.");
				return;
			}
			if (_serverRpc == null)
			{
				DisplayAdminReply("[ServerGuard] Not connected to a server peer yet.");
				return;
			}
			try
			{
				_serverRpc.Invoke("ServerGuard_AdminCommand", new object[1] { command ?? "" });
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogInfo((object)("[ServerGuard.Client] Sent admin command: " + command));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)("[ServerGuard.Client] Admin command send failed: " + ex.Message));
				}
				DisplayAdminReply("[ServerGuard] Send failed: " + ex.Message);
			}
		}

		internal void DisplayAdminReply(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string[] array = text.Split(new char[1] { '\n' });
			try
			{
				object obj = ResolveConsoleInstance();
				string[] array2;
				if (obj == null || _consoleWriteMethod == null)
				{
					array2 = array;
					foreach (string text2 in array2)
					{
						ManualLogSource logS = LogS;
						if (logS != null)
						{
							logS.LogInfo((object)("[ServerGuard] " + text2));
						}
					}
					return;
				}
				array2 = array;
				foreach (string text3 in array2)
				{
					if (string.IsNullOrWhiteSpace(text3))
					{
						continue;
					}
					try
					{
						object[] parameters = ((_consoleWriteArity != 1) ? new object[2] { text3, false } : new object[1] { text3 });
						_consoleWriteMethod.Invoke(obj, parameters);
					}
					catch
					{
						ManualLogSource logS2 = LogS;
						if (logS2 != null)
						{
							logS2.LogInfo((object)("[ServerGuard] " + text3));
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS3 = LogS;
				if (logS3 != null)
				{
					logS3.LogWarning((object)("[ServerGuard.Client] DisplayAdminReply error: " + ex.Message));
				}
			}
		}

		private object ResolveConsoleInstance()
		{
			if (_consoleInstance != null && _consoleWriteMethod != null)
			{
				return _consoleInstance;
			}
			Type type = typeof(Terminal).Assembly.GetType("Console");
			object obj = null;
			if (type != null)
			{
				try
				{
					PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null)
					{
						obj = property.GetValue(null);
					}
				}
				catch
				{
				}
				if (obj == null)
				{
					try
					{
						FieldInfo field = type.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						if (field != null)
						{
							obj = field.GetValue(null);
						}
					}
					catch
					{
					}
				}
			}
			if (obj == null)
			{
				try
				{
					obj = Object.FindObjectOfType<Terminal>();
				}
				catch
				{
				}
			}
			if (obj == null)
			{
				return null;
			}
			MethodInfo methodInfo = null;
			int num = 0;
			string[] array = new string[2] { "Print", "AddString" };
			foreach (string text in array)
			{
				MethodInfo[] methods = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (!(methodInfo2.Name != text))
					{
						ParameterInfo[] parameters = methodInfo2.GetParameters();
						if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
						{
							methodInfo = methodInfo2;
							num = 1;
							break;
						}
						if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(bool) && methodInfo == null)
						{
							methodInfo = methodInfo2;
							num = 2;
						}
					}
				}
				if (methodInfo != null && num == 1)
				{
					break;
				}
			}
			if (methodInfo == null)
			{
				return null;
			}
			_consoleInstance = obj;
			_consoleWriteMethod = methodInfo;
			_consoleWriteArity = num;
			return _consoleInstance;
		}

		internal void SendBuildPlace(string pieceName, Vector3 pos)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (_serverRpc == null)
			{
				return;
			}
			try
			{
				string text = SanitiseShort(pieceName, 64);
				string text2 = string.Format(CultureInfo.InvariantCulture, "{0}|{1:F1}|{2:F1}|{3:F1}", text, pos.x, pos.y, pos.z);
				_serverRpc.Invoke("ServerGuard_BuildPlace", new object[1] { text2 });
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] BuildPlace RPC failed: " + ex.Message));
				}
			}
		}

		internal void SendBuildDestroy(string pieceName, Vector3 pos, string attackerKind, string attackerLabel)
		{
			//IL_004e: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			if (_serverRpc == null)
			{
				return;
			}
			try
			{
				string text = SanitiseShort(pieceName, 64);
				string text2 = SanitiseShort(attackerKind ?? "unknown", 16);
				string text3 = SanitiseShort(attackerLabel ?? "", 48);
				string text4 = string.Format(CultureInfo.InvariantCulture, "{0}|{1:F1}|{2:F1}|{3:F1}|{4}|{5}", text, pos.x, pos.y, pos.z, text2, text3);
				_serverRpc.Invoke("ServerGuard_BuildDestroy", new object[1] { text4 });
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] BuildDestroy RPC failed: " + ex.Message));
				}
			}
		}

		private static string SanitiseShort(string s, int max)
		{
			string text = (s ?? "").Replace('|', ' ').Replace('\n', ' ').Trim();
			if (text.Length > max)
			{
				text = text.Substring(0, max);
			}
			return text;
		}

		internal void SendDeathReport(Player p)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			if (_serverRpc == null)
			{
				return;
			}
			try
			{
				Vector3 position = ((Component)p).transform.position;
				string text = "environment";
				string text2 = "";
				string text3 = "";
				if (_playerLastHitField == null)
				{
					FieldInfo[] fields = typeof(Player).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						if (fieldInfo.FieldType == typeof(HitData))
						{
							_playerLastHitField = fieldInfo;
							break;
						}
					}
				}
				object? obj = _playerLastHitField?.GetValue(p);
				HitData val = (HitData)((obj is HitData) ? obj : null);
				if (val != null && val != null)
				{
					text3 = DominantDamageType(val);
					if (_hitGetAttackerMethod == null)
					{
						_hitGetAttackerMethod = typeof(HitData).GetMethod("GetAttacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					}
					Character val2 = null;
					if (_hitGetAttackerMethod != null)
					{
						try
						{
							object? obj2 = _hitGetAttackerMethod.Invoke(val, null);
							val2 = (Character)((obj2 is Character) ? obj2 : null);
						}
						catch
						{
						}
					}
					if ((Object)(object)val2 != (Object)null)
					{
						Player val3 = (Player)(object)((val2 is Player) ? val2 : null);
						if (val3 != null)
						{
							if ((Object)(object)val3 == (Object)(object)p)
							{
								text = "self";
								text2 = "";
							}
							else
							{
								text = "player";
								text2 = val3.GetPlayerName() ?? "";
							}
						}
						else
						{
							text = "creature";
							try
							{
								text2 = val2.GetHoverName() ?? ((Object)val2).name ?? "";
							}
							catch
							{
								text2 = ((Object)val2).name ?? "";
							}
						}
					}
				}
				text2 = (text2 ?? "").Replace('|', ' ').Replace('\n', ' ').Trim();
				text3 = (text3 ?? "").Replace('|', ' ').Replace('\n', ' ').Trim();
				string text4 = string.Format(CultureInfo.InvariantCulture, "{0:F1}|{1:F1}|{2:F1}|{3}|{4}|{5}", position.x, position.y, position.z, text, text2, text3);
				try
				{
					_serverRpc.Invoke("ServerGuard_PlayerDeath", new object[1] { text4 });
					LogS.LogInfo((object)("[ServerGuard.Client] Death report sent (" + text + " / " + text2 + " / " + text3 + ")."));
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Death report RPC failed: " + ex.Message));
					}
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)("[ServerGuard.Client] SendDeathReport error: " + ex2.Message));
				}
			}
		}

		private static string DominantDamageType(HitData hit)
		{
			if (hit == null)
			{
				return "";
			}
			FieldInfo[] fields;
			if (_hitDamageField == null)
			{
				fields = typeof(HitData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.Name.Equals("m_damage", StringComparison.OrdinalIgnoreCase))
					{
						_hitDamageField = fieldInfo;
						break;
					}
				}
			}
			if (_hitDamageField == null)
			{
				return "";
			}
			object value;
			try
			{
				value = _hitDamageField.GetValue(hit);
			}
			catch
			{
				return "";
			}
			if (value == null)
			{
				return "";
			}
			string text = "";
			float num = 0f;
			fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo2 in fields)
			{
				if (!(fieldInfo2.FieldType != typeof(float)))
				{
					float num2;
					try
					{
						num2 = (float)fieldInfo2.GetValue(value);
					}
					catch
					{
						continue;
					}
					if (num2 > num)
					{
						num = num2;
						text = fieldInfo2.Name;
					}
				}
			}
			if (text.StartsWith("m_", StringComparison.Ordinal))
			{
				text = text.Substring(2);
			}
			if (text.Length > 0)
			{
				text = char.ToUpperInvariant(text[0]) + text.Substring(1);
			}
			return text;
		}

		[IteratorStateMachine(typeof(<SkillReportLoop>d__42))]
		private IEnumerator SkillReportLoop()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SkillReportLoop>d__42(0)
			{
				<>4__this = this
			};
		}

		private static Skills GetPlayerSkills(Player p)
		{
			if ((Object)(object)p == (Object)null)
			{
				return null;
			}
			if (_playerSkillsField == null)
			{
				FieldInfo[] fields = typeof(Player).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.FieldType == typeof(Skills))
					{
						_playerSkillsField = fieldInfo;
						break;
					}
				}
				if (_playerSkillsField == null)
				{
					return null;
				}
			}
			object? value = _playerSkillsField.GetValue(p);
			return (Skills)((value is Skills) ? value : null);
		}

		[IteratorStateMachine(typeof(<EnumerateSkills>d__47))]
		private static IEnumerable<KeyValuePair<string, float>> EnumerateSkills(Skills skills)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <EnumerateSkills>d__47(-2)
			{
				<>3__skills = skills
			};
		}

		private void SendSkillReportNow()
		{
			if (_serverRpc == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Skills playerSkills = GetPlayerSkills(localPlayer);
			if ((Object)(object)playerSkills == (Object)null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			bool flag = true;
			foreach (KeyValuePair<string, float> item in EnumerateSkills(playerSkills))
			{
				string key = item.Key;
				float value = item.Value;
				if (!string.IsNullOrEmpty(key) && key.IndexOf(':') < 0 && key.IndexOf('|') < 0 && key.Length <= 32)
				{
					if (!flag)
					{
						stringBuilder.Append('|');
					}
					stringBuilder.Append(key);
					stringBuilder.Append(':');
					stringBuilder.Append(value.ToString("F1", CultureInfo.InvariantCulture));
					flag = false;
				}
			}
			if (stringBuilder.Length == 0)
			{
				return;
			}
			try
			{
				_serverRpc.Invoke("ServerGuard_SkillReport", new object[1] { stringBuilder.ToString() });
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] Skill report send failed: " + ex.Message));
				}
			}
		}

		private static bool IsActiveMultiplayerClient()
		{
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					return false;
				}
				if (ZNet.instance.IsServer())
				{
					return false;
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal void ReportDevcommand(string command)
		{
			try
			{
				LogS.LogWarning((object)("[ServerGuard.Client] Blocked cheat attempt: `" + command + "` (multiplayer client)"));
				if (_serverRpc != null)
				{
					try
					{
						_serverRpc.Invoke("ServerGuard_DevcommandAttempt", new object[1] { command ?? "" });
						return;
					}
					catch (Exception ex)
					{
						LogS.LogWarning((object)("[ServerGuard.Client] Could not report devcommand to server: " + ex.Message));
						return;
					}
				}
			}
			catch
			{
			}
		}

		internal void ReportAnimationCancel(string source)
		{
			try
			{
				LogS.LogInfo((object)("[ServerGuard.Client] Blocked animation cancel via " + source + " (mid-attack)."));
				if (_serverRpc != null)
				{
					try
					{
						_serverRpc.Invoke("ServerGuard_AnimationCancelAttempt", new object[1] { source ?? "" });
						return;
					}
					catch (Exception ex)
					{
						LogS.LogWarning((object)("[ServerGuard.Client] Could not report animation-cancel: " + ex.Message));
						return;
					}
				}
			}
			catch
			{
			}
		}

		private static bool IsRegisteredCheatCommand(string cmd)
		{
			if (string.IsNullOrEmpty(cmd))
			{
				return false;
			}
			try
			{
				Type typeFromHandle = typeof(Terminal);
				IDictionary dictionary = null;
				FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType) && fieldInfo.GetValue(null) is IDictionary dictionary2)
					{
						dictionary = dictionary2;
						break;
					}
				}
				if (dictionary == null)
				{
					return false;
				}
				object obj = null;
				foreach (DictionaryEntry item in dictionary)
				{
					if (item.Key is string a && string.Equals(a, cmd, StringComparison.OrdinalIgnoreCase))
					{
						obj = item.Value;
						break;
					}
				}
				if (obj == null)
				{
					return false;
				}
				Type type = obj.GetType();
				string[] array = new string[5] { "IsCheat", "isCheat", "m_isCheat", "Cheat", "cheat" };
				bool flag = default(bool);
				bool flag2 = default(bool);
				foreach (string name in array)
				{
					FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && field.FieldType == typeof(bool))
					{
						object value = field.GetValue(obj);
						int num;
						if (value is bool)
						{
							flag = (bool)value;
							num = 1;
						}
						else
						{
							num = 0;
						}
						return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
					}
					PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.PropertyType == typeof(bool))
					{
						object value = property.GetValue(obj);
						int num2;
						if (value is bool)
						{
							flag2 = (bool)value;
							num2 = 1;
						}
						else
						{
							num2 = 0;
						}
						return (byte)((uint)num2 & (flag2 ? 1u : 0u)) != 0;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool ShouldBlockAnimationCancel(Player p)
		{
			try
			{
				if (!IsActiveMultiplayerClient())
				{
					return false;
				}
				if ((Object)(object)p == (Object)null)
				{
					return false;
				}
				if ((Object)(object)p != (Object)(object)Player.m_localPlayer)
				{
					return false;
				}
				return ((Character)p).InAttack();
			}
			catch
			{
				return false;
			}
		}
	}
}