Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FasterLoot v0.5.0
FasterLoot.dll
Decompiled 8 hours agousing 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; } } }