Decompiled source of ShadowPersonMaterialFix v1.1.0

ShadowPersonMaterialFix/ShadowPersonMaterialFix.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ShadowPersonFix.Patches;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("nezuma")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+6cd044a0222d0f42c1644522c7ed0d07b0228ddd")]
[assembly: AssemblyProduct("ShadowPersonMaterialFix")]
[assembly: AssemblyTitle("ShadowPersonMaterialFix")]
[assembly: AssemblyVersion("1.1.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 ShadowPersonFix
{
	internal static class PluginConstants
	{
		public const string Guid = "ru.nezuma.valheim.shadowpersonfix";

		public const string Name = "ShadowPerson Material Fix";

		public const string Version = "1.0.0";
	}
	internal static class PluginState
	{
		public static Shader Shader { get; set; }
	}
	internal sealed class ShaderFinder
	{
		private const int RequiredPropertyCount = 5;

		private static readonly string[] CandidateNames = new string[8] { "Custom/Creature", "Custom/Player", "Custom/Character", "Custom/Lit", "Custom/Prop", "Custom/StaticRock", "Custom/Vegetation", "Custom/Standard" };

		private static readonly string[] TrackedProperties = new string[7] { "_SkinBumpMap", "_ChestTex", "_ChestBumpMap", "_LegsTex", "_LegsBumpMap", "_ChestMetal", "_LegsMetal" };

		private readonly ManualLogSource _log;

		private bool _warningLogged;

		public ShaderFinder(ManualLogSource log)
		{
			_log = log;
		}

		public Shader Find()
		{
			return FindByName() ?? FindByScan();
		}

		private Shader FindByName()
		{
			string[] candidateNames = CandidateNames;
			foreach (string text in candidateNames)
			{
				try
				{
					Shader val = Shader.Find(text);
					if ((Object)(object)val != (Object)null && HasEnoughProperties(val))
					{
						_log.LogInfo((object)("Найден хороший шейдер через Shader.Find: '" + ((Object)val).name + "'"));
						return val;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private Shader FindByScan()
		{
			Shader val = null;
			int num = 0;
			try
			{
				Shader[] array = Resources.FindObjectsOfTypeAll<Shader>();
				foreach (Shader val2 in array)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						int num2 = CountTrackedProperties(val2);
						if (num2 >= 5 && num2 > num)
						{
							num = num2;
							val = val2;
						}
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Ошибка поиска шейдеров: " + ex.Message));
			}
			if ((Object)(object)val != (Object)null)
			{
				_log.LogInfo((object)$"Найден хороший шейдер сканированием: '{((Object)val).name}' с {num}/7 свойствами");
			}
			else if (!_warningLogged)
			{
				_warningLogged = true;
				_log.LogWarning((object)"Не найден подходящий шейдер. Продолжаю искать...");
			}
			return val;
		}

		private static bool HasEnoughProperties(Shader shader)
		{
			return CountTrackedProperties(shader) >= 5;
		}

		private static int CountTrackedProperties(Shader shader)
		{
			int num = 0;
			try
			{
				string[] trackedProperties = TrackedProperties;
				foreach (string name in trackedProperties)
				{
					if (HasProperty(shader, name))
					{
						num++;
					}
				}
			}
			catch
			{
			}
			return num;
		}

		private static bool HasProperty(Shader shader, string name)
		{
			try
			{
				int propertyCount = shader.GetPropertyCount();
				for (int i = 0; i < propertyCount; i++)
				{
					if (shader.GetPropertyName(i) == name)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}
	}
	[BepInPlugin("ru.nezuma.valheim.shadowpersonfix", "ShadowPerson Material Fix", "1.0.0")]
	public sealed class ShadowPersonFixPlugin : BaseUnityPlugin
	{
		private ShadowPersonMaterialUpdater _materialUpdater;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ShaderFinder finder = new ShaderFinder(Log);
			_materialUpdater = new ShadowPersonMaterialUpdater(Log, finder);
			Harmony val = new Harmony("ru.nezuma.valheim.shadowpersonfix");
			val.PatchAll(typeof(RendererMaterialPatch).Assembly);
			Log.LogInfo((object)"ShadowPerson Material Fix v1.0.0 загружен. Шейдер будет найден при первом обращении.");
		}

		private void Update()
		{
			_materialUpdater.Tick();
		}
	}
	internal sealed class ShadowPersonMaterialUpdater
	{
		private const float SearchInterval = 5f;

		private const string TargetPrefix = "ShadowPerson";

		private readonly ManualLogSource _log;

		private readonly ShaderFinder _finder;

		private readonly List<Material> _fixedMaterials = new List<Material>();

		private float _lastSearchTime;

		public ShadowPersonMaterialUpdater(ManualLogSource log, ShaderFinder finder)
		{
			_log = log;
			_finder = finder;
		}

		public void Tick()
		{
			if ((Object)(object)PluginState.Shader == (Object)null)
			{
				if (Time.time - _lastSearchTime < 5f)
				{
					return;
				}
				_lastSearchTime = Time.time;
				PluginState.Shader = _finder.Find();
				if ((Object)(object)PluginState.Shader == (Object)null)
				{
					return;
				}
			}
			UpdateAllMaterials();
		}

		private void UpdateAllMaterials()
		{
			Material[] array;
			try
			{
				array = Resources.FindObjectsOfTypeAll<Material>();
			}
			catch
			{
				return;
			}
			Material[] array2 = array;
			foreach (Material material in array2)
			{
				TryReplaceShader(material);
			}
		}

		private void TryReplaceShader(Material material)
		{
			if (!ShouldProcess(material))
			{
				return;
			}
			Shader shader = PluginState.Shader;
			if ((Object)(object)shader == (Object)null || (Object)(object)material.shader == (Object)(object)shader)
			{
				return;
			}
			try
			{
				Shader shader2 = material.shader;
				material.shader = shader;
				_fixedMaterials.Add(material);
				_log.LogInfo((object)("Подменил шейдер у '" + ((Object)material).name + "': '" + ((shader2 != null) ? ((Object)shader2).name : null) + "' → '" + ((Object)shader).name + "'"));
			}
			catch
			{
			}
		}

		private bool ShouldProcess(Material material)
		{
			if ((Object)(object)material == (Object)null)
			{
				return false;
			}
			if (string.IsNullOrEmpty(((Object)material).name))
			{
				return false;
			}
			if (_fixedMaterials.Contains(material))
			{
				return false;
			}
			return ((Object)material).name.IndexOf("ShadowPerson", StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
}
namespace ShadowPersonFix.Patches
{
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class RendererMaterialPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Renderer __instance)
		{
			try
			{
				Material sharedMaterial = __instance.sharedMaterial;
				if ((Object)(object)sharedMaterial == (Object)null)
				{
					return;
				}
				string name = ((Object)sharedMaterial).name;
				if (string.IsNullOrEmpty(name) || name.IndexOf("ShadowPerson", StringComparison.OrdinalIgnoreCase) < 0)
				{
					return;
				}
				Shader shader = PluginState.Shader;
				if (!((Object)(object)shader != (Object)null) || !((Object)(object)sharedMaterial.shader != (Object)(object)shader))
				{
					return;
				}
				try
				{
					sharedMaterial.shader = shader;
				}
				catch
				{
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch]
	internal static class VisEquipmentPatch
	{
		private static readonly string[] TargetMethodNames = new string[3] { "UpdateBaseModel", "SetChestEquipped", "SetLegEquipped" };

		[HarmonyTargetMethods]
		public static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = AccessTools.TypeByName("VisEquipment");
			if (type == null)
			{
				yield break;
			}
			string[] targetMethodNames = TargetMethodNames;
			foreach (string name in targetMethodNames)
			{
				MethodInfo method = AccessTools.Method(type, name, (Type[])null, (Type[])null);
				if (method != null)
				{
					yield return method;
				}
			}
		}

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return TextureSetRewriter.Rewrite(instructions);
		}
	}
	internal static class TextureSetRewriter
	{
		private static readonly HashSet<int> GuardedPropertyIds = new HashSet<int>
		{
			Shader.PropertyToID("_SkinBumpMap"),
			Shader.PropertyToID("_ChestTex"),
			Shader.PropertyToID("_ChestBumpMap"),
			Shader.PropertyToID("_LegsTex"),
			Shader.PropertyToID("_LegsBumpMap"),
			Shader.PropertyToID("_ChestMetal"),
			Shader.PropertyToID("_LegsMetal")
		};

		private static readonly MethodInfo SafeByInt = AccessTools.Method(typeof(TextureSetRewriter), "SafeSetTexture", new Type[3]
		{
			typeof(Material),
			typeof(int),
			typeof(Texture)
		}, (Type[])null);

		private static readonly MethodInfo SafeByString = AccessTools.Method(typeof(TextureSetRewriter), "SafeSetTexture", new Type[3]
		{
			typeof(Material),
			typeof(string),
			typeof(Texture)
		}, (Type[])null);

		public static IEnumerable<CodeInstruction> Rewrite(IEnumerable<CodeInstruction> instructions)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				yield return TryRewrite(instruction) ?? instruction;
			}
		}

		private static CodeInstruction TryRewrite(CodeInstruction instruction)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			if (instruction.opcode != OpCodes.Callvirt && instruction.opcode != OpCodes.Call)
			{
				return null;
			}
			if (!(instruction.operand is MethodInfo methodInfo))
			{
				return null;
			}
			if (methodInfo.Name != "SetTexture")
			{
				return null;
			}
			if (methodInfo.DeclaringType?.FullName != "UnityEngine.Material")
			{
				return null;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length != 2)
			{
				return null;
			}
			if (parameters[0].ParameterType == typeof(int) && SafeByInt != null)
			{
				return new CodeInstruction(OpCodes.Call, (object)SafeByInt);
			}
			if (parameters[0].ParameterType == typeof(string) && SafeByString != null)
			{
				return new CodeInstruction(OpCodes.Call, (object)SafeByString);
			}
			return null;
		}

		public static void SafeSetTexture(Material material, int propertyId, Texture texture)
		{
			if ((Object)(object)material == (Object)null || IsGuarded(material, propertyId))
			{
				return;
			}
			try
			{
				material.SetTexture(propertyId, texture);
			}
			catch
			{
			}
		}

		public static void SafeSetTexture(Material material, string propertyName, Texture texture)
		{
			if ((Object)(object)material == (Object)null || string.IsNullOrEmpty(propertyName) || IsShadowPerson(propertyName))
			{
				return;
			}
			try
			{
				material.SetTexture(propertyName, texture);
			}
			catch
			{
			}
		}

		private static bool IsGuarded(Material material, int propertyId)
		{
			return GuardedPropertyIds.Contains(propertyId) && IsShadowPerson(material);
		}

		private static bool IsShadowPerson(Material material)
		{
			return IsShadowPerson((material != null) ? ((Object)material).name : null);
		}

		private static bool IsShadowPerson(string name)
		{
			return !string.IsNullOrEmpty(name) && name.IndexOf("ShadowPerson", StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
}