Decompiled source of Player Heads v420.101.1

PlayerHeads.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PlayerHeads
{
	[BepInPlugin("kg_blackviking.PlayerHeads", "kg_blackviking.PlayerHeads", "1.1.0")]
	public sealed class MultiplayerPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ScenePatch
		{
			private static void Prefix(ZNetScene __instance)
			{
				if (!((Object)(object)headPrefab == (Object)null))
				{
					GameObject val = __instance.m_prefabs.Concat(__instance.m_nonNetViewPrefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == "PlayerHead_IDM"));
					if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)headPrefab)
					{
						headPrefab = null;
						LogError("A different network prefab uses the PlayerHead_IDM name; head spawning disabled.");
					}
					else if ((Object)(object)val == (Object)null)
					{
						__instance.m_prefabs.Add(headPrefab);
					}
				}
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class DatabaseAwakePatch
		{
			private static void Prefix(ObjectDB __instance)
			{
				RegisterItem(__instance, refresh: false);
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class DatabaseCopyPatch
		{
			private static void Postfix(ObjectDB __instance)
			{
				RegisterItem(__instance, refresh: true);
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class DeathPatch
		{
			private static void Prefix(Player __instance)
			{
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0100: Unknown result type (might be due to invalid IL or missing references)
				//IL_0105: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
				//IL_025d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0264: Expected O, but got Unknown
				//IL_0266: Unknown result type (might be due to invalid IL or missing references)
				//IL_028a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0291: Expected O, but got Unknown
				//IL_0293: Unknown result type (might be due to invalid IL or missing references)
				//IL_032e: Unknown result type (might be due to invalid IL or missing references)
				//IL_033c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0368: Unknown result type (might be due to invalid IL or missing references)
				//IL_036d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0371: Unknown result type (might be due to invalid IL or missing references)
				//IL_0384: Unknown result type (might be due to invalid IL or missing references)
				//IL_038b: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)headPrefab == (Object)null || (Object)(object)__instance == (Object)null || LastHitField == null)
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
				{
					return;
				}
				ZDO zDO = component.GetZDO();
				if (zDO == null || zDO.GetBool("PlayerHeads.SpawnedForDeath", false) || zDO.GetBool(ZDOVars.s_dead, false) || ((Character)__instance).GetHealth() > 0f)
				{
					return;
				}
				object? value = LastHitField.GetValue(__instance);
				HitData val = (HitData)((value is HitData) ? value : null);
				Character val2 = ((val == null) ? null : val.GetAttacker());
				if ((Object)(object)val2 == (Object)null || !val2.IsPlayer())
				{
					return;
				}
				GameObject val3 = null;
				try
				{
					if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZNetScene.instance.GetPrefab("PlayerHead_IDM") == (Object)null)
					{
						return;
					}
					zDO.Set("PlayerHeads.SpawnedForDeath", true);
					val3 = Object.Instantiate<GameObject>(headPrefab, ((Component)__instance).transform.position + Vector3.up * 2f, Quaternion.identity);
					ZNetView component2 = val3.GetComponent<ZNetView>();
					ItemDrop component3 = val3.GetComponent<ItemDrop>();
					if ((Object)(object)component2 == (Object)null || !component2.IsValid() || !component2.IsOwner() || (Object)(object)component3 == (Object)null)
					{
						ZNetScene.instance.Destroy(val3);
						val3 = null;
						LogError("Network head creation did not produce an owned ZDO.");
						return;
					}
					ZDO zDO2 = component2.GetZDO();
					string text = __instance.GetPlayerName();
					if (string.IsNullOrWhiteSpace(text) && (Object)(object)Game.instance != (Object)null && Game.instance.GetPlayerProfile() != null)
					{
						text = Game.instance.GetPlayerProfile().GetName();
					}
					text = SafeNick(text);
					string text2 = ((Humanoid)__instance).GetHair() ?? "";
					string text3 = ((Humanoid)__instance).GetBeard() ?? "";
					Vector3 hairColor = __instance.GetHairColor();
					Vector3 vec = zDO.GetVec3(ZDOVars.s_skinColor, Vector3.one);
					component3.m_itemData.m_customData["PlayerHeads.Nick"] = text;
					component3.m_itemData.m_customData["PlayerHeads.PlayerID"] = __instance.GetPlayerID().ToString();
					component3.m_itemData.m_customData["PlayerHeads.Hair"] = text2;
					component3.m_itemData.m_customData["PlayerHeads.Beard"] = text3;
					ZPackage val4 = new ZPackage();
					val4.Write(hairColor);
					component3.m_itemData.m_customData["PlayerHeads.HairColor"] = val4.GetBase64();
					ZPackage val5 = new ZPackage();
					val5.Write(vec);
					component3.m_itemData.m_customData["PlayerHeads.SkinColor"] = val5.GetBase64();
					component3.m_itemData.m_customData["PlayerHeads.Model"] = __instance.GetPlayerModel().ToString();
					ItemDrop.SaveToZDO(component3.m_itemData, zDO2, -1);
					zDO2.Set("PlayerHeads.Nick", text);
					zDO2.Set("PlayerHeads.PlayerID", __instance.GetPlayerID());
					zDO2.Set("PlayerHeads.Hair", text2);
					zDO2.Set("PlayerHeads.Beard", text3);
					zDO2.Set("PlayerHeads.HairColor", hairColor);
					zDO2.Set("PlayerHeads.SkinColor", vec);
					zDO2.Set("PlayerHeads.Model", __instance.GetPlayerModel());
					Rigidbody component4 = val3.GetComponent<Rigidbody>();
					if ((Object)(object)component4 != (Object)null)
					{
						Vector3 insideUnitSphere = Random.insideUnitSphere;
						insideUnitSphere.y = Math.Abs(insideUnitSphere.y);
						component4.AddForce(insideUnitSphere * 5f, (ForceMode)1);
					}
				}
				catch (Exception ex)
				{
					if ((Object)(object)val3 != (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
					{
						ZNetScene.instance.Destroy(val3);
					}
					LogError("Network head creation failed: " + ex);
				}
			}
		}

		[HarmonyPatch(typeof(ItemDrop), "GetHoverName")]
		private static class HoverNamePatch
		{
			private static void Postfix(ItemDrop __instance, ref string __result)
			{
				if (IsHead(__instance))
				{
					__instance.Load();
					string nick = GetNick(__instance.m_itemData);
					if (!string.IsNullOrEmpty(nick))
					{
						__result = SafeNick(nick) + "'s head";
					}
				}
			}
		}

		[HarmonyPatch(typeof(ItemDrop), "GetHoverText")]
		private static class HoverTextPatch
		{
			private static void Postfix(ItemDrop __instance, ref string __result)
			{
				if (IsHead(__instance))
				{
					__instance.Load();
					string nick = GetNick(__instance.m_itemData);
					if (!string.IsNullOrEmpty(nick))
					{
						int num = ((__result == null) ? (-1) : __result.IndexOf('\n'));
						__result = SafeNick(nick) + "'s head" + ((num < 0) ? "" : __result.Substring(num));
					}
				}
			}
		}

		public sealed class HeadAppearance : MonoBehaviour
		{
			private ZNetView view;

			private bool applied;

			private void Awake()
			{
				view = ((Component)this).GetComponent<ZNetView>();
			}

			private void Update()
			{
				//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fe: Expected O, but got Unknown
				//IL_011f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0121: Unknown result type (might be due to invalid IL or missing references)
				//IL_0178: Unknown result type (might be due to invalid IL or missing references)
				//IL_0182: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated())
				{
					applied = true;
				}
				else
				{
					if (applied || (Object)(object)view == (Object)null || !view.IsValid() || (Object)(object)ObjectDB.instance == (Object)null)
					{
						return;
					}
					ItemDrop component = ((Component)this).GetComponent<ItemDrop>();
					if ((Object)(object)component == (Object)null)
					{
						applied = true;
						return;
					}
					component.Load();
					Dictionary<string, string> customData = component.m_itemData.m_customData;
					if (customData == null || string.IsNullOrEmpty(GetNick(component.m_itemData)))
					{
						return;
					}
					customData.TryGetValue("PlayerHeads.Hair", out var value);
					customData.TryGetValue("PlayerHeads.Beard", out var value2);
					customData.TryGetValue("PlayerHeads.HairColor", out var value3);
					customData.TryGetValue("PlayerHeads.SkinColor", out var value4);
					if (value3 != null && value4 != null)
					{
						try
						{
							Vector3 hairColor = new ZPackage(value3).ReadVector3();
							Vector3 val = new ZPackage(value4).ReadVector3();
							MaterialPropertyBlock val2 = new MaterialPropertyBlock();
							Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>();
							foreach (Renderer obj in componentsInChildren)
							{
								obj.GetPropertyBlock(val2);
								val2.SetColor("_SkinColor", Utils.Vec3ToColor(val));
								obj.SetPropertyBlock(val2);
							}
							Transform val3 = FindChild(((Component)this).transform, "head");
							if ((Object)(object)val3 == (Object)null)
							{
								if (!missingVisualReported)
								{
									missingVisualReported = true;
									LogWarning("Head prefab has no head anchor; hairstyle and beard skipped.");
								}
							}
							else
							{
								AttachAppearance(value, val3, hairColor);
								AttachAppearance(value2, val3, hairColor);
							}
							applied = true;
							return;
						}
						catch (Exception ex)
						{
							applied = true;
							LogWarning("Head appearance could not be applied: " + ex.Message);
							return;
						}
					}
					applied = true;
				}
			}

			private static Transform FindChild(Transform root, string name)
			{
				if (string.Equals(((Object)root).name, name, StringComparison.OrdinalIgnoreCase))
				{
					return root;
				}
				for (int i = 0; i < root.childCount; i++)
				{
					Transform val = FindChild(root.GetChild(i), name);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
				return null;
			}

			private static void AttachAppearance(string itemName, Transform anchor, Vector3 hairColor)
			{
				//IL_00a2: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
				//IL_0104: Unknown result type (might be due to invalid IL or missing references)
				//IL_013b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0142: Expected O, but got Unknown
				//IL_0163: Unknown result type (might be due to invalid IL or missing references)
				//IL_0164: Unknown result type (might be due to invalid IL or missing references)
				if (string.IsNullOrEmpty(itemName) || (Object)(object)ObjectDB.instance == (Object)null)
				{
					return;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemName);
				if ((Object)(object)itemPrefab == (Object)null)
				{
					if (!missingAttachmentReported)
					{
						missingAttachmentReported = true;
						LogWarning("A saved hair or beard item is unavailable; its visual was skipped.");
					}
					return;
				}
				Transform val = itemPrefab.transform.Find("attach");
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponentInChildren<ZNetView>() != (Object)null)
				{
					if (!missingAttachmentReported)
					{
						missingAttachmentReported = true;
						LogWarning("A saved hair or beard item has no safe attach visual; its visual was skipped.");
					}
					return;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject);
				val2.transform.SetParent(anchor, false);
				val2.transform.localPosition = Vector3.zero;
				val2.transform.localRotation = Quaternion.identity;
				Transform val3 = itemPrefab.transform.Find("equipoffset");
				if ((Object)(object)val3 != (Object)null)
				{
					Transform transform = val2.transform;
					transform.localPosition += val3.position;
					Transform transform2 = val2.transform;
					transform2.localRotation *= val3.rotation;
				}
				val2.SetActive(true);
				Collider[] componentsInChildren = val2.GetComponentsInChildren<Collider>();
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					componentsInChildren[i].enabled = false;
				}
				MaterialPropertyBlock val4 = new MaterialPropertyBlock();
				Renderer[] componentsInChildren2 = val2.GetComponentsInChildren<Renderer>();
				foreach (Renderer obj in componentsInChildren2)
				{
					obj.GetPropertyBlock(val4);
					val4.SetColor("_SkinColor", Utils.Vec3ToColor(hairColor));
					obj.SetPropertyBlock(val4);
				}
			}
		}

		private const string PrefabName = "PlayerHead_IDM";

		private const string NickKey = "PlayerHeads.Nick";

		private const string LegacyKey = "kg_blackviking.PlayerHeads#PlayerHeads.blackViking_PlayerHeads+PlayerHead";

		private const string PlayerIdKey = "PlayerHeads.PlayerID";

		private const string HairKey = "PlayerHeads.Hair";

		private const string BeardKey = "PlayerHeads.Beard";

		private const string HairColorKey = "PlayerHeads.HairColor";

		private const string SkinColorKey = "PlayerHeads.SkinColor";

		private const string ModelKey = "PlayerHeads.Model";

		private const string SpawnedKey = "PlayerHeads.SpawnedForDeath";

		private static readonly FieldInfo LastHitField = AccessTools.Field(typeof(Character), "m_lastHit");

		private static GameObject headPrefab;

		private static bool missingVisualReported;

		private static bool missingAttachmentReported;

		private static MultiplayerPlugin plugin;

		private void Awake()
		{
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			plugin = this;
			try
			{
				string text = Assembly.GetExecutingAssembly().GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith("kgplayerheads", StringComparison.OrdinalIgnoreCase));
				if (text == null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"Embedded head AssetBundle is missing.");
					return;
				}
				using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text))
				{
					if (stream == null)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)"Cannot open embedded head AssetBundle.");
						return;
					}
					AssetBundle val = AssetBundle.LoadFromStream(stream);
					if ((Object)(object)val == (Object)null)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)"Cannot load head AssetBundle.");
						return;
					}
					headPrefab = val.LoadAsset<GameObject>("PlayerHead_IDM");
				}
				if ((Object)(object)headPrefab == (Object)null || (Object)(object)headPrefab.GetComponent<ItemDrop>() == (Object)null || (Object)(object)headPrefab.GetComponent<ZNetView>() == (Object)null)
				{
					headPrefab = null;
					((BaseUnityPlugin)this).Logger.LogError((object)"Head prefab lacks ItemDrop or ZNetView; network spawning disabled.");
					return;
				}
				headPrefab.GetComponent<ZNetView>().m_persistent = true;
				headPrefab.GetComponent<ItemDrop>().m_autoDestroy = false;
				headPrefab.GetComponent<ItemDrop>().m_autoPickup = false;
				headPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_autoStack = false;
				headPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize = 1;
				headPrefab.AddComponent<HeadAppearance>();
				new Harmony("kg_blackviking.PlayerHeads").PatchAll(typeof(MultiplayerPlugin).Assembly);
			}
			catch (Exception ex)
			{
				headPrefab = null;
				((BaseUnityPlugin)this).Logger.LogError((object)("Head initialization failed: " + ex));
			}
		}

		private static void LogError(string message)
		{
			if ((Object)(object)plugin != (Object)null)
			{
				((BaseUnityPlugin)plugin).Logger.LogError((object)message);
			}
		}

		private static void LogWarning(string message)
		{
			if ((Object)(object)plugin != (Object)null)
			{
				((BaseUnityPlugin)plugin).Logger.LogWarning((object)message);
			}
		}

		private static bool IsHead(ItemDrop drop)
		{
			if ((Object)(object)drop != (Object)null)
			{
				return Utils.GetPrefabName(((Component)drop).gameObject) == "PlayerHead_IDM";
			}
			return false;
		}

		private static string GetNick(ItemData item)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (item == null || item.m_customData == null)
			{
				return null;
			}
			if (item.m_customData.TryGetValue("PlayerHeads.Nick", out var value) && !string.IsNullOrEmpty(value))
			{
				return value;
			}
			if (!item.m_customData.TryGetValue("kg_blackviking.PlayerHeads#PlayerHeads.blackViking_PlayerHeads+PlayerHead", out var value2))
			{
				return null;
			}
			string[] array = value2.Split(new char[1] { '|' });
			foreach (string text in array)
			{
				if (text.StartsWith("Nickname:", StringComparison.Ordinal))
				{
					try
					{
						return new ZPackage(text.Substring("Nickname:".Length)).ReadString();
					}
					catch
					{
						return null;
					}
				}
			}
			return null;
		}

		private static string SafeNick(string nick)
		{
			if (!string.IsNullOrWhiteSpace(nick))
			{
				return nick.Replace("<", "‹").Replace(">", "›");
			}
			return "Unknown";
		}

		private static void RegisterItem(ObjectDB db, bool refresh)
		{
			if ((Object)(object)headPrefab == (Object)null || (Object)(object)db == (Object)null)
			{
				return;
			}
			GameObject val = ((IEnumerable<GameObject>)db.m_items).FirstOrDefault((Func<GameObject, bool>)((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == "PlayerHead_IDM"));
			if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)headPrefab)
			{
				headPrefab = null;
				LogError("A different item uses the PlayerHead_IDM name; head spawning disabled.");
			}
			else
			{
				if ((Object)(object)val != (Object)null)
				{
					return;
				}
				db.m_items.Add(headPrefab);
				if (refresh)
				{
					try
					{
						AccessTools.Method(typeof(ObjectDB), "UpdateRegisters", (Type[])null, (Type[])null).Invoke(db, null);
					}
					catch (Exception ex)
					{
						LogError("Could not refresh item database: " + ex);
					}
				}
			}
		}
	}
}