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 ResourceDropModifier v0.1.0
ResourceDropModifier.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using MushroomMods; using MushroomSync; using SoftReferenceableAssets; 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("ResourceDropModifier")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Per-item drop multipliers for Valheim, one config setting per item grouped by biome, synced from the server.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+7757ccc54aeca6be4b442e05fcdfb2495dea3f50")] [assembly: AssemblyProduct("Resource Drop Modifier")] [assembly: AssemblyTitle("ResourceDropModifier")] [assembly: AssemblyVersion("0.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 MushroomMods { internal static class PatchIsolation { internal static int PatchAllIsolated(Harmony harmony, Assembly assembly, ManualLogSource log) { int num = 0; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly); foreach (Type type in typesFromAssembly) { try { harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { num++; log.LogError((object)("Harmony patch class " + type.FullName + " was skipped and the rest of " + harmony.Id + " is still active. " + ex)); } } return num; } } } namespace ResourceDropModifier { internal static class ConsoleCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__Reload; public static ConsoleEvent <1>__Rescan; public static ConsoleEvent <2>__Show; } private static bool _registered; internal static void Register() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown if (_registered) { return; } _registered = true; try { object obj = <>O.<0>__Reload; if (obj == null) { ConsoleEvent val = Reload; <>O.<0>__Reload = val; obj = (object)val; } new ConsoleCommand("rdm_reload", "re-read the Resource Drop Modifier config from disk and apply it", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>O.<1>__Rescan; if (obj2 == null) { ConsoleEvent val2 = Rescan; <>O.<1>__Rescan = val2; obj2 = (object)val2; } new ConsoleCommand("rdm_rescan", "re-run the Resource Drop Modifier catalog walk and add any new items to the config", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>O.<2>__Show; if (obj3 == null) { ConsoleEvent val3 = Show; <>O.<2>__Show = val3; obj3 = (object)val3; } new ConsoleCommand("rdm_show", "rdm_show <prefab> - print the drop multiplier in force for an item", (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); Plugin.Log.LogInfo((object)"Console commands registered: rdm_reload, rdm_rescan, rdm_show"); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to register Resource Drop Modifier console commands; continuing without them: " + ex.Message)); } } private static void Reload(ConsoleEventArgs args) { Plugin instance = Plugin.Instance; if (!((Object)(object)instance == (Object)null)) { string text = instance.ReloadFromDisk(); Terminal context = args.Context; if (context != null) { context.AddString(text); } } } private static void Rescan(ConsoleEventArgs args) { Plugin instance = Plugin.Instance; if ((Object)(object)instance == (Object)null) { return; } if ((Object)(object)ZoneSystem.instance == (Object)null) { Terminal context = args.Context; if (context != null) { context.AddString("<color=red>No world loaded - join a world first.</color>"); } return; } instance.OnWorldReady(); Terminal context2 = args.Context; if (context2 != null) { context2.AddString("Catalog rebuilt: " + DropConfig.Count + " item setting(s)."); } } private static void Show(ConsoleEventArgs args) { if (args.Args == null || args.Args.Length < 2) { Terminal context = args.Context; if (context != null) { context.AddString("Usage: rdm_show <prefab>"); } return; } string text = args.Args[1]; float multiplier; bool flag = Plugin.Table.TryGet(text, out multiplier); string text2 = ((Plugin.Sync != null && Plugin.Sync.ClientSyncActive) ? "server" : "local"); Terminal context2 = args.Context; if (context2 != null) { context2.AddString(flag ? (text + " = " + multiplier + " (" + text2 + ")") : (text + " has no setting; vanilla amounts (" + text2 + ")")); } } } internal sealed class CatalogEntry { internal string PrefabName; internal string DisplayName; internal Biome Biomes; internal bool Scalable; internal readonly SortedSet<string> Sources = new SortedSet<string>(StringComparer.Ordinal); internal Biome Section { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Biome[] biomeOrder = DropCatalog.BiomeOrder; foreach (Biome val in biomeOrder) { if ((Biomes & val) != 0) { return val; } } return (Biome)0; } } } internal static class DropCatalog { private sealed class Walk { private const int MaxSourcesPerItem = 8; internal readonly Dictionary<string, CatalogEntry> Entries = new Dictionary<string, CatalogEntry>(StringComparer.Ordinal); private readonly ManualLogSource _log; private readonly HashSet<GameObject> _visited = new HashSet<GameObject>(); internal Walk(ManualLogSource log) { _log = log; } internal int SpawnLists() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) int num = 0; SpawnSystemList[] array = Resources.FindObjectsOfTypeAll<SpawnSystemList>(); foreach (SpawnSystemList val in array) { if ((Object)(object)val == (Object)null || val.m_spawners == null) { continue; } foreach (SpawnData spawner in val.m_spawners) { if (spawner != null && !((Object)(object)spawner.m_prefab == (Object)null) && spawner.m_enabled) { num++; Root(spawner.m_prefab, spawner.m_biome); } } } return num; } internal int Vegetation() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) int num = 0; ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || instance.m_vegetation == null) { return 0; } foreach (ZoneVegetation item in instance.m_vegetation) { if (item != null && !((Object)(object)item.m_prefab == (Object)null) && item.m_enable) { num++; Root(item.m_prefab, item.m_biome); } } return num; } internal int Locations() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) int num = 0; ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || instance.m_locations == null) { return 0; } foreach (ZoneLocation location in instance.m_locations) { if (location == null || !location.m_enable) { continue; } bool loadedHere; GameObject val = Acquire(ref location.m_prefab, out loadedHere); if ((Object)(object)val == (Object)null) { continue; } try { num++; Root(val, location.m_biome); } finally { if (loadedHere) { location.m_prefab.Release(); } } } return num; } private GameObject Acquire(ref SoftReference<GameObject> reference, out bool loadedHere) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) loadedHere = false; if (!reference.IsValid) { return null; } if (!reference.IsLoaded) { try { if ((int)reference.Load() != 0) { return null; } } catch (Exception ex) { _log.LogWarning((object)("Drop catalog: could not load " + reference.Name + ": " + ex.Message)); return null; } loadedHere = true; } return reference.Asset; } private void Root(GameObject prefab, Biome biomes) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) _visited.Clear(); Follow(prefab, biomes); } private void Follow(GameObject prefab, Biome biomes) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null || !_visited.Add(prefab)) { return; } try { Collect(prefab, biomes); } catch (Exception ex) { _log.LogWarning((object)("Drop catalog: skipped " + ((Object)prefab).name + ": " + ex.Message)); } } private void Collect(GameObject root, Biome biomes) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_0452: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_04f2: Unknown result type (might be due to invalid IL or missing references) //IL_04b2: Unknown result type (might be due to invalid IL or missing references) CharacterDrop[] componentsInChildren = root.GetComponentsInChildren<CharacterDrop>(true); foreach (CharacterDrop val in componentsInChildren) { foreach (Drop drop in val.m_drops) { Add(drop.m_prefab, biomes, ((Component)val).gameObject, drop.m_dontScale); } } DropOnDestroyed[] componentsInChildren2 = root.GetComponentsInChildren<DropOnDestroyed>(true); foreach (DropOnDestroyed val2 in componentsInChildren2) { AddTable(val2.m_dropWhenDestroyed, biomes, ((Component)val2).gameObject); } TreeBase[] componentsInChildren3 = root.GetComponentsInChildren<TreeBase>(true); foreach (TreeBase val3 in componentsInChildren3) { AddTable(val3.m_dropWhenDestroyed, biomes, ((Component)val3).gameObject); Follow(val3.m_logPrefab, biomes); } TreeLog[] componentsInChildren4 = root.GetComponentsInChildren<TreeLog>(true); foreach (TreeLog val4 in componentsInChildren4) { AddTable(val4.m_dropWhenDestroyed, biomes, ((Component)val4).gameObject); Follow(val4.m_subLogPrefab, biomes); } MineRock[] componentsInChildren5 = root.GetComponentsInChildren<MineRock>(true); foreach (MineRock val5 in componentsInChildren5) { AddTable(val5.m_dropItems, biomes, ((Component)val5).gameObject); } MineRock5[] componentsInChildren6 = root.GetComponentsInChildren<MineRock5>(true); foreach (MineRock5 val6 in componentsInChildren6) { AddTable(val6.m_dropItems, biomes, ((Component)val6).gameObject); } Pickable[] componentsInChildren7 = root.GetComponentsInChildren<Pickable>(true); foreach (Pickable val7 in componentsInChildren7) { Add(val7.m_itemPrefab, biomes, ((Component)val7).gameObject, val7.m_dontScale); AddTable(val7.m_extraDrops, biomes, ((Component)val7).gameObject); } PickableItem[] componentsInChildren8 = root.GetComponentsInChildren<PickableItem>(true); foreach (PickableItem val8 in componentsInChildren8) { if ((Object)(object)val8.m_itemPrefab != (Object)null) { Add(((Component)val8.m_itemPrefab).gameObject, biomes, ((Component)val8).gameObject, dontScale: false); } if (val8.m_randomItemPrefabs == null) { continue; } RandomItem[] randomItemPrefabs = val8.m_randomItemPrefabs; foreach (RandomItem val9 in randomItemPrefabs) { if ((Object)(object)val9.m_itemPrefab != (Object)null) { Add(((Component)val9.m_itemPrefab).gameObject, biomes, ((Component)val8).gameObject, dontScale: false); } } } Container[] componentsInChildren9 = root.GetComponentsInChildren<Container>(true); foreach (Container val10 in componentsInChildren9) { AddTable(val10.m_defaultItems, biomes, ((Component)val10).gameObject); } LootSpawner[] componentsInChildren10 = root.GetComponentsInChildren<LootSpawner>(true); foreach (LootSpawner val11 in componentsInChildren10) { AddTable(val11.m_items, biomes, ((Component)val11).gameObject); } Beehive[] componentsInChildren11 = root.GetComponentsInChildren<Beehive>(true); foreach (Beehive val12 in componentsInChildren11) { if ((Object)(object)val12.m_honeyItem != (Object)null) { Add(((Component)val12.m_honeyItem).gameObject, biomes, ((Component)val12).gameObject, dontScale: false); } } SapCollector[] componentsInChildren12 = root.GetComponentsInChildren<SapCollector>(true); foreach (SapCollector val13 in componentsInChildren12) { if ((Object)(object)val13.m_spawnItem != (Object)null) { Add(((Component)val13.m_spawnItem).gameObject, biomes, ((Component)val13).gameObject, dontScale: false); } } Fish[] componentsInChildren13 = root.GetComponentsInChildren<Fish>(true); foreach (Fish val14 in componentsInChildren13) { AddTable(val14.m_extraDrops, biomes, ((Component)val14).gameObject); } CreatureSpawner[] componentsInChildren14 = root.GetComponentsInChildren<CreatureSpawner>(true); foreach (CreatureSpawner val15 in componentsInChildren14) { Follow(val15.m_creaturePrefab, biomes); } SpawnArea[] componentsInChildren15 = root.GetComponentsInChildren<SpawnArea>(true); foreach (SpawnArea val16 in componentsInChildren15) { if (val16.m_prefabs == null) { continue; } foreach (SpawnData prefab in val16.m_prefabs) { if (prefab != null) { Follow(prefab.m_prefab, biomes); } } } Destructible[] componentsInChildren16 = root.GetComponentsInChildren<Destructible>(true); foreach (Destructible val17 in componentsInChildren16) { Follow(val17.m_spawnWhenDestroyed, biomes); } Growup[] componentsInChildren17 = root.GetComponentsInChildren<Growup>(true); foreach (Growup val18 in componentsInChildren17) { Follow(val18.m_grownPrefab, biomes); if (val18.m_altGrownPrefabs == null) { continue; } foreach (GrownEntry altGrownPrefab in val18.m_altGrownPrefabs) { if (altGrownPrefab != null) { Follow(altGrownPrefab.m_prefab, biomes); } } } DungeonGenerator[] componentsInChildren18 = root.GetComponentsInChildren<DungeonGenerator>(true); foreach (DungeonGenerator generator in componentsInChildren18) { Rooms(generator, biomes); } } private void Rooms(DungeonGenerator generator, Biome biomes) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) List<RoomData> rooms; try { rooms = DungeonDB.GetRooms(); } catch (Exception) { return; } if (rooms == null) { return; } foreach (RoomData item in rooms) { if (item == null || !item.m_enabled || (item.m_theme & generator.m_themes) == 0) { continue; } bool loadedHere; GameObject val = Acquire(ref item.m_prefab, out loadedHere); if ((Object)(object)val == (Object)null) { continue; } try { Follow(val, biomes); } finally { if (loadedHere) { item.m_prefab.Release(); } } } } private void AddTable(DropTable table, Biome biomes, GameObject source) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (table == null || table.m_drops == null) { return; } foreach (DropData drop in table.m_drops) { Add(drop.m_item, biomes, source, drop.m_dontScale); } } private void Add(GameObject prefab, Biome biomes, GameObject source, bool dontScale) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null) { return; } if ((Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null) { Follow(prefab, biomes); return; } if (!Entries.TryGetValue(((Object)prefab).name, out var value)) { value = new CatalogEntry { PrefabName = ((Object)prefab).name }; Entries[((Object)prefab).name] = value; } CatalogEntry catalogEntry = value; catalogEntry.Biomes |= biomes; value.Scalable |= !dontScale; if ((Object)(object)source != (Object)null && value.Sources.Count < 8) { value.Sources.Add(SourceName(source)); } } private static string SourceName(GameObject source) { string text = ((Object)source).name; if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length); } return text.Trim(); } } internal static readonly Biome[] BiomeOrder; internal static List<CatalogEntry> Build(ManualLogSource log) { Walk walk = new Walk(log); Stopwatch stopwatch = Stopwatch.StartNew(); int num = walk.SpawnLists(); int num2 = walk.Vegetation(); int num3 = walk.Locations(); List<CatalogEntry> list = new List<CatalogEntry>(walk.Entries.Values); foreach (CatalogEntry item in list) { item.DisplayName = Localize(item.PrefabName); } log.LogInfo((object)("Drop catalog: " + list.Count + " item(s) from " + num + " spawner(s), " + num2 + " vegetation prefab(s) and " + num3 + " location(s) in " + stopwatch.ElapsedMilliseconds + " ms.")); return list; } private static string Localize(string prefabName) { try { ObjectDB instance = ObjectDB.instance; GameObject val = (((Object)(object)instance != (Object)null) ? instance.GetItemPrefab(prefabName) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); string text = (((Object)(object)val2 != (Object)null && val2.m_itemData != null && val2.m_itemData.m_shared != null) ? val2.m_itemData.m_shared.m_name : null); if (string.IsNullOrEmpty(text)) { return prefabName; } Localization instance2 = Localization.instance; string text2 = ((instance2 != null) ? instance2.Localize(text) : null); if (string.IsNullOrEmpty(text2) || text2.StartsWith("[", StringComparison.Ordinal)) { return text.TrimStart(new char[1] { '$' }); } return text2; } catch (Exception) { return prefabName; } } static DropCatalog() { Biome[] array = new Biome[9]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); BiomeOrder = (Biome[])(object)array; } } internal static class DropConfig { internal const float MaxMultiplier = 10f; private static readonly char[] InvalidKeyChars = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' }; private static readonly Dictionary<string, ConfigEntry<float>> Entries = new Dictionary<string, ConfigEntry<float>>(StringComparer.Ordinal); internal static int Count => Entries.Count; internal unsafe static int BindAll(ConfigFile config, List<CatalogEntry> catalog, ManualLogSource log) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) Dictionary<Biome, List<CatalogEntry>> dictionary = new Dictionary<Biome, List<CatalogEntry>>(); foreach (CatalogEntry item in catalog) { Biome section = item.Section; if ((int)section == 0) { log.LogInfo((object)("Drop catalog: " + item.PrefabName + " has no biome and is not listed.")); continue; } if (!dictionary.TryGetValue(section, out var value)) { value = (dictionary[section] = new List<CatalogEntry>()); } value.Add(item); } int num = 0; bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { Biome[] biomeOrder = DropCatalog.BiomeOrder; for (int i = 0; i < biomeOrder.Length; i++) { Biome key = biomeOrder[i]; if (!dictionary.TryGetValue(key, out var value2)) { continue; } value2.Sort((CatalogEntry a, CatalogEntry b) => string.CompareOrdinal(a.PrefabName, b.PrefabName)); foreach (CatalogEntry item2 in value2) { if (Bind(config, ((object)(*(Biome*)(&key))/*cast due to .constrained prefix*/).ToString(), item2, log)) { num++; } } } } finally { config.SaveOnConfigSet = saveOnConfigSet; } if (num > 0) { config.Save(); } return num; } private static bool Bind(ConfigFile config, string section, CatalogEntry entry, ManualLogSource log) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_007a: Expected O, but got Unknown if (Entries.ContainsKey(entry.PrefabName)) { return false; } if (entry.PrefabName.IndexOfAny(InvalidKeyChars) >= 0) { log.LogWarning((object)("Drop catalog: '" + entry.PrefabName + "' cannot be a config key and has no setting.")); return false; } ConfigEntry<float> value = config.Bind<float>(new ConfigDefinition(section, entry.PrefabName), 1f, new ConfigDescription(Describe(entry), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); Entries[entry.PrefabName] = value; return true; } private static string Describe(CatalogEntry entry) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(entry.DisplayName ?? entry.PrefabName); if (entry.Sources.Count > 0) { stringBuilder.Append(". Drops from: "); stringBuilder.Append(string.Join(", ", entry.Sources)); } stringBuilder.Append(". Vanilla amount times this; 0 removes the drop."); if (!entry.Scalable) { stringBuilder.Append(" Not scalable: vanilla exempts every drop of this item, so this setting does nothing."); } stringBuilder.Append(" While connected to a server, the server's value is the one in force."); return stringBuilder.ToString(); } internal static MultiplierTable BuildTable() { Dictionary<string, float> dictionary = new Dictionary<string, float>(Entries.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, ConfigEntry<float>> entry in Entries) { dictionary[entry.Key] = entry.Value.Value; } return new MultiplierTable(dictionary); } internal static bool IsItemSetting(ConfigEntryBase setting) { if (setting != null && Entries.TryGetValue(setting.Definition.Key, out var value)) { return (object)value == setting; } return false; } } internal static class ItemIndex { private static Dictionary<SharedData, string> _bySharedData = new Dictionary<SharedData, string>(); internal static int Count => _bySharedData.Count; internal static void Rebuild(ObjectDB db) { Dictionary<SharedData, string> dictionary = new Dictionary<SharedData, string>(); if ((Object)(object)db != (Object)null) { foreach (GameObject item in db.m_items) { if (!((Object)(object)item == (Object)null)) { ItemDrop component = item.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null && !dictionary.ContainsKey(component.m_itemData.m_shared)) { dictionary[component.m_itemData.m_shared] = ((Object)item).name; } } } } _bySharedData = dictionary; } internal static string NameOf(ItemData data) { if (data == null) { return null; } if ((Object)(object)data.m_dropPrefab != (Object)null) { return ((Object)data.m_dropPrefab).name; } if (data.m_shared != null && _bySharedData.TryGetValue(data.m_shared, out var value)) { return value; } return null; } } internal sealed class MultiplierSync { private readonly SyncChannel _channel; private readonly ManualLogSource _log; internal MultiplierTable LocalTable = MultiplierTable.Empty; internal bool ClientSyncActive => _channel.ClientSyncActive; internal MultiplierSync(string pluginGuid, string pluginVersion, ManualLogSource log) { _log = log; _channel = SyncChannel.Create(pluginGuid + ".Multipliers", pluginVersion, log); _channel.WritePayload = Write; _channel.ReadPayload = Read; _channel.Cleared = OnCleared; _channel.SendGate = () => Plugin.LockConfiguration == null || Plugin.LockConfiguration.Value; } internal static bool IsServerAuthority() { return SyncChannel.IsServerAuthority(); } internal void Start() { _channel.Start(); } internal void Broadcast() { _channel.Broadcast(); } private void Write(ZPackage pkg) { List<KeyValuePair<string, float>> list = new List<KeyValuePair<string, float>>(); foreach (KeyValuePair<string, float> entry in Plugin.Table.Entries) { if (entry.Value != 1f) { list.Add(entry); } } pkg.Write(list.Count); foreach (KeyValuePair<string, float> item in list) { pkg.Write(item.Key); pkg.Write(item.Value); } } private void Read(ZPackage pkg) { int num = SyncChannel.ReadCount(pkg); Dictionary<string, float> dictionary = new Dictionary<string, float>(num); for (int i = 0; i < num; i++) { string key = pkg.ReadString(); float value = pkg.ReadSingle(); dictionary[key] = value; } Plugin.Table = new MultiplierTable(dictionary); _log.LogInfo((object)("Using " + num + " drop multiplier(s) from the server.")); } private void OnCleared(string reason) { Plugin.Table = LocalTable; _log.LogInfo((object)("Server multipliers dropped (" + reason + "); using the local config.")); } } internal sealed class MultiplierTable { internal static readonly MultiplierTable Empty = new MultiplierTable(new Dictionary<string, float>()); private readonly Dictionary<string, float> _byPrefab; internal int Count => _byPrefab.Count; internal IEnumerable<KeyValuePair<string, float>> Entries => _byPrefab; internal MultiplierTable(Dictionary<string, float> byPrefab) { _byPrefab = new Dictionary<string, float>(byPrefab, StringComparer.Ordinal); } internal float Get(string prefabName) { if (prefabName != null && _byPrefab.TryGetValue(prefabName, out var value)) { return value; } return 1f; } internal bool TryGet(string prefabName, out float multiplier) { multiplier = 1f; if (prefabName != null) { return _byPrefab.TryGetValue(prefabName, out multiplier); } return false; } internal static int Scale(int amount, float multiplier) { if (multiplier == 1f || amount <= 0) { return amount; } if (multiplier <= 0f) { return 0; } double num = (double)amount * (double)multiplier; int num2 = (int)Math.Floor(num); double num3 = num - (double)num2; if (num3 > 0.0 && (double)Random.value < num3) { num2++; } return num2; } } internal static class DropScaling { internal const int MaxObjectsPerDrop = 100; internal const int MaxListEntries = 200; internal static int Apply(ItemData data, int vanilla) { string text = ItemIndex.NameOf(data); if (text == null || !Plugin.Table.TryGet(text, out var multiplier) || multiplier == 1f) { return vanilla; } int num = MultiplierTable.Scale(vanilla, multiplier); int num2 = ((data.m_shared != null && data.m_shared.m_maxStackSize > 1) ? data.m_shared.m_maxStackSize : 100); if (num > num2) { num = num2; } Plugin.Debug("ScaleDrops " + text + ": " + vanilla + " x " + multiplier + " = " + num); return num; } } [HarmonyPatch(typeof(Game))] internal static class Game_ScaleDrops_Patch { [HarmonyPrefix] [HarmonyPatch("ScaleDrops", new Type[] { typeof(GameObject), typeof(int) })] internal static bool RouteSingle(Game __instance, GameObject drop, int amount, ref int __result) { if (Game.m_resourceRate != 1f || (Object)(object)drop == (Object)null) { return true; } ItemDrop component = drop.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { return true; } __result = __instance.ScaleDrops(component.m_itemData, amount); return false; } [HarmonyPrefix] [HarmonyPatch("ScaleDrops", new Type[] { typeof(GameObject), typeof(int), typeof(int) })] internal static bool RouteRange(Game __instance, GameObject drop, int randomMin, int randomMax, ref int __result) { if (Game.m_resourceRate != 1f || (Object)(object)drop == (Object)null) { return true; } ItemDrop component = drop.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { return true; } __result = __instance.ScaleDrops(component.m_itemData, randomMin, randomMax); return false; } [HarmonyPostfix] [HarmonyPatch("ScaleDrops", new Type[] { typeof(ItemData), typeof(int) })] internal static void ScaleSingle(ItemData data, ref int __result) { __result = DropScaling.Apply(data, __result); } [HarmonyPostfix] [HarmonyPatch("ScaleDrops", new Type[] { typeof(ItemData), typeof(int), typeof(int) })] internal static void ScaleRange(ItemData data, ref int __result) { __result = DropScaling.Apply(data, __result); } } [HarmonyPatch(typeof(DropTable), "GetDropList", new Type[] { })] internal static class DropTable_GetDropList_Patch { [HarmonyPostfix] internal static void Rescale(DropTable __instance, ref List<GameObject> __result) { if (__result == null || __result.Count == 0 || Plugin.Table.Count == 0) { return; } List<GameObject> list = new List<GameObject>(); Dictionary<GameObject, int> dictionary = new Dictionary<GameObject, int>(); foreach (GameObject item in __result) { if (!((Object)(object)item == (Object)null)) { if (!dictionary.TryGetValue(item, out var value)) { list.Add(item); } dictionary[item] = value + 1; } } bool flag = false; List<GameObject> list2 = new List<GameObject>(__result.Count); foreach (GameObject item2 in list) { int num = dictionary[item2]; int num2 = num; if (!IsExempt(__instance, item2) && Plugin.Table.TryGet(((Object)item2).name, out var multiplier) && multiplier != 1f) { num2 = MultiplierTable.Scale(num, multiplier); if (num2 > 100) { num2 = 100; } flag = true; Plugin.Debug("GetDropList " + ((Object)item2).name + ": " + num + " x " + multiplier + " = " + num2); } for (int i = 0; i < num2; i++) { if (list2.Count >= 200) { break; } list2.Add(item2); } } if (flag) { __result = list2; } } private static bool IsExempt(DropTable table, GameObject prefab) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) foreach (DropData drop in table.m_drops) { if ((Object)(object)drop.m_item == (Object)(object)prefab) { return drop.m_dontScale; } } return false; } } [HarmonyPatch(typeof(DropTable), "GetDropListItems")] internal static class DropTable_GetDropListItems_Patch { [HarmonyPostfix] internal static void DropEmptyStacks(List<ItemData> __result) { if (__result != null && __result.Count > 0) { __result.RemoveAll((ItemData item) => item == null || item.m_stack <= 0); } } } [HarmonyPatch(typeof(Pickable), "RPC_Pick")] internal static class Pickable_RPC_Pick_Patch { [HarmonyPrefix] internal static void LiftFloor(Pickable __instance, out int __state) { __state = __instance.m_minAmountScaled; if (!__instance.m_dontScale && (Object)(object)__instance.m_itemPrefab != (Object)null && Plugin.Table.TryGet(((Object)__instance.m_itemPrefab).name, out var multiplier) && multiplier < 1f) { __instance.m_minAmountScaled = 0; } } [HarmonyFinalizer] internal static Exception RestoreFloor(Pickable __instance, int __state, Exception __exception) { __instance.m_minAmountScaled = __state; return __exception; } } [HarmonyPatch(typeof(ZoneSystem), "Start")] internal static class ZoneSystem_Start_Patch { [HarmonyPostfix] internal static void BuildCatalog() { Plugin instance = Plugin.Instance; if ((Object)(object)instance != (Object)null) { instance.OnWorldReady(); } } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class Terminal_InitTerminal_Patch { [HarmonyPostfix] internal static void Register() { ConsoleCommands.Register(); } } [BepInPlugin("Gonfreecss.ResourceDropModifier", "ResourceDropModifier", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "Gonfreecss.ResourceDropModifier"; public const string PluginName = "ResourceDropModifier"; public const string PluginVersion = "0.1.0"; private const float RebuildDelaySeconds = 1f; private const double IgnoreOwnWriteSeconds = 2.0; internal static Plugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<bool> LockConfiguration; internal static ConfigEntry<bool> EnableDebugLogging; internal static MultiplierTable Table = MultiplierTable.Empty; internal static MultiplierSync Sync; private Harmony _harmony; private FileSystemWatcher _watcher; private volatile bool _fileChanged; private float _rebuildDue = -1f; private DateTime _lastOwnWrite = DateTime.MinValue; private void Awake() { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; LockConfiguration = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LockConfiguration", true, "If on, the server owns the multipliers and connected clients use the server's values."); EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDebugLogging", false, "Log every scaled drop. Noisy; for checking a multiplier is applied."); ((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged; ((BaseUnityPlugin)this).Config.ConfigReloaded += OnConfigReloaded; StartWatcher(); Sync = new MultiplierSync("Gonfreecss.ResourceDropModifier", "0.1.0", Log); Sync.Start(); _harmony = new Harmony("Gonfreecss.ResourceDropModifier"); int num = PatchIsolation.PatchAllIsolated(_harmony, typeof(Plugin).Assembly, Log); Log.LogInfo((object)("ResourceDropModifier 0.1.0 loaded." + ((num > 0) ? (" " + num + " patch class(es) skipped - see the errors above.") : ""))); } private void OnDestroy() { _watcher?.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { if (_fileChanged) { _fileChanged = false; if ((DateTime.UtcNow - _lastOwnWrite).TotalSeconds > 2.0) { ReloadFromDisk(); } } if (_rebuildDue >= 0f && Time.unscaledTime >= _rebuildDue) { _rebuildDue = -1f; RebuildTable(); } } internal void OnWorldReady() { try { ItemIndex.Rebuild(ObjectDB.instance); List<CatalogEntry> catalog = DropCatalog.Build(Log); _lastOwnWrite = DateTime.UtcNow; int num = DropConfig.BindAll(((BaseUnityPlugin)this).Config, catalog, Log); if (num > 0) { Log.LogInfo((object)("Bound " + num + " item setting(s) in " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + " (" + DropConfig.Count + " total).")); } RebuildTable(); } catch (Exception ex) { Log.LogError((object)("Building the drop catalog failed; multipliers from the last successful build stay in force. " + ex)); } } private void RebuildTable() { MultiplierTable multiplierTable = DropConfig.BuildTable(); Sync.LocalTable = multiplierTable; if (MultiplierSync.IsServerAuthority()) { Table = multiplierTable; Sync.Broadcast(); Debug("Table rebuilt: " + multiplierTable.Count + " entries, " + CountChanged(multiplierTable) + " not at 1; broadcast."); } else if (!Sync.ClientSyncActive) { Table = multiplierTable; Debug("Table rebuilt from local config: " + multiplierTable.Count + " entries (no server table active)."); } } private static int CountChanged(MultiplierTable table) { int num = 0; foreach (KeyValuePair<string, float> entry in table.Entries) { if (entry.Value != 1f) { num++; } } return num; } private void OnSettingChanged(object sender, SettingChangedEventArgs args) { if (args != null && args.ChangedSetting != null && (DropConfig.IsItemSetting(args.ChangedSetting) || (object)args.ChangedSetting == LockConfiguration)) { _lastOwnWrite = DateTime.UtcNow; ScheduleRebuild(); } } private void OnConfigReloaded(object sender, EventArgs args) { ScheduleRebuild(); } private void ScheduleRebuild() { _rebuildDue = Time.unscaledTime + 1f; } internal string ReloadFromDisk() { try { ((BaseUnityPlugin)this).Config.Reload(); return "Reloaded " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + "; applying in " + 1f + "s."; } catch (Exception ex) { Log.LogWarning((object)("Config reload failed; keeping the current values. " + ex.Message)); return "<color=red>Reload failed: " + ex.Message + "</color>"; } } private void StartWatcher() { try { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath); string fileName = Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath); if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName)) { _watcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite) }; _watcher.Changed += delegate { _fileChanged = true; }; _watcher.Created += delegate { _fileChanged = true; }; _watcher.Renamed += delegate { _fileChanged = true; }; _watcher.EnableRaisingEvents = true; } } catch (Exception ex) { Log.LogWarning((object)("Could not watch the config file for changes (use rdm_reload instead): " + ex.Message)); _watcher = null; } } internal static void Debug(string message) { if (EnableDebugLogging != null && EnableDebugLogging.Value) { Log.LogInfo((object)message); } } } }