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 WeightsRebalanced v2.1.0
plugins/WeightsRebalanced.dll
Decompiled 2 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalConfig; using LethalConfig.ConfigItems; using LethalConfig.ConfigItems.Options; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using WeightsRebalanced.HotReload; using WeightsRebalanced.ItemDetection; using WeightsRebalanced.Networking; using WeightsRebalanced.Presets; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WeightsRebalanced")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.1.0.0")] [assembly: AssemblyInformationalVersion("2.1.0")] [assembly: AssemblyProduct("WeightsRebalanced")] [assembly: AssemblyTitle("WeightsRebalanced")] [assembly: AssemblyVersion("2.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 WeightsRebalanced { public static class ConfigValidator { public const float MinWeight = 0f; public const float MaxWeight = 500f; public static float ClampWeight(float value, string itemName = null) { float num = Mathf.Clamp(value, 0f, 500f); if (num != value && itemName != null) { LogHelper.LogWarning($"Weight for '{itemName}' clamped from {value:F1} to {num:F1} kg (valid range: {0f}-{500f} kg)"); } return num; } } public enum ItemCategory { Scrap, Equipment, Store, Modded } public static class LethalConfigIntegration { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static GenericButtonHandler <>9__5_0; public static GenericButtonHandler <>9__5_1; public static GenericButtonHandler <>9__5_2; public static GenericButtonHandler <>9__5_3; public static GenericButtonHandler <>9__5_4; internal void <InitializeInternal>b__5_0() { Plugin.ResetAllWeights(); } internal void <InitializeInternal>b__5_1() { DiagnosticCommands.PrintAllItemsWithOrigin(); } internal void <InitializeInternal>b__5_2() { DiagnosticCommands.PrintNetworkStatus(); } internal void <InitializeInternal>b__5_3() { string text = $"Custom_{DateTime.Now:yyyy-MM-dd_HH-mm}"; if (PresetManager.SavePreset(text, "User-created preset")) { LogHelper.LogInfo("Preset saved as '" + text + "'"); } } internal void <InitializeInternal>b__5_4() { Plugin.ResetAllWeights(); } } private static bool? _isAvailable; private static bool _initialized; public static bool IsAvailable { get { bool valueOrDefault = _isAvailable == true; if (!_isAvailable.HasValue) { valueOrDefault = Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig"); _isAvailable = valueOrDefault; } return _isAvailable.Value; } } public static void Initialize() { if (_initialized || !IsAvailable) { if (!IsAvailable) { LogHelper.LogInfo("LethalConfig not found - mod works without the GUI."); } return; } _initialized = true; try { InitializeInternal(); LogHelper.LogInfo("LethalConfig detected - GUI integration enabled."); } catch (Exception ex) { LogHelper.LogWarning("Failed to initialize LethalConfig integration: " + ex.Message); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void InitializeInternal() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Plugin.EnableWeightModifications, new BoolCheckBoxOptions { RequiresRestart = false })); LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem<WeightUnit>(Plugin.DisplayUnit, new EnumDropDownOptions { RequiresRestart = false })); AddMultiplierSlider(Plugin.GlobalMultiplier); AddMultiplierSlider(Plugin.ScrapMultiplier); AddMultiplierSlider(Plugin.EquipmentMultiplier); AddMultiplierSlider(Plugin.StoreMultiplier); AddMultiplierSlider(Plugin.ModdedMultiplier); object obj = <>c.<>9__5_0; if (obj == null) { GenericButtonHandler val = delegate { Plugin.ResetAllWeights(); }; <>c.<>9__5_0 = val; obj = (object)val; } LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("General", "Reset All Weights", "Resets all item weights to their default vanilla values.", "Reset All", (GenericButtonHandler)obj)); object obj2 = <>c.<>9__5_1; if (obj2 == null) { GenericButtonHandler val2 = delegate { DiagnosticCommands.PrintAllItemsWithOrigin(); }; <>c.<>9__5_1 = val2; obj2 = (object)val2; } LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("General", "Diagnostic: Print Item Detection", "Prints all items with detected origin (vanilla/modded), category and weights to the log.", "Print Diagnostics", (GenericButtonHandler)obj2)); object obj3 = <>c.<>9__5_2; if (obj3 == null) { GenericButtonHandler val3 = delegate { DiagnosticCommands.PrintNetworkStatus(); }; <>c.<>9__5_2 = val3; obj3 = (object)val3; } LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("General", "Network Sync Status", "Check if using host's weights or local configuration.", "Check Status", (GenericButtonHandler)obj3)); object obj4 = <>c.<>9__5_3; if (obj4 == null) { GenericButtonHandler val4 = delegate { string text = $"Custom_{DateTime.Now:yyyy-MM-dd_HH-mm}"; if (PresetManager.SavePreset(text, "User-created preset")) { LogHelper.LogInfo("Preset saved as '" + text + "'"); } }; <>c.<>9__5_3 = val4; obj4 = (object)val4; } LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("Presets", "Save Current as Preset", "Save your current weight configuration as a preset named with the current timestamp.", "Save Preset", (GenericButtonHandler)obj4)); object obj5 = <>c.<>9__5_4; if (obj5 == null) { GenericButtonHandler val5 = delegate { Plugin.ResetAllWeights(); }; <>c.<>9__5_4 = val5; obj5 = (object)val5; } LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("Presets", "Load Vanilla Weights", "Reset all items to their default vanilla weights.", "Load Vanilla", (GenericButtonHandler)obj5)); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void AddMultiplierSlider(ConfigEntry<float> entry) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown FloatSliderOptions val = new FloatSliderOptions(); ((BaseRangeOptions<float>)val).Min = 0f; ((BaseRangeOptions<float>)val).Max = 10f; ((BaseOptions)val).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(entry, val)); } public static void RegisterWeightSlider(ConfigEntry<float> configEntry, ItemCategory category) { if (!IsAvailable) { return; } try { RegisterWeightSliderInternal(configEntry); } catch (Exception ex) { LogHelper.LogWarning("Failed to register slider for '" + ((ConfigEntryBase)configEntry).Definition.Key + "': " + ex.Message); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void RegisterWeightSliderInternal(ConfigEntry<float> configEntry) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown FloatSliderOptions val = new FloatSliderOptions(); ((BaseRangeOptions<float>)val).Min = 0f; ((BaseRangeOptions<float>)val).Max = 500f; ((BaseOptions)val).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(configEntry, val)); LogHelper.LogVerbose("Registered LethalConfig slider for '" + ((ConfigEntryBase)configEntry).Definition.Key + "'"); } public static string GetCategorySection(ItemCategory category) { return category switch { ItemCategory.Scrap => "Scrap Items", ItemCategory.Equipment => "Equipment", ItemCategory.Store => "Store Items", ItemCategory.Modded => "Modded Items", _ => "Item Weights", }; } } public static class LogHelper { private static ManualLogSource _logger; private static ConfigEntry<ModLogLevel> _logLevel; public static ModLogLevel CurrentLogLevel => _logLevel?.Value ?? ModLogLevel.Normal; public static void Initialize(ManualLogSource logger, ConfigFile config) { _logger = logger; _logLevel = config.Bind<ModLogLevel>("Logging", "LogLevel", ModLogLevel.Normal, "Logging level:\n- None: No logs\n- Minimal: Only errors and warnings\n- Normal: Basic information (default)\n- Verbose: All details including per-item weight changes"); } public static void LogError(string message) { if (CurrentLogLevel >= ModLogLevel.Minimal) { ManualLogSource logger = _logger; if (logger != null) { logger.LogError((object)message); } } } public static void LogWarning(string message) { if (CurrentLogLevel >= ModLogLevel.Minimal) { ManualLogSource logger = _logger; if (logger != null) { logger.LogWarning((object)message); } } } public static void LogInfo(string message) { if (CurrentLogLevel >= ModLogLevel.Normal) { ManualLogSource logger = _logger; if (logger != null) { logger.LogInfo((object)message); } } } public static void LogDebug(string message) { if (CurrentLogLevel >= ModLogLevel.Verbose) { ManualLogSource logger = _logger; if (logger != null) { logger.LogDebug((object)message); } } } public static void LogVerbose(string message) { if (CurrentLogLevel >= ModLogLevel.Verbose) { ManualLogSource logger = _logger; if (logger != null) { logger.LogInfo((object)("[VERBOSE] " + message)); } } } } public enum ModLogLevel { None, Minimal, Normal, Verbose } [BepInPlugin("com.weights.rebalanced", "Weights Rebalanced", "2.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGUID = "com.weights.rebalanced"; public const string ModName = "Weights Rebalanced"; public const string ModVersion = "2.1.0"; private Harmony _harmony; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static ConfigFile ConfigInstance { get; private set; } public static Dictionary<string, ConfigEntry<float>> ItemWeights { get; } = new Dictionary<string, ConfigEntry<float>>(); public static Dictionary<string, float> DefaultItemWeightsKg { get; } = new Dictionary<string, float>(); public static Dictionary<string, float> CurrentItemWeightsKg { get; } = new Dictionary<string, float>(); public static Dictionary<string, string> ConfigKeyToItemName { get; } = new Dictionary<string, string>(); public static Dictionary<string, ItemCategory> ItemCategories { get; } = new Dictionary<string, ItemCategory>(); public static ConfigEntry<bool> EnableWeightModifications { get; private set; } public static ConfigEntry<WeightUnit> DisplayUnit { get; private set; } public static ConfigEntry<float> GlobalMultiplier { get; private set; } public static ConfigEntry<float> ScrapMultiplier { get; private set; } public static ConfigEntry<float> EquipmentMultiplier { get; private set; } public static ConfigEntry<float> StoreMultiplier { get; private set; } public static ConfigEntry<float> ModdedMultiplier { get; private set; } private void Awake() { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigInstance = ((BaseUnityPlugin)this).Config; LogHelper.Initialize(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config); ItemClassifier.Initialize(); PresetManager.Initialize(); EnableWeightModifications = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Weight Modifications", true, "Global toggle to enable or disable all weight modifications. Set to false to use vanilla weights."); DisplayUnit = ((BaseUnityPlugin)this).Config.Bind<WeightUnit>("General", "Display Unit", WeightUnit.Kilograms, "Unit used by the HUD weight counter: Kilograms, Pounds (vanilla) or Both."); GlobalMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Multipliers", "Global Weight Multiplier", 1f, new ConfigDescription("Multiplies the weight of ALL items on top of their configured values.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); ScrapMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Multipliers", "Scrap Multiplier", 1f, new ConfigDescription("Extra multiplier applied to scrap items.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); EquipmentMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Multipliers", "Equipment Multiplier", 1f, new ConfigDescription("Extra multiplier applied to equipment.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); StoreMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Multipliers", "Store Items Multiplier", 1f, new ConfigDescription("Extra multiplier applied to store items.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); ModdedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Multipliers", "Modded Items Multiplier", 1f, new ConfigDescription("Extra multiplier applied to modded items.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); LogHelper.LogInfo("Weights Rebalanced v2.1.0 is loading..."); _harmony = new Harmony("com.weights.rebalanced"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); LethalConfigIntegration.Initialize(); ConfigWatcher.Initialize(); LogHelper.LogInfo(string.Format("{0} loaded successfully! (log level: {1}, modifications: {2})", "Weights Rebalanced", LogHelper.CurrentLogLevel, EnableWeightModifications.Value ? "enabled" : "disabled")); } private static string SanitizeConfigKey(string name) { if (string.IsNullOrEmpty(name)) { return "Unknown"; } string input = Regex.Replace(name, "[\\n\\t\\\\\"\\'\\[\\]]", ""); input = Regex.Replace(input, "\\s+", " ").Trim(); if (string.IsNullOrWhiteSpace(input)) { return $"Item_{name.GetHashCode():X8}"; } return input; } public static float GetEffectiveWeightKg(string itemName, float vanillaKg, ItemCategory category) { vanillaKg = (float)Math.Round(vanillaKg, 2); if (!DefaultItemWeightsKg.ContainsKey(itemName)) { DefaultItemWeightsKg[itemName] = vanillaKg; } ItemCategories[itemName] = category; EnsureConfigEntry(itemName, vanillaKg, category); if (WeightSyncManager.TryGetSyncedWeight(itemName, out var weightKg)) { return weightKg; } if (!EnableWeightModifications.Value) { return vanillaKg; } return ConfigValidator.ClampWeight(ConfigValidator.ClampWeight(ItemWeights[itemName].Value, itemName) * GlobalMultiplier.Value * GetCategoryMultiplier(category)); } private static void EnsureConfigEntry(string itemName, float vanillaKg, ItemCategory category) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown if (!ItemWeights.ContainsKey(itemName)) { string text = SanitizeConfigKey(itemName); string categorySection = LethalConfigIntegration.GetCategorySection(category); ItemWeights[itemName] = ConfigInstance.Bind<float>(categorySection, text, vanillaKg, new ConfigDescription($"Weight of '{text}' in kilograms.\nVanilla weight: {vanillaKg:F1} kg\nValid range: {0f}-{500f} kg", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>())); ConfigKeyToItemName[text] = itemName; LethalConfigIntegration.RegisterWeightSlider(ItemWeights[itemName], category); } } public static float GetCategoryMultiplier(ItemCategory category) { return category switch { ItemCategory.Scrap => ScrapMultiplier.Value, ItemCategory.Equipment => EquipmentMultiplier.Value, ItemCategory.Store => StoreMultiplier.Value, ItemCategory.Modded => ModdedMultiplier.Value, _ => 1f, }; } public static void ResetAllWeights() { int num = 0; ConfigWatcher.Suppress = true; try { foreach (KeyValuePair<string, ConfigEntry<float>> itemWeight in ItemWeights) { if (DefaultItemWeightsKg.TryGetValue(itemWeight.Key, out var value)) { itemWeight.Value.Value = value; num++; } } } finally { ConfigWatcher.Suppress = false; } WeightApplier.ApplyAllItems(); WeightSyncManager.BroadcastFullSyncIfHost(); LogHelper.LogInfo($"Reset {num} item weights to vanilla values."); } } public static class WeightApplier { private static readonly Dictionary<int, float> _originalGameWeights = new Dictionary<int, float>(); public static void ApplyAllItems() { if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.allItemsList == (Object)null) { return; } Dictionary<Item, float> dictionary = null; int num = 0; foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { float num2 = (((Object)(object)items != (Object)null) ? items.weight : 0f); if (!ApplyToItem(items)) { continue; } num++; if (!Mathf.Approximately(num2, items.weight)) { if (dictionary == null) { dictionary = new Dictionary<Item, float>(); } dictionary[items] = items.weight - num2; } } if (dictionary != null) { AdjustCarriedWeights(dictionary); } LogHelper.LogDebug($"Applied weights to {num} items ({dictionary?.Count ?? 0} changed)."); } public static bool ApplyToItem(Item item) { if ((Object)(object)item == (Object)null || string.IsNullOrEmpty(item.itemName)) { return false; } int instanceID = ((Object)item).GetInstanceID(); if (!_originalGameWeights.TryGetValue(instanceID, out var value)) { value = item.weight; _originalGameWeights[instanceID] = value; } float num = WeightMath.GameWeightToKg(value); ItemCategory itemCategory = CategoryDetector.DetermineCategory(item); float effectiveWeightKg = Plugin.GetEffectiveWeightKg(item.itemName, num, itemCategory); float num2 = WeightMath.KgToGameWeight(effectiveWeightKg); if (!Mathf.Approximately(item.weight, num2)) { LogHelper.LogVerbose($"[{itemCategory}] '{item.itemName}': {WeightMath.GameWeightToKg(item.weight):F2} kg -> {effectiveWeightKg:F2} kg (vanilla {num:F2} kg)"); item.weight = num2; } Plugin.CurrentItemWeightsKg[item.itemName] = effectiveWeightKg; return true; } private static void AdjustCarriedWeights(Dictionary<Item, float> weightDeltas) { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val == (Object)null || val.ItemSlots == null) { continue; } float num = 0f; GrabbableObject[] itemSlots = val.ItemSlots; foreach (GrabbableObject val2 in itemSlots) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.itemProperties != (Object)null && weightDeltas.TryGetValue(val2.itemProperties, out var value)) { num += value; } } if (num != 0f) { val.carryWeight = Mathf.Max(1f, val.carryWeight + num); LogHelper.LogDebug($"Adjusted carry weight of '{val.playerUsername}' by {num:F3} (held items changed)."); } } } public static bool TryGetVanillaKg(Item item, out float vanillaKg) { if ((Object)(object)item != (Object)null && _originalGameWeights.TryGetValue(((Object)item).GetInstanceID(), out var value)) { vanillaKg = WeightMath.GameWeightToKg(value); return true; } vanillaKg = 0f; return false; } } public static class WeightMath { public const float LbPerGameWeight = 105f; public const float KgPerLb = 0.45359236f; public static float GameWeightToKg(float gameWeight) { return Mathf.Max(0f, gameWeight - 1f) * 105f * 0.45359236f; } public static float KgToGameWeight(float kg) { return 1f + Mathf.Max(0f, kg) / 0.45359236f / 105f; } public static float GameWeightToLb(float gameWeight) { return Mathf.Max(0f, gameWeight - 1f) * 105f; } } public enum WeightUnit { Kilograms, Pounds, Both } } namespace WeightsRebalanced.Presets { public static class PresetManager { private static string _presetsDirectory; public static void Initialize() { _presetsDirectory = Path.Combine(Paths.ConfigPath, "WeightsRebalanced", "Presets"); if (!Directory.Exists(_presetsDirectory)) { Directory.CreateDirectory(_presetsDirectory); LogHelper.LogInfo("Created presets directory: " + _presetsDirectory); } } public static bool SavePreset(string presetName, string description = "") { try { WeightPreset weightPreset = new WeightPreset { PresetName = presetName, Description = description }; foreach (KeyValuePair<string, ConfigEntry<float>> itemWeight in Plugin.ItemWeights) { weightPreset.ItemWeights[itemWeight.Key] = itemWeight.Value.Value; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Preset: " + weightPreset.PresetName); stringBuilder.AppendLine("# Description: " + weightPreset.Description); stringBuilder.AppendLine("# Created: " + weightPreset.CreatedDate); stringBuilder.AppendLine("# ModVersion: " + weightPreset.ModVersion); stringBuilder.AppendLine(); foreach (KeyValuePair<string, float> itemWeight2 in weightPreset.ItemWeights) { stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}={1:F1}", itemWeight2.Key, itemWeight2.Value)); } string text = Path.Combine(_presetsDirectory, SanitizeFileName(presetName) + ".txt"); File.WriteAllText(text, stringBuilder.ToString()); LogHelper.LogInfo($"Saved preset '{presetName}' with {weightPreset.ItemWeights.Count} items to {text}"); return true; } catch (Exception ex) { LogHelper.LogError("Failed to save preset '" + presetName + "': " + ex.Message); return false; } } public static bool LoadPreset(string presetName) { try { string text = Path.Combine(_presetsDirectory, SanitizeFileName(presetName) + ".txt"); if (!File.Exists(text)) { LogHelper.LogWarning("Preset file not found: " + text); return false; } Dictionary<string, float> dictionary = new Dictionary<string, float>(); string[] array = File.ReadAllLines(text); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("#")) { string[] array2 = text2.Split('='); if (array2.Length == 2 && float.TryParse(array2[1].Trim().Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { dictionary[array2[0].Trim()] = result; } } } if (dictionary.Count == 0) { LogHelper.LogError("Invalid preset file: " + text); return false; } ApplyPreset(dictionary); LogHelper.LogInfo($"Loaded preset '{presetName}' with {dictionary.Count} items."); return true; } catch (Exception ex) { LogHelper.LogError("Failed to load preset '" + presetName + "': " + ex.Message); return false; } } private static void ApplyPreset(Dictionary<string, float> weights) { int num = 0; int num2 = 0; ConfigWatcher.Suppress = true; try { foreach (KeyValuePair<string, float> weight in weights) { float value = ConfigValidator.ClampWeight(weight.Value, weight.Key); if (Plugin.ItemWeights.TryGetValue(weight.Key, out var value2)) { value2.Value = value; num++; } else { num2++; } } } finally { ConfigWatcher.Suppress = false; } WeightApplier.ApplyAllItems(); WeightSyncManager.BroadcastFullSyncIfHost(); LogHelper.LogInfo($"Applied preset: {num} items updated, {num2} items not currently loaded."); } public static List<string> GetAvailablePresets() { List<string> list = new List<string>(); try { if (Directory.Exists(_presetsDirectory)) { string[] files = Directory.GetFiles(_presetsDirectory, "*.txt"); foreach (string path in files) { list.Add(Path.GetFileNameWithoutExtension(path)); } } } catch (Exception ex) { LogHelper.LogError("Failed to list presets: " + ex.Message); } return list; } private static string SanitizeFileName(string fileName) { char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { fileName = fileName.Replace(oldChar, '_'); } return fileName; } } [Serializable] public class WeightPreset { public string PresetName { get; set; } public string Description { get; set; } public string CreatedDate { get; set; } public string ModVersion { get; set; } public Dictionary<string, float> ItemWeights { get; set; } = new Dictionary<string, float>(); public WeightPreset() { CreatedDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); ModVersion = "2.1.0"; } } } namespace WeightsRebalanced.Patches { [HarmonyPatch] internal static class ItemSpawnPatches { [HarmonyPatch(typeof(StartOfRound), "Awake")] [HarmonyPostfix] private static void StartOfRound_Awake_Postfix() { CategoryDetector.CacheStoreItems(); ItemClassifier.RefreshModdedItems(); WeightApplier.ApplyAllItems(); LogSummary(); } [HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")] [HarmonyPrefix] private static void SpawnScrapInLevel_Prefix() { WeightApplier.ApplyAllItems(); } private static void LogSummary() { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; using (Dictionary<string, ItemCategory>.ValueCollection.Enumerator enumerator = Plugin.ItemCategories.Values.GetEnumerator()) { while (enumerator.MoveNext()) { switch (enumerator.Current) { case ItemCategory.Scrap: num++; break; case ItemCategory.Equipment: num2++; break; case ItemCategory.Store: num3++; break; case ItemCategory.Modded: num4++; break; } } } LogHelper.LogInfo($"Weights applied: {num} scrap, {num2} equipment, {num3} store, {num4} modded items."); } } [HarmonyPatch] internal static class NetworkPatches { [HarmonyPatch(typeof(NetworkManager), "StartHost")] [HarmonyPostfix] private static void StartHost_Postfix() { WeightSyncManager.OnSessionStarted(); } [HarmonyPatch(typeof(NetworkManager), "StartClient")] [HarmonyPostfix] private static void StartClient_Postfix() { WeightSyncManager.OnSessionStarted(); } [HarmonyPatch(typeof(NetworkManager), "Shutdown")] [HarmonyPrefix] private static void Shutdown_Prefix() { WeightSyncManager.OnSessionShutdown(); } } [HarmonyPatch] internal static class WeightPatches { [HarmonyPatch(typeof(HUDManager), "UpdateWeightCounter")] [HarmonyPostfix] private static void UpdateWeightCounter_Postfix(HUDManager __instance) { if (Plugin.DisplayUnit.Value != WeightUnit.Pounds && !((Object)(object)__instance.weightCounter == (Object)null)) { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)val == (Object)null)) { float num = Mathf.Clamp(val.carryWeight - 1f, 0f, 100f) * 105f; float num2 = num * 0.45359236f; ((TMP_Text)__instance.weightCounter).text = ((Plugin.DisplayUnit.Value == WeightUnit.Both) ? $"{num2:F1} kg ({Mathf.RoundToInt(num)} lb)" : $"{num2:F1} kg"); } } } } } namespace WeightsRebalanced.Networking { public static class WeightSyncManager { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnReceiveMessage; public static Action<ulong> <1>__OnClientConnected; } private const string MessageName = "WeightsRebalanced_SyncWeights"; private const byte FullSyncMessage = 1; private const byte SingleWeightMessage = 2; private static readonly Dictionary<string, float> _syncedOverrides = new Dictionary<string, float>(); private static bool _clientConnectedHooked; public static bool HasOverrides => _syncedOverrides.Count > 0; public static bool TryGetSyncedWeight(string itemName, out float weightKg) { return _syncedOverrides.TryGetValue(itemName, out weightKg); } public static void OnSessionStarted() { //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_003d: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return; } try { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; if (customMessagingManager != null) { object obj = <>O.<0>__OnReceiveMessage; if (obj == null) { HandleNamedMessageDelegate val = OnReceiveMessage; <>O.<0>__OnReceiveMessage = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("WeightsRebalanced_SyncWeights", (HandleNamedMessageDelegate)obj); } LogHelper.LogInfo("Weight sync message handler registered for this session."); } catch (Exception ex) { LogHelper.LogError("Failed to register weight sync handler: " + ex.Message); } if (singleton.IsHost && !_clientConnectedHooked) { singleton.OnClientConnectedCallback += OnClientConnected; _clientConnectedHooked = true; } } public static void OnSessionShutdown() { NetworkManager singleton = NetworkManager.Singleton; if (_clientConnectedHooked && (Object)(object)singleton != (Object)null) { singleton.OnClientConnectedCallback -= OnClientConnected; _clientConnectedHooked = false; } if (_syncedOverrides.Count > 0) { LogHelper.LogInfo($"Disconnected - dropping {_syncedOverrides.Count} synced weights, restoring local configuration."); _syncedOverrides.Clear(); WeightApplier.ApplyAllItems(); } } private static void OnClientConnected(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsHost && clientId != singleton.LocalClientId) { LogHelper.LogInfo($"Client {clientId} connected - sending weight sync."); SendMessage(SerializeFullSync(), clientId); } } public static void BroadcastFullSyncIfHost() { if (IsHost()) { SendMessageToAllClients(SerializeFullSync()); } } public static void BroadcastSingleWeightIfHost(string itemName, float weightKg) { if (IsHost()) { SendMessageToAllClients(SerializeSingleWeight(itemName, weightKg)); } } private static void SendMessageToAllClients(byte[] payload) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return; } foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId) { SendMessage(payload, connectedClientsId); } } } private unsafe static void SendMessage(byte[] payload, ulong clientId) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) try { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(payload.Length + 4, (Allocator)2, -1); try { int num = payload.Length; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteBytesSafe(payload, -1, 0); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("WeightsRebalanced_SyncWeights", clientId, val, (NetworkDelivery)4); LogHelper.LogDebug($"Sent {payload.Length} bytes of weight data to client {clientId}."); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } catch (Exception ex) { LogHelper.LogError($"Failed to send weight sync to client {clientId}: {ex.Message}"); } } private static void OnReceiveMessage(ulong senderId, FastBufferReader reader) { //IL_000f: 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) if (IsHost()) { return; } try { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); byte[] payload = new byte[num]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref payload, num, 0); DeserializeAndApply(payload); } catch (Exception ex) { LogHelper.LogError("Failed to process weight sync: " + ex.Message); } } private static byte[] SerializeFullSync() { using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write((byte)1); binaryWriter.Write("2.1.0"); binaryWriter.Write(Plugin.CurrentItemWeightsKg.Count); foreach (KeyValuePair<string, float> item in Plugin.CurrentItemWeightsKg) { binaryWriter.Write(item.Key); binaryWriter.Write(item.Value); } return memoryStream.ToArray(); } private static byte[] SerializeSingleWeight(string itemName, float weightKg) { using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write((byte)2); binaryWriter.Write(itemName); binaryWriter.Write(weightKg); return memoryStream.ToArray(); } private static void DeserializeAndApply(byte[] payload) { using MemoryStream input = new MemoryStream(payload); using BinaryReader binaryReader = new BinaryReader(input); switch (binaryReader.ReadByte()) { case 1: { string arg = binaryReader.ReadString(); int num2 = binaryReader.ReadInt32(); LogHelper.LogInfo($"Receiving full weight sync from host (mod version {arg}, {num2} items)."); for (int i = 0; i < num2; i++) { string key = binaryReader.ReadString(); float value = binaryReader.ReadSingle(); _syncedOverrides[key] = ConfigValidator.ClampWeight(value); } WeightApplier.ApplyAllItems(); LogHelper.LogInfo($"Applied {num2} synced weights (local config file untouched)."); break; } case 2: { string text = binaryReader.ReadString(); float num = ConfigValidator.ClampWeight(binaryReader.ReadSingle()); _syncedOverrides[text] = num; WeightApplier.ApplyAllItems(); LogHelper.LogDebug($"Applied synced weight update: '{text}' = {num:F1} kg."); break; } } } public static bool IsHost() { if ((Object)(object)NetworkManager.Singleton != (Object)null) { return NetworkManager.Singleton.IsHost; } return false; } public static bool IsSyncedClient() { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsClient) { return !singleton.IsHost; } return false; } } } namespace WeightsRebalanced.ItemDetection { public static class CategoryDetector { private static readonly HashSet<string> _storeItemNames = new HashSet<string>(); public static ItemCategory DetermineCategory(Item item) { if ((Object)(object)item == (Object)null) { return ItemCategory.Scrap; } if (ItemClassifier.GetItemOrigin(item) == ItemOrigin.Modded) { return ItemCategory.Modded; } if (item.isScrap) { return ItemCategory.Scrap; } if (_storeItemNames.Contains(item.itemName)) { return ItemCategory.Store; } return ItemCategory.Equipment; } public static void CacheStoreItems() { Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val == (Object)null || val.buyableItemsList == null) { return; } _storeItemNames.Clear(); Item[] buyableItemsList = val.buyableItemsList; foreach (Item val2 in buyableItemsList) { if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(val2.itemName)) { _storeItemNames.Add(val2.itemName); } } LogHelper.LogDebug($"Cached {_storeItemNames.Count} store items."); } } public static class DiagnosticCommands { public static void PrintAllItemsWithOrigin() { LogHelper.LogInfo("=== Item Detection Diagnostics ==="); LogHelper.LogInfo(ItemClassifier.GetDiagnostics()); if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.allItemsList == (Object)null) { LogHelper.LogInfo("Item list not available yet (start or join a game first)."); return; } foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { if (!((Object)(object)items == (Object)null) && !string.IsNullOrEmpty(items.itemName)) { ItemOrigin itemOrigin = ItemClassifier.GetItemOrigin(items); ItemCategory itemCategory = CategoryDetector.DetermineCategory(items); float vanillaKg; string text = (WeightApplier.TryGetVanillaKg(items, out vanillaKg) ? $"{vanillaKg:F1} kg" : "?"); float num = WeightMath.GameWeightToKg(items.weight); LogHelper.LogInfo($"[{itemOrigin}/{itemCategory}] '{items.itemName}': vanilla {text}, current {num:F1} kg"); } } LogHelper.LogInfo("=== End of diagnostics ==="); } public static void PrintNetworkStatus() { if (WeightSyncManager.IsSyncedClient()) { LogHelper.LogInfo(WeightSyncManager.HasOverrides ? "Using HOST'S weight configuration (synced from server)." : "Connected as client, but no sync received yet - using LOCAL configuration."); } else if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost) { LogHelper.LogInfo("You are the HOST - your weights are synced to all clients."); } else { LogHelper.LogInfo("Not in multiplayer - using your LOCAL weight configuration."); } } } public static class ItemClassifier { private static readonly HashSet<string> _moddedItemNames = new HashSet<string>(); private static bool _lethalLibChecked; private static Type _lethalLibItems; public static void Initialize() { LogHelper.LogDebug("Item classifier initialized (LethalLib lookup happens lazily)."); } public static void RefreshModdedItems() { if (!_lethalLibChecked) { _lethalLibChecked = true; _lethalLibItems = Type.GetType("LethalLib.Modules.Items, LethalLib"); LogHelper.LogInfo((_lethalLibItems != null) ? "LethalLib detected - modded items will be classified via its registration lists." : "LethalLib not present - all items will be treated as vanilla unless registered later."); } if (_lethalLibItems == null) { return; } _moddedItemNames.Clear(); string[] array = new string[3] { "scrapItems", "shopItems", "plainItems" }; foreach (string text in array) { try { FieldInfo field = _lethalLibItems.GetField(text, BindingFlags.Static | BindingFlags.Public); if (field == null || !(field.GetValue(null) is IEnumerable enumerable)) { continue; } foreach (object item in enumerable) { if (item != null) { object? obj = item.GetType().GetField("item", BindingFlags.Instance | BindingFlags.Public)?.GetValue(item); Item val = (Item)((obj is Item) ? obj : null); if (val != null && !string.IsNullOrEmpty(val.itemName)) { _moddedItemNames.Add(val.itemName); } } } } catch (Exception ex) { LogHelper.LogDebug("Failed to read LethalLib list '" + text + "': " + ex.Message); } } LogHelper.LogInfo($"Detected {_moddedItemNames.Count} modded items via LethalLib."); } public static ItemOrigin GetItemOrigin(Item item) { if ((Object)(object)item == (Object)null || string.IsNullOrEmpty(item.itemName)) { return ItemOrigin.Unknown; } if (!_moddedItemNames.Contains(item.itemName)) { return ItemOrigin.Vanilla; } return ItemOrigin.Modded; } public static string GetDiagnostics() { return $"LethalLib present: {_lethalLibItems != null}\nKnown modded items: {_moddedItemNames.Count}"; } } public enum ItemOrigin { Unknown, Vanilla, Modded } } namespace WeightsRebalanced.HotReload { public static class ConfigWatcher { private static bool _initialized; public static bool Suppress { get; set; } public static void Initialize() { if (!_initialized) { _initialized = true; Plugin.ConfigInstance.SettingChanged += OnConfigChanged; LogHelper.LogDebug("Config hot reload watcher initialized."); } } private static void OnConfigChanged(object sender, SettingChangedEventArgs e) { if (Suppress) { return; } string section = e.ChangedSetting.Definition.Section; string key = e.ChangedSetting.Definition.Key; if (Plugin.ConfigKeyToItemName.TryGetValue(key, out var value) && IsWeightSection(section)) { LogHelper.LogDebug($"Config changed: '{value}' = {(float)e.ChangedSetting.BoxedValue:F1} kg"); WeightApplier.ApplyAllItems(); if (Plugin.CurrentItemWeightsKg.TryGetValue(value, out var value2)) { WeightSyncManager.BroadcastSingleWeightIfHost(value, value2); } } else if (section == "Multipliers" || key == "Enable Weight Modifications") { LogHelper.LogDebug("Config changed: " + section + "/" + key + " - reapplying all weights."); WeightApplier.ApplyAllItems(); WeightSyncManager.BroadcastFullSyncIfHost(); } } private static bool IsWeightSection(string section) { switch (section) { default: return section == "Item Weights"; case "Scrap Items": case "Equipment": case "Store Items": case "Modded Items": return true; } } } }