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> _entries = new List>(); internal ConfigEntry Enabled { get; } internal ConfigEntry SpeedMultiplier { get; } internal ConfigEntry DebugLogging { get; } internal ConfigEntry 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(file.Bind("Loot", "FasterMonsterLoot", true, "Server-owned switch for the ragdoll delay before ordinary hostile monster loot appears."), (ConfigScope)2); SpeedMultiplier = Add(file.Bind("Loot", "SpeedMultiplier", 1.5f, new ConfigDescription("Loot delay speed multiplier. 2 means twice as fast; 1 is vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 10f), Array.Empty())), (ConfigScope)2); CreatureSpeedOverrides = Add(file.Bind("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(file.Bind("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 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 pair in _entries) { yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, pair.Item1.Definition.Section, pair.Item1.Definition.Key, pair.Item2, (Func)(() => 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("Loot", "FasterMonsterLoot", Enabled.Value, (ConfigDescription)null).Value; SpeedMultiplier.Value = val.Bind("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 Add(ConfigEntry entry, ConfigScope scope) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) _entries.Add(Tuple.Create((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 _registrations = new List(); 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[] bytes) => (!LootPolicy.TryDeserialize(bytes, out var policy, out var _)) ? error : string.Empty)), 2, (ushort)1, (ushort)2, (Func)Settings.PolicyBytes, (Action)delegate(byte[] bytes) { LootPolicy.TryDeserialize(bytes, out policy, out error); Policy = policy; }, (Action)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)delegate { HasPolicy = false; }, 0)); _registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)9, (Action)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(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { Character component2 = ((Component)characterDrop).GetComponent(); 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 candidatePaths) { string[] paths = ((candidatePaths == null) ? Array.Empty() : 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 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 Overrides = new Dictionary(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 item in Overrides.OrderBy, string>((KeyValuePair 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; } } }