Decompiled source of FasterLoot v0.5.0

FasterLoot.dll

Decompiled 8 hours 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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.FasterLoot.Core;
using JG224.ModCore.API;
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: AssemblyCompany("FasterLoot")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("FasterLoot")]
[assembly: AssemblyTitle("FasterLoot")]
[assembly: AssemblyVersion("0.5.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 JG224.FasterLoot
{
	internal sealed class FasterLootConfig
	{
		private readonly ConfigFile _file;

		private byte[] _cachedPolicy;

		private readonly List<Tuple<ConfigEntryBase, ConfigScope>> _entries = new List<Tuple<ConfigEntryBase, ConfigScope>>();

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<float> SpeedMultiplier { get; }

		internal ConfigEntry<bool> DebugLogging { get; }

		internal ConfigEntry<string> CreatureSpeedOverrides { get; }

		private FasterLootConfig(ConfigFile file)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			_file = file;
			Enabled = Add<bool>(file.Bind<bool>("Loot", "FasterMonsterLoot", true, "Server-owned switch for the ragdoll delay before ordinary hostile monster loot appears."), (ConfigScope)2);
			SpeedMultiplier = Add<float>(file.Bind<float>("Loot", "SpeedMultiplier", 1.5f, new ConfigDescription("Loot delay speed multiplier. 2 means twice as fast; 1 is vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())), (ConfigScope)2);
			CreatureSpeedOverrides = Add<string>(file.Bind<string>("Loot", "CreatureSpeedOverrides", "", "Server-owned exact prefab=speed pairs, comma separated, e.g. Troll=3,Greydwarf=4. Up to 64 entries; speeds 1–10. Player/boss/tamed exclusions always apply."), (ConfigScope)2);
			DebugLogging = Add<bool>(file.Bind<bool>("Diagnostics", "DebugLogging", false, "Enable verbose FasterLoot diagnostics."), (ConfigScope)5);
		}

		internal byte[] PolicyBytes()
		{
			if (_cachedPolicy != null)
			{
				return _cachedPolicy;
			}
			if (!LootPolicy.TryCreate(Enabled.Value, SpeedMultiplier.Value, CreatureSpeedOverrides.Value, out var policy, out var error))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)(error + " Using the global speed without overrides."));
				}
				LootPolicy.TryCreate(Enabled.Value, SpeedMultiplier.Value, "", out policy, out var _);
			}
			return _cachedPolicy = policy.Serialize();
		}

		internal static FasterLootConfig Load(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			string text = LegacyFasterLootConfigRules.FindImportSource(Directory.GetFiles(configDirectory, "*.cfg", SearchOption.TopDirectoryOnly));
			FasterLootConfig fasterLootConfig = new FasterLootConfig(PluginConfigFiles.Attach(plugin, new ConfigFile(Path.Combine(configDirectory, "jg224.FasterLoot.cfg"), true, plugin.Info.Metadata)));
			if (text != null)
			{
				fasterLootConfig.ImportLegacy(text, log);
			}
			return fasterLootConfig;
		}

		internal IEnumerable<IDisposable> RegisterMetadata(ICoreServices services, ModuleId owner)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			foreach (Tuple<ConfigEntryBase, ConfigScope> pair in _entries)
			{
				yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, pair.Item1.Definition.Section, pair.Item1.Definition.Key, pair.Item2, (Func<string>)(() => Convert.ToString(pair.Item1.BoxedValue, CultureInfo.InvariantCulture)), 1));
			}
		}

		private void ImportLegacy(string path, ManualLogSource log)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			try
			{
				ConfigFile val = new ConfigFile(path, false);
				Enabled.Value = val.Bind<bool>("Loot", "FasterMonsterLoot", Enabled.Value, (ConfigDescription)null).Value;
				SpeedMultiplier.Value = val.Bind<float>("Loot", "SpeedMultiplier", SpeedMultiplier.Value, (ConfigDescription)null).Value;
				_file.Save();
				if (log != null)
				{
					log.LogInfo((object)("Imported Faster Loot settings from " + Path.GetFileName(path) + "."));
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.LogWarning((object)("Could not import GeneralTweaks Faster Loot settings; using defaults. " + ex.Message));
				}
			}
		}

		private ConfigEntry<T> Add<T>(ConfigEntry<T> entry, ConfigScope scope)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_entries.Add(Tuple.Create<ConfigEntryBase, ConfigScope>((ConfigEntryBase)(object)entry, scope));
			entry.SettingChanged += delegate
			{
				_cachedPolicy = null;
			};
			return entry;
		}
	}
	[BepInPlugin("jg224.FasterLoot", "FasterLoot", "0.5.0")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.FasterLoot";

		public const string PluginName = "FasterLoot";

		public const string PluginVersion = "0.5.0";

		public const string ModCoreGuid = "com.jg224.modcore";

		public const string GeneralTweaksGuid = "JG224.GeneralTweaks";

		public const int ProtocolVersion = 2;

		public static readonly ModuleId ModuleId = new ModuleId("fasterloot");

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private Harmony _harmony;

		private AuthoritativePolicyChannel _policyChannel;

		private bool _shutDown;

		internal static LootPolicy Policy { get; private set; }

		internal static bool HasPolicy { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static FasterLootConfig Settings { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = FasterLootConfig.Load((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger);
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before FasterLoot.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				if (TryGetLegacyOwner(out var version))
				{
					string text = "GeneralTweaks " + version?.ToString() + " still contains Faster Loot; FasterLoot is paused to prevent duplicate hooks.";
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)3, text);
					((BaseUnityPlugin)this).Logger.LogWarning((object)(text + " Update GeneralTweaks to 3.0.0 or newer."));
					return;
				}
				_policyChannel = new AuthoritativePolicyChannel(Core, new PolicyDescriptor(ModuleId, 1, 20000, (Func<byte[], string>)((byte[] bytes) => (!LootPolicy.TryDeserialize(bytes, out var policy, out var _)) ? error : string.Empty)), 2, (ushort)1, (ushort)2, (Func<byte[]>)Settings.PolicyBytes, (Action<byte[]>)delegate(byte[] bytes)
				{
					LootPolicy.TryDeserialize(bytes, out policy, out error);
					Policy = policy;
				}, (Action<string>)delegate(string error)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)error);
				});
				_harmony = new Harmony("jg224.FasterLoot");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Network-owner monster loot timing hook ready.");
				Game.isModded = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("FasterLoot 0.5.0 loaded; protocol " + 2 + "."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("FasterLoot failed to initialize safely: " + ex));
				if (Core != null)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void Update()
		{
			AuthoritativePolicyChannel policyChannel = _policyChannel;
			if (policyChannel != null)
			{
				policyChannel.Tick(Time.unscaledTimeAsDouble, Object.op_Implicit((Object)(object)ZNet.instance), Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer());
			}
			AuthoritativePolicyChannel policyChannel2 = _policyChannel;
			HasPolicy = policyChannel2 != null && policyChannel2.HasPolicy;
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void RegisterWithCore()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: 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)
			SemanticVersion val = default(SemanticVersion);
			if (!SemanticVersion.TryParse("0.5.0", ref val))
			{
				throw new InvalidOperationException("FasterLoot has invalid release version metadata.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.FasterLoot", "FasterLoot", val, 2, (ModuleSide)3, (ModuleRequirement)4, 0uL, 1, 1)));
			foreach (IDisposable item in Settings.RegisterMetadata(Core, ModuleId))
			{
				_registrations.Add(item);
			}
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)3, (Action<LifecycleEvent>)delegate
			{
				HasPolicy = false;
			}, 0));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)9, (Action<LifecycleEvent>)delegate
			{
				HasPolicy = false;
			}, 0));
		}

		private static bool TryGetLegacyOwner(out Version version)
		{
			version = null;
			if (!Chainloader.PluginInfos.TryGetValue("JG224.GeneralTweaks", out var value))
			{
				return false;
			}
			version = value.Metadata.Version;
			if (version != null)
			{
				return version.CompareTo(new Version(3, 0, 0)) < 0;
			}
			return false;
		}

		private void Shutdown()
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if (_shutDown)
			{
				return;
			}
			_shutDown = true;
			HasPolicy = false;
			Policy = null;
			AuthoritativePolicyChannel policyChannel = _policyChannel;
			if (policyChannel != null)
			{
				policyChannel.Dispose();
			}
			_policyChannel = null;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			for (int num = _registrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_registrations[num].Dispose();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Registration cleanup failed: " + ex.Message));
					}
				}
			}
			_registrations.Clear();
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			Settings = null;
			Log = null;
		}

		internal static void Debug(string message)
		{
			FasterLootConfig settings = Settings;
			if (settings != null && settings.DebugLogging.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)message);
				}
			}
		}
	}
}
namespace JG224.FasterLoot.Patches
{
	[HarmonyPatch(typeof(Ragdoll), "Setup")]
	internal static class FasterMonsterLootPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Ragdoll __instance, CharacterDrop characterDrop)
		{
			if (!Plugin.HasPolicy)
			{
				return;
			}
			LootPolicy policy = Plugin.Policy;
			if (policy == null || !policy.Enabled || !Object.op_Implicit((Object)(object)__instance) || !Object.op_Implicit((Object)(object)characterDrop))
			{
				return;
			}
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner())
			{
				Character component2 = ((Component)characterDrop).GetComponent<Character>();
				if (IsEligible(component2))
				{
					float num = LootRules.AdjustDelay(__instance.m_ttl, Plugin.Policy.ForCreature(((Object)((Component)component2).gameObject).name));
					((MonoBehaviour)__instance).CancelInvoke("DestroyNow");
					((MonoBehaviour)__instance).InvokeRepeating("DestroyNow", num, 1f);
					Plugin.Debug("Adjusted " + ((Object)component2).name + " loot delay from " + __instance.m_ttl + "s to " + num + "s.");
				}
			}
		}

		private static bool IsEligible(Character character)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			if (!Object.op_Implicit((Object)(object)character) || character.IsPlayer() || character.IsBoss() || character.IsTamed())
			{
				return false;
			}
			if (character.IsMonsterFaction(Time.time))
			{
				return true;
			}
			if ((int)character.GetFaction() == 10 && Object.op_Implicit((Object)(object)character.GetBaseAI()))
			{
				return character.GetBaseAI().IsAggravated();
			}
			return false;
		}
	}
}
namespace JG224.FasterLoot.Core
{
	internal static class LootRules
	{
		internal static float AdjustDelay(float originalSeconds, float multiplier)
		{
			if (float.IsNaN(originalSeconds) || float.IsInfinity(originalSeconds))
			{
				return 0.05f;
			}
			if (float.IsNaN(multiplier) || float.IsInfinity(multiplier))
			{
				multiplier = 1f;
			}
			return Math.Max(0.05f, originalSeconds / Math.Max(1f, multiplier));
		}
	}
	internal static class LegacyFasterLootConfigRules
	{
		internal const string CurrentFileName = "jg224.FasterLoot.cfg";

		internal const string GeneralTweaksFileName = "jg224.GeneralTweaks.cfg";

		internal const string LegacyGeneralTweaksFileName = "JG224.GeneralTweaks.cfg";

		internal static string FindImportSource(IEnumerable<string> candidatePaths)
		{
			string[] paths = ((candidatePaths == null) ? Array.Empty<string>() : candidatePaths.ToArray());
			if (FindExact(paths, "jg224.FasterLoot.cfg") != null)
			{
				return null;
			}
			return FindExact(paths, "jg224.GeneralTweaks.cfg") ?? FindExact(paths, "JG224.GeneralTweaks.cfg");
		}

		private static string FindExact(IEnumerable<string> paths, string fileName)
		{
			return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal));
		}
	}
	internal sealed class LootPolicy
	{
		internal const int MaximumBytes = 20000;

		internal const float DefaultSpeed = 1.5f;

		internal bool Enabled = true;

		internal float Speed = 1.5f;

		internal readonly Dictionary<string, float> Overrides = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		internal float ForCreature(string name)
		{
			string key = (name ?? string.Empty).Replace("(Clone)", string.Empty).Trim();
			if (!Overrides.TryGetValue(key, out var value))
			{
				return Speed;
			}
			return value;
		}

		internal static bool TryCreate(bool enabled, float speed, string overrides, out LootPolicy policy, out string error)
		{
			policy = null;
			error = string.Empty;
			if (!ValidSpeed(speed) || (overrides?.Length ?? 0) > 8192)
			{
				error = "Speed must be 1–10; overrides are limited to 8192 characters.";
				return false;
			}
			LootPolicy lootPolicy = new LootPolicy
			{
				Enabled = enabled,
				Speed = speed
			};
			string[] array = (overrides ?? string.Empty).Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					string[] array2 = text.Split(new char[1] { '=' });
					if (array2.Length != 2 || array2[0].Trim().Length == 0 || array2[0].Trim().Length > 64 || !float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !ValidSpeed(result))
					{
						error = "Use exact prefab=speed entries, separated by commas; speeds must be 1–10.";
						return false;
					}
					lootPolicy.Overrides[array2[0].Trim()] = result;
					if (lootPolicy.Overrides.Count > 64)
					{
						error = "At most 64 creature overrides are supported.";
						return false;
					}
				}
			}
			policy = lootPolicy;
			return true;
		}

		internal byte[] Serialize()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write((byte)1);
			binaryWriter.Write(Enabled);
			binaryWriter.Write(Speed);
			binaryWriter.Write((byte)Overrides.Count);
			foreach (KeyValuePair<string, float> item in Overrides.OrderBy<KeyValuePair<string, float>, string>((KeyValuePair<string, float> pair) => pair.Key, StringComparer.Ordinal))
			{
				binaryWriter.Write(item.Key);
				binaryWriter.Write(item.Value);
			}
			return memoryStream.ToArray();
		}

		internal static bool TryDeserialize(byte[] bytes, out LootPolicy policy, out string error)
		{
			policy = null;
			error = "Invalid loot policy.";
			if (bytes == null || bytes.Length > 20000)
			{
				return false;
			}
			try
			{
				using MemoryStream memoryStream = new MemoryStream(bytes, writable: false);
				using BinaryReader binaryReader = new BinaryReader(memoryStream);
				if (binaryReader.ReadByte() != 1)
				{
					return false;
				}
				LootPolicy lootPolicy = new LootPolicy
				{
					Enabled = binaryReader.ReadBoolean(),
					Speed = binaryReader.ReadSingle()
				};
				int num = binaryReader.ReadByte();
				if (!ValidSpeed(lootPolicy.Speed) || num > 64)
				{
					return false;
				}
				for (int i = 0; i < num; i++)
				{
					string text = binaryReader.ReadString();
					float value = binaryReader.ReadSingle();
					if (text.Length == 0 || text.Length > 64 || !ValidSpeed(value) || lootPolicy.Overrides.ContainsKey(text))
					{
						return false;
					}
					lootPolicy.Overrides.Add(text, value);
				}
				if (memoryStream.Position != memoryStream.Length)
				{
					return false;
				}
				policy = lootPolicy;
				error = string.Empty;
				return true;
			}
			catch (IOException)
			{
				return false;
			}
			catch (ArgumentException)
			{
				return false;
			}
		}

		private static bool ValidSpeed(float value)
		{
			if (!float.IsNaN(value) && value >= 1f)
			{
				return value <= 10f;
			}
			return false;
		}
	}
}