Decompiled source of CatosMobDropsEditor v0.1.2

CatosMobDropsEditor.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
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("CatosMobDropsEditor")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+0289ac670986326c262dd31306333b216b0ff2db")]
[assembly: AssemblyProduct("CatosMobDropsEditor")]
[assembly: AssemblyTitle("CatosMobDropsEditor")]
[assembly: AssemblyVersion("0.1.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 CatosMobDropsEditor
{
	[BepInPlugin("catos.mobdropseditor", "Cato's Mob Drops Editor", "0.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		private enum DropOperation
		{
			Add,
			Edit,
			Remove,
			Replace,
			Disabled
		}

		private sealed class DropRule
		{
			public string MobPrefab { get; }

			public DropOperation Operation { get; }

			public string DropPrefab { get; }

			public int AmountMin { get; }

			public int AmountMax { get; }

			public float ChancePercent { get; }

			public bool ScaleByLevel { get; }

			public bool OnePerPlayer { get; }

			public DropRule(string mobPrefab, DropOperation operation, string dropPrefab, int amountMin, int amountMax, float chancePercent, bool scaleByLevel, bool onePerPlayer)
			{
				MobPrefab = mobPrefab;
				Operation = operation;
				DropPrefab = dropPrefab;
				AmountMin = amountMin;
				AmountMax = amountMax;
				ChancePercent = chancePercent;
				ScaleByLevel = scaleByLevel;
				OnePerPlayer = onePerPlayer;
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetSceneAwakePatch
		{
			private static void Postfix()
			{
				GenerateItemReferenceFile();
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					LoadAuthorityRules("server config");
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class ZNetAwakePatch
		{
			private static void Prefix()
			{
				_effectiveRules = Array.Empty<DropRule>();
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnDestroy")]
		private static class ZNetOnDestroyPatch
		{
			private static void Postfix()
			{
				_effectiveRules = Array.Empty<DropRule>();
			}
		}

		[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
		private static class CharacterDropGenerateDropListPatch
		{
			private static void Prefix(CharacterDrop __instance, out List<Drop>? __state)
			{
				__state = null;
				if (_effectiveRules.Count == 0)
				{
					return;
				}
				string mobName = ((Object)((Component)__instance).gameObject).name.Replace("(Clone)", string.Empty).Trim();
				if (_effectiveRules.Any((DropRule rule) => rule.MobPrefab.Equals(mobName, StringComparison.OrdinalIgnoreCase)))
				{
					__state = __instance.m_drops;
					__instance.m_drops = ((IEnumerable<Drop>)__state).Select((Func<Drop, Drop>)((Drop drop) => new Drop
					{
						m_prefab = drop.m_prefab,
						m_amountMin = drop.m_amountMin,
						m_amountMax = drop.m_amountMax,
						m_chance = drop.m_chance,
						m_onePerPlayer = drop.m_onePerPlayer,
						m_levelMultiplier = drop.m_levelMultiplier,
						m_dontScale = drop.m_dontScale
					})).ToList();
					ApplyRulesToLiveDropComponent(__instance);
				}
			}

			private static void Postfix(CharacterDrop __instance, List<Drop>? __state)
			{
				if (__state != null)
				{
					__instance.m_drops = __state;
				}
			}

			private static Exception Finalizer(CharacterDrop __instance, List<Drop>? __state, Exception __exception)
			{
				if (__state != null)
				{
					__instance.m_drops = __state;
				}
				return __exception;
			}
		}

		public const string Guid = "catos.mobdropseditor";

		public const string Name = "Cato's Mob Drops Editor";

		public const string Version = "0.1.2";

		private static Plugin? _instance;

		private static ConfigEntry<string>? _overrides;

		private static IReadOnlyList<DropRule> _effectiveRules = Array.Empty<DropRule>();

		private static readonly Harmony Harmony = new Harmony("catos.mobdropseditor");

		private void Awake()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			_instance = this;
			_overrides = ((BaseUnityPlugin)this).Config.Bind<string>("Drops", "Overrides", "", new ConfigDescription("One rule per line: MobPrefab|Operation|DropPrefab|Min|Max|ChancePercent|ScaleByLevel|OnePerPlayer. To disable all drops from a mob, use MobPrefab|Disabled. Blank means vanilla behavior.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				ReadOnly = true
			} }));
			_overrides.SettingChanged += delegate
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					LoadAuthorityRules("server config changed");
				}
			};
			Harmony.PatchAll();
			SynchronizationManager.OnConfigurationSynchronized += OnConfigurationSynchronized;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Cato's Mob Drops Editor 0.1.2 loaded. Client config edits do not activate drop rules in multiplayer.");
		}

		private void OnDestroy()
		{
			SynchronizationManager.OnConfigurationSynchronized -= OnConfigurationSynchronized;
			Harmony.UnpatchSelf();
		}

		private static void OnConfigurationSynchronized(object sender, ConfigurationSynchronizationEventArgs args)
		{
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
			{
				LoadAuthorityRules("server synchronization");
			}
		}

		private static void LoadAuthorityRules(string source)
		{
			if (_overrides != null)
			{
				_effectiveRules = ParseRules(_overrides.Value).ToList();
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)$"Loaded {_effectiveRules.Count} drop rule(s) from {source}.");
			}
		}

		private static void ApplyRule(GameObject mob, CharacterDrop characterDrop, DropRule rule)
		{
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			Drop val = ((IEnumerable<Drop>)characterDrop.m_drops).FirstOrDefault((Func<Drop, bool>)((Drop drop) => (Object)(object)drop.m_prefab != (Object)null && ((Object)drop.m_prefab).name.Equals(rule.DropPrefab, StringComparison.OrdinalIgnoreCase)));
			switch (rule.Operation)
			{
			default:
				return;
			case DropOperation.Disabled:
				characterDrop.m_drops.Clear();
				return;
			case DropOperation.Remove:
				characterDrop.m_drops.RemoveAll((Drop drop) => (Object)(object)drop.m_prefab != (Object)null && ((Object)drop.m_prefab).name.Equals(rule.DropPrefab, StringComparison.OrdinalIgnoreCase));
				return;
			case DropOperation.Replace:
				characterDrop.m_drops.Clear();
				val = null;
				break;
			case DropOperation.Edit:
				if (val == null)
				{
					((BaseUnityPlugin)_instance).Logger.LogWarning((object)("Cannot edit drop '" + rule.DropPrefab + "' on '" + ((Object)mob).name + "': it is not in the vanilla/current drop table."));
				}
				else
				{
					SetDrop(val, rule);
				}
				return;
			case DropOperation.Add:
				break;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(rule.DropPrefab);
			if ((Object)(object)prefab == (Object)null)
			{
				((BaseUnityPlugin)_instance).Logger.LogWarning((object)("Drop prefab '" + rule.DropPrefab + "' was not found; skipping rule for '" + ((Object)mob).name + "'."));
				return;
			}
			if (val != null)
			{
				characterDrop.m_drops.Remove(val);
			}
			Drop val2 = new Drop
			{
				m_prefab = prefab
			};
			SetDrop(val2, rule);
			characterDrop.m_drops.Add(val2);
		}

		private static void ApplyRulesToLiveDropComponent(CharacterDrop characterDrop)
		{
			if (_overrides == null)
			{
				return;
			}
			string mobName = ((Object)((Component)characterDrop).gameObject).name.Replace("(Clone)", string.Empty).Trim();
			List<DropRule> list = _effectiveRules.Where((DropRule rule) => rule.MobPrefab.Equals(mobName, StringComparison.OrdinalIgnoreCase)).ToList();
			foreach (DropRule item in list)
			{
				ApplyRule(((Component)characterDrop).gameObject, characterDrop, item);
			}
			if (list.Count > 0)
			{
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)($"Generating drops for '{mobName}' with {list.Count} configured rule(s): " + string.Join(", ", characterDrop.m_drops.Select((Drop drop) => (!((Object)(object)drop.m_prefab != (Object)null)) ? "<missing>" : ((Object)drop.m_prefab).name))));
			}
		}

		private static void SetDrop(Drop drop, DropRule rule)
		{
			drop.m_amountMin = rule.AmountMin;
			drop.m_amountMax = rule.AmountMax;
			drop.m_chance = rule.ChancePercent / 100f;
			drop.m_levelMultiplier = rule.ScaleByLevel;
			drop.m_onePerPlayer = rule.OnePerPlayer;
		}

		private static IEnumerable<DropRule> ParseRules(string value)
		{
			string[] array = value.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length != 0 && !text2.StartsWith("#") && !text2.StartsWith("//"))
				{
					string[] array2 = (from field in text2.Split(new char[1] { '|' })
						select field.Trim()).ToArray();
					DropOperation result;
					int result2;
					int result3;
					float result4;
					bool result5;
					bool result6;
					if (array2.Length == 2 && array2[1].Equals("Disabled", StringComparison.OrdinalIgnoreCase))
					{
						yield return new DropRule(array2[0], DropOperation.Disabled, string.Empty, 1, 1, 0f, scaleByLevel: false, onePerPlayer: false);
					}
					else if (array2.Length != 8 || !Enum.TryParse<DropOperation>(array2[1], ignoreCase: true, out result) || !int.TryParse(array2[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2) || !int.TryParse(array2[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out result3) || !float.TryParse(array2[5], NumberStyles.Float, CultureInfo.InvariantCulture, out result4) || !bool.TryParse(array2[6], out result5) || !bool.TryParse(array2[7], out result6))
					{
						((BaseUnityPlugin)_instance).Logger.LogWarning((object)("Invalid drop rule (expected 8 pipe-separated fields): " + text));
					}
					else if (result2 < 1 || result3 < result2 || result4 < 0f || result4 > 100f)
					{
						((BaseUnityPlugin)_instance).Logger.LogWarning((object)("Invalid amount/chance in drop rule: " + text));
					}
					else
					{
						yield return new DropRule(array2[0], result, array2[2], result2, result3, result4, result5, result6);
					}
				}
			}
		}

		private static void GenerateItemReferenceFile()
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			try
			{
				var list = (from @group in (from prefab in ZNetScene.instance.m_prefabs
						where (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<ItemDrop>() != (Object)null
						select new
						{
							Prefab = ((Object)prefab).name,
							DisplayName = (prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name ?? string.Empty)
						}).GroupBy(item => item.Prefab, StringComparer.OrdinalIgnoreCase)
					select @group.First()).OrderBy(item => item.Prefab, StringComparer.OrdinalIgnoreCase).ToList();
				List<string> list2 = new List<string> { "# Cato's Mob Drops Editor - generated item prefab reference", "# Use the Prefab ID in drop rules, not the localized display name.", "# Generated from the live Valheim ZNetScene registry, including loaded modded items.", "# Jotunn reference: https://valheim-modding.github.io/Jotunn/data/objects/item-list.html", "#", "# Prefab ID | Display name" };
				list2.AddRange(list.Select(item => item.Prefab + " | " + StripRichText(item.DisplayName)));
				string text = Path.Combine(Paths.ConfigPath, "catos.mobdropseditor.item-prefabs.txt");
				File.WriteAllLines(text, list2);
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)$"Generated item prefab reference with {list.Count} entries: {text}");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)_instance).Logger.LogWarning((object)("Could not generate the item prefab reference: " + ex.Message));
			}
		}

		private static string StripRichText(string value)
		{
			string text = value;
			while (true)
			{
				int num = text.IndexOf('<');
				if (num < 0)
				{
					return text;
				}
				int num2 = text.IndexOf('>', num);
				if (num2 < 0)
				{
					break;
				}
				text = text.Remove(num, num2 - num + 1);
			}
			return text;
		}
	}
}