Decompiled source of JarOfDirt ServerPlugin v1.1.1

plugins/JarOfDirt_ServerPlugin.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("JarOfDirt_ServerPlugin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("JarOfDirt_ServerPlugin")]
[assembly: AssemblyTitle("JarOfDirt_ServerPlugin")]
[assembly: AssemblyVersion("1.0.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 JarOfDirtPlugin
{
	[BepInPlugin("jarofdirt.ServerPlugin", "JarOfDirt Server Plugin", "1.1.1")]
	public class ServerPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "jarofdirt.ServerPlugin";

		public const string PluginName = "JarOfDirt Server Plugin";

		public const string PluginVersion = "1.1.1";

		public const string ModpackHash = "4ef40130e9478ce0";

		private static ManualLogSource _log;

		private static Harmony _harmony;

		private static readonly HashSet<string> BalrondClayPieces = new HashSet<string> { "clay_stair_bal", "ClayBeam_bal", "ClayCube_bal", "ClayPole_bal", "clayr_arch_bal", "ClayWall_bal", "clay_floor_2x2_bal" };

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

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

		private static readonly HashSet<string> BlockedAdmins = new HashSet<string>();

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

		private static readonly HashSet<string> BlockedCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"spawn", "give", "god", "ghost", "heal", "puke", "hair", "beard", "model", "raiseskill",
			"resetskill", "resetcharacter", "event", "stopevent", "randomevent", "exploremap", "resetmap", "goto", "pos", "tame",
			"hatch", "location", "genloc", "setpower", "resetenv", "wind", "resetwind", "freefly", "ffsmooth", "dpsdebug",
			"save"
		};

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			_log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("jarofdirt.ServerPlugin");
			_harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ServerPlugin), "ZNetAwake_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "RPC_PeerInfo", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ServerPlugin), "RPC_PeerInfo_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			MethodInfo methodInfo = AccessTools.Method(typeof(Terminal), "InputText", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ServerPlugin), "Terminal_InputText_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ZNet), "Disconnect", new Type[1] { typeof(ZNetPeer) }, (Type[])null);
			if (methodInfo2 != null)
			{
				_harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(ServerPlugin), "ZNet_Disconnect_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			_harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ServerPlugin), "ZNetScene_Awake_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_log.LogInfo((object)"JarOfDirt Server Plugin v1.1.1 loaded (modpack hash: 4ef40130e9478ce0)");
		}

		private static void ZNetAwake_Postfix(ZNet __instance)
		{
			ValidatedPeers.Clear();
			ModpackVerifiedPeers.Clear();
			BlockedAdmins.Clear();
			PeerSteamIds.Clear();
			ZRoutedRpc.instance.Register<string>("JOD_Validate", (Action<long, string>)RPC_ServerOnValidate);
			bool flag = __instance.IsServer();
			_log.LogInfo((object)$"ZNet initialized (server={flag})");
			if (!flag)
			{
				((MonoBehaviour)__instance).StartCoroutine(ClientSendValidation());
			}
		}

		private static IEnumerator ClientSendValidation()
		{
			for (float waited = 0f; waited < 30f; waited += 1f)
			{
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					yield break;
				}
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null && serverPeer.IsReady())
				{
					break;
				}
				yield return (object)new WaitForSeconds(1f);
			}
			yield return (object)new WaitForSeconds(3f);
			if ((Object)(object)ZNet.instance != (Object)null && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC("JOD_Validate", new object[1] { "4ef40130e9478ce0" });
				_log.LogInfo((object)"Sent JOD_Validate to server (hash: 4ef40130e9478ce0)");
			}
		}

		private static void RPC_ServerOnValidate(long sender, string clientHash)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			ValidatedPeers.Add(sender);
			bool flag = string.Equals(clientHash, "4ef40130e9478ce0", StringComparison.OrdinalIgnoreCase);
			if (flag)
			{
				ModpackVerifiedPeers.Add(sender);
				_log.LogInfo((object)$"Peer {sender} validated - plugin present, modpack hash MATCHES");
			}
			else
			{
				_log.LogWarning((object)string.Format("Peer {0} validated - plugin present, but modpack hash MISMATCH (client: {1}, server: {2})", sender, clientHash, "4ef40130e9478ce0"));
				try
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ShowMessage", new object[2] { 2, "Your modpack is outdated! Please update JarOfDirt_Valheim in your mod manager." });
				}
				catch
				{
				}
				if (PeerSteamIds.TryGetValue(sender, out var value))
				{
					BlockedAdmins.Add(value);
					_log.LogWarning((object)("Admin blocked for " + value + " due to modpack mismatch"));
				}
			}
			if (flag && PeerSteamIds.TryGetValue(sender, out var value2) && BlockedAdmins.Remove(value2))
			{
				_log.LogInfo((object)("Late validation from " + value2 + " - restoring admin access"));
			}
		}

		private static void RPC_PeerInfo_Postfix(ZRpc rpc, ZNet __instance)
		{
			if (!__instance.IsServer())
			{
				return;
			}
			ZNetPeer val = null;
			foreach (ZNetPeer peer in __instance.GetPeers())
			{
				if (peer.m_rpc == rpc)
				{
					val = peer;
					break;
				}
			}
			if (val == null)
			{
				return;
			}
			string hostName = val.m_socket.GetHostName();
			long uid = val.m_uid;
			PeerSteamIds[uid] = hostName;
			_log.LogInfo((object)$"Peer connected: UID={uid}, SteamID={hostName}");
			try
			{
				object value = Traverse.Create((object)__instance).Field("m_adminList").GetValue();
				if (value != null)
				{
					MethodInfo method = value.GetType().GetMethod("Contains", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
					if (method != null && (bool)method.Invoke(value, new object[1] { hostName }))
					{
						_log.LogInfo((object)$"Peer {uid} ({hostName}) is admin - starting validation timer");
						((MonoBehaviour)__instance).StartCoroutine(ValidateAdminPeer(uid, hostName));
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Error checking admin list: " + ex.Message));
			}
		}

		private static IEnumerator ValidateAdminPeer(long uid, string steamId)
		{
			yield return (object)new WaitForSeconds(20f);
			if (!ValidatedPeers.Contains(uid))
			{
				_log.LogWarning((object)$"Admin peer {uid} ({steamId}) has NO JarOfDirt plugin - blocking admin");
				BlockedAdmins.Add(steamId);
				try
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(uid, "ShowMessage", new object[2] { 2, "Admin access requires JarOfDirt modpack. Install the modpack to receive admin." });
					yield break;
				}
				catch
				{
					yield break;
				}
			}
			if (!ModpackVerifiedPeers.Contains(uid))
			{
				_log.LogWarning((object)$"Admin peer {uid} ({steamId}) has outdated modpack - admin blocked");
			}
			else
			{
				_log.LogInfo((object)$"Admin peer {uid} ({steamId}) fully validated - plugin present, modpack current");
			}
		}

		private static void ZNet_Disconnect_Prefix(ZNetPeer peer)
		{
			if (peer != null)
			{
				long uid = peer.m_uid;
				ValidatedPeers.Remove(uid);
				ModpackVerifiedPeers.Remove(uid);
				if (PeerSteamIds.TryGetValue(uid, out var value))
				{
					BlockedAdmins.Remove(value);
					PeerSteamIds.Remove(uid);
				}
			}
		}

		private static bool Terminal_InputText_Prefix(Terminal __instance)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				return true;
			}
			try
			{
				object value = Traverse.Create((object)__instance).Field("m_input").GetValue();
				if (value == null)
				{
					return true;
				}
				PropertyInfo property = value.GetType().GetProperty("text");
				if (property == null)
				{
					return true;
				}
				string text = (string)property.GetValue(value);
				if (string.IsNullOrEmpty(text))
				{
					return true;
				}
				string text2 = text.Trim();
				if (text2.StartsWith("/"))
				{
					text2 = text2.Substring(1);
				}
				text2 = text2.Split(new char[1] { ' ' })[0].ToLower();
				if (BlockedCommands.Contains(text2))
				{
					__instance.AddString("<color=orange>[JarOfDirt]</color> <color=red>Command '" + text2 + "' is restricted by server policy.</color>");
					PropertyInfo property2 = value.GetType().GetProperty("text");
					if (property2 != null)
					{
						property2.SetValue(value, "");
					}
					return false;
				}
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Error in command filter: " + ex.Message));
			}
			return true;
		}

		private static void ZNetScene_Awake_Postfix(ZNetScene __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				((MonoBehaviour)__instance).StartCoroutine(DelayedRemoveBalrondClay(__instance));
			}
		}

		private static IEnumerator DelayedRemoveBalrondClay(ZNetScene scene)
		{
			yield return null;
			yield return null;
			yield return null;
			if ((Object)(object)scene == (Object)null)
			{
				yield break;
			}
			int num = 0;
			int num2 = scene.m_prefabs.RemoveAll((GameObject prefab) => (Object)(object)prefab != (Object)null && BalrondClayPieces.Contains(((Object)prefab).name));
			try
			{
				Dictionary<int, GameObject> value = Traverse.Create((object)scene).Field("m_namedPrefabs").GetValue<Dictionary<int, GameObject>>();
				if (value != null)
				{
					List<int> list = new List<int>();
					foreach (KeyValuePair<int, GameObject> item in value)
					{
						if ((Object)(object)item.Value != (Object)null && BalrondClayPieces.Contains(((Object)item.Value).name))
						{
							list.Add(item.Key);
						}
					}
					foreach (int item2 in list)
					{
						value.Remove(item2);
					}
				}
			}
			catch
			{
			}
			foreach (GameObject prefab in scene.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				ItemDrop component = prefab.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				PieceTable val = component.m_itemData?.m_shared?.m_buildPieces;
				if (!((Object)(object)val == (Object)null))
				{
					int num3 = val.m_pieces.RemoveAll((GameObject piece) => (Object)(object)piece != (Object)null && BalrondClayPieces.Contains(((Object)piece).name));
					num += num3;
				}
			}
			if (num2 > 0 || num > 0)
			{
				_log.LogInfo((object)$"Removed Balrond clay pieces: {num2} from ZNetScene, {num} from PieceTables");
			}
			else
			{
				_log.LogInfo((object)"Balrond clay piece removal: none found (already removed or not loaded)");
			}
		}
	}
}