using 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.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
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 UnityEngine.UI;
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("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("WeightsRebalanced")]
[assembly: AssemblyTitle("WeightsRebalanced")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 = 0.1f;
public const float MaxWeight = 500f;
public static float ClampWeight(float value, string itemName = null)
{
float num = Mathf.Clamp(value, 0.1f, 500f);
if (num != value && itemName != null)
{
LogHelper.LogWarning($"Weight for '{itemName}' clamped from {value:F1} to {num:F1} kg (valid range: {0.1f}-{500f} kg)");
}
return num;
}
public static bool IsValidWeight(float value)
{
if (value >= 0.1f)
{
return value <= 500f;
}
return false;
}
}
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__6_0;
public static GenericButtonHandler <>9__6_1;
public static GenericButtonHandler <>9__6_2;
public static GenericButtonHandler <>9__6_3;
public static GenericButtonHandler <>9__6_4;
internal void <InitializeInternal>b__6_0()
{
Plugin.ResetAllWeights();
}
internal void <InitializeInternal>b__6_1()
{
DiagnosticCommands.PrintAllItemsWithOrigin();
}
internal void <InitializeInternal>b__6_2()
{
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__6_3()
{
Plugin.ResetAllWeights();
LogHelper.LogInfo("Loaded vanilla weight preset");
}
internal void <InitializeInternal>b__6_4()
{
if (WeightSyncManager.IsClient())
{
LogHelper.LogInfo("Using HOST'S weight configuration (synced from server)");
return;
}
NetworkManager singleton = NetworkManager.Singleton;
if (singleton != null && 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");
}
}
}
private static bool? _isAvailable;
private static bool _initialized;
public static bool IsAvailable
{
get
{
if (!_isAvailable.HasValue)
{
_isAvailable = CheckLethalConfigAvailable();
}
return _isAvailable.Value;
}
}
private static bool CheckLethalConfigAvailable()
{
try
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
if (assemblies[i].GetName().Name == "LethalConfig")
{
LogHelper.LogInfo("LethalConfig detected - GUI integration enabled.");
return true;
}
}
}
catch (Exception ex)
{
LogHelper.LogWarning("Error checking for LethalConfig: " + ex.Message);
}
LogHelper.LogInfo("LethalConfig not found - mod will work without GUI.");
return false;
}
public static void Initialize()
{
if (_initialized || !IsAvailable)
{
return;
}
_initialized = true;
try
{
InitializeInternal();
}
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_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Expected O, but got Unknown
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Expected O, but got Unknown
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Expected O, but got Unknown
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Expected O, but got Unknown
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00d2: Expected O, but got Unknown
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Expected O, but got Unknown
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Expected O, but got Unknown
//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Expected O, but got Unknown
//IL_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_014c: Expected O, but got Unknown
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0142: Expected O, but got Unknown
LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Plugin.EnableWeightModifications, new BoolCheckBoxOptions
{
RequiresRestart = false
}));
object obj = <>c.<>9__6_0;
if (obj == null)
{
GenericButtonHandler val = delegate
{
Plugin.ResetAllWeights();
};
<>c.<>9__6_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__6_1;
if (obj2 == null)
{
GenericButtonHandler val2 = delegate
{
DiagnosticCommands.PrintAllItemsWithOrigin();
};
<>c.<>9__6_1 = val2;
obj2 = (object)val2;
}
LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("General", "Diagnostic: Print Item Detection", "Prints all items with detected origin (vanilla/modded) and category to log.", "Print Diagnostics", (GenericButtonHandler)obj2));
object obj3 = <>c.<>9__6_2;
if (obj3 == null)
{
GenericButtonHandler val3 = 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__6_2 = val3;
obj3 = (object)val3;
}
LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("Presets", "Save Current as Preset", "Save your current weight configuration as a preset. The preset will be named with current timestamp.", "Save Preset", (GenericButtonHandler)obj3));
object obj4 = <>c.<>9__6_3;
if (obj4 == null)
{
GenericButtonHandler val4 = delegate
{
Plugin.ResetAllWeights();
LogHelper.LogInfo("Loaded vanilla weight preset");
};
<>c.<>9__6_3 = val4;
obj4 = (object)val4;
}
LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("Presets", "Load Vanilla Weights", "Reset all items to their default vanilla weights.", "Load Vanilla", (GenericButtonHandler)obj4));
object obj5 = <>c.<>9__6_4;
if (obj5 == null)
{
GenericButtonHandler val5 = delegate
{
if (WeightSyncManager.IsClient())
{
LogHelper.LogInfo("Using HOST'S weight configuration (synced from server)");
}
else
{
NetworkManager singleton = NetworkManager.Singleton;
if (singleton != null && 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");
}
}
};
<>c.<>9__6_4 = val5;
obj5 = (object)val5;
}
LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("General", "Network Sync Status", "Check if using host's weights or local configuration.", "Check Status", (GenericButtonHandler)obj5));
LogHelper.LogDebug("LethalConfig integration fully initialized with all features.");
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void RegisterWeightSlider(ConfigEntry<float> configEntry, ItemCategory category, float minValue = 0.1f, float maxValue = 500f)
{
if (!IsAvailable)
{
return;
}
try
{
RegisterWeightSliderInternal(configEntry, category, minValue, maxValue);
}
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, ItemCategory category, float minValue, float maxValue)
{
//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_000d: Expected O, but got Unknown
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Expected O, but got Unknown
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Expected O, but got Unknown
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Expected O, but got Unknown
FloatSliderOptions val = new FloatSliderOptions();
((BaseRangeOptions<float>)val).Min = minValue;
((BaseRangeOptions<float>)val).Max = maxValue;
((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 enum ModLogLevel
{
None,
Minimal,
Normal,
Verbose
}
public static class LogHelper
{
private static ManualLogSource _logger;
private static ConfigEntry<ModLogLevel> _logLevel;
private static ConfigEntry<bool> _enableFileLogging;
private static string _logDirectory;
private static string _currentLogFile;
private static readonly object _fileLock = new object();
private const int MaxLogFiles = 5;
private const long MaxLogFileSize = 1048576L;
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");
_enableFileLogging = config.Bind<bool>("Logging", "Enable File Logging", false, "Enable logging to file. Logs are saved in BepInEx/plugins/WeightsRebalanced/logs/");
_logDirectory = Path.Combine(Paths.PluginPath, "WeightsRebalanced", "logs");
if (_enableFileLogging.Value)
{
InitializeFileLogging();
}
}
private static void InitializeFileLogging()
{
try
{
if (!Directory.Exists(_logDirectory))
{
Directory.CreateDirectory(_logDirectory);
}
string text = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
_currentLogFile = Path.Combine(_logDirectory, "WeightsRebalanced_" + text + ".log");
RotateLogs();
WriteToFile($"=== WeightsRebalanced Log Started at {DateTime.Now:yyyy-MM-dd HH:mm:ss} ===");
}
catch (Exception ex)
{
ManualLogSource logger = _logger;
if (logger != null)
{
logger.LogWarning((object)("Failed to initialize file logging: " + ex.Message));
}
}
}
private static void RotateLogs()
{
try
{
string[] files = Directory.GetFiles(_logDirectory, "WeightsRebalanced_*.log");
if (files.Length >= 5)
{
Array.Sort(files, (string a, string b) => File.GetCreationTime(a).CompareTo(File.GetCreationTime(b)));
int num = files.Length - 5 + 1;
for (int i = 0; i < num; i++)
{
File.Delete(files[i]);
}
}
}
catch (Exception ex)
{
ManualLogSource logger = _logger;
if (logger != null)
{
logger.LogWarning((object)("Failed to rotate log files: " + ex.Message));
}
}
}
private static void WriteToFile(string message)
{
ConfigEntry<bool> enableFileLogging = _enableFileLogging;
if (enableFileLogging == null || !enableFileLogging.Value || string.IsNullOrEmpty(_currentLogFile))
{
return;
}
lock (_fileLock)
{
try
{
if (File.Exists(_currentLogFile) && new FileInfo(_currentLogFile).Length >= 1048576)
{
InitializeFileLogging();
}
string text = DateTime.Now.ToString("HH:mm:ss.fff");
File.AppendAllText(_currentLogFile, "[" + text + "] " + message + Environment.NewLine);
}
catch
{
}
}
}
public static void LogError(string message)
{
if (CurrentLogLevel >= ModLogLevel.Minimal)
{
ManualLogSource logger = _logger;
if (logger != null)
{
logger.LogError((object)message);
}
WriteToFile("[ERROR] " + message);
}
}
public static void LogWarning(string message)
{
if (CurrentLogLevel >= ModLogLevel.Minimal)
{
ManualLogSource logger = _logger;
if (logger != null)
{
logger.LogWarning((object)message);
}
WriteToFile("[WARN] " + message);
}
}
public static void LogInfo(string message)
{
if (CurrentLogLevel >= ModLogLevel.Normal)
{
ManualLogSource logger = _logger;
if (logger != null)
{
logger.LogInfo((object)message);
}
WriteToFile("[INFO] " + message);
}
}
public static void LogDebug(string message)
{
if (CurrentLogLevel >= ModLogLevel.Verbose)
{
ManualLogSource logger = _logger;
if (logger != null)
{
logger.LogDebug((object)message);
}
WriteToFile("[DEBUG] " + message);
}
}
public static void LogVerbose(string message)
{
if (CurrentLogLevel >= ModLogLevel.Verbose)
{
ManualLogSource logger = _logger;
if (logger != null)
{
logger.LogInfo((object)("[VERBOSE] " + message));
}
WriteToFile("[VERBOSE] " + message);
}
}
}
[BepInPlugin("com.weights.rebalanced", "Weights Rebalanced", "2.0.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.0.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; private set; } = new Dictionary<string, ConfigEntry<float>>();
public static Dictionary<string, float> DefaultItemWeights { get; private set; } = new Dictionary<string, float>();
public static Dictionary<string, float> CurrentItemWeightsKg { get; private set; } = new Dictionary<string, float>();
public static ConfigEntry<bool> EnableWeightModifications { get; private set; }
private void Awake()
{
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: 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 = ConfigInstance.Bind<bool>("General", "Enable Weight Modifications", true, "Global toggle to enable or disable all weight modifications. Set to false to use vanilla weights.");
LogHelper.LogInfo("Weights Rebalanced v2.0.0 is loading...");
_harmony = new Harmony("com.weights.rebalanced");
_harmony.PatchAll(Assembly.GetExecutingAssembly());
LethalConfigIntegration.Initialize();
ConfigWatcher.Initialize();
LogHelper.LogInfo("Weights Rebalanced loaded successfully!");
LogHelper.LogInfo($"Log level: {LogHelper.CurrentLogLevel}");
LogHelper.LogInfo("Weight modifications: " + (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))
{
LogHelper.LogWarning("Item '" + name + "' has no valid characters, using hash as config key.");
return $"Item_{name.GetHashCode():X8}";
}
if (input != name)
{
LogHelper.LogDebug("Item name sanitized: '" + name + "' -> '" + input + "'");
}
return input;
}
public static float GetItemWeight(string itemName, float defaultWeight, ItemCategory category = ItemCategory.Scrap)
{
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Expected O, but got Unknown
defaultWeight = (float)Math.Round(defaultWeight, 1);
if (!DefaultItemWeights.ContainsKey(itemName))
{
DefaultItemWeights[itemName] = defaultWeight;
}
if (!EnableWeightModifications.Value)
{
CurrentItemWeightsKg[itemName] = defaultWeight;
return defaultWeight;
}
string text = SanitizeConfigKey(itemName);
if (!ItemWeights.ContainsKey(itemName))
{
string categorySection = LethalConfigIntegration.GetCategorySection(category);
ItemWeights[itemName] = ConfigInstance.Bind<float>(categorySection, text, defaultWeight, new ConfigDescription("Weight of '" + text + "' in kilograms.\n" + $"Vanilla weight: {defaultWeight:F1} kg\n" + $"Valid range: {0.1f}-{500f} kg", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 500f), Array.Empty<object>()));
LethalConfigIntegration.RegisterWeightSlider(ItemWeights[itemName], category);
}
float value = ItemWeights[itemName].Value;
value = ConfigValidator.ClampWeight(value, itemName);
if (value != ItemWeights[itemName].Value)
{
ItemWeights[itemName].Value = value;
}
CurrentItemWeightsKg[itemName] = value;
return value;
}
public static void ResetAllWeights()
{
int num = 0;
foreach (KeyValuePair<string, ConfigEntry<float>> itemWeight in ItemWeights)
{
if (DefaultItemWeights.TryGetValue(itemWeight.Key, out var value))
{
itemWeight.Value.Value = value;
CurrentItemWeightsKg[itemWeight.Key] = value;
num++;
}
}
LogHelper.LogInfo($"Reset {num} item weights to default values.");
}
public static void ResetItemWeight(string itemName)
{
if (ItemWeights.TryGetValue(itemName, out var value) && DefaultItemWeights.TryGetValue(itemName, out var value2))
{
value.Value = value2;
CurrentItemWeightsKg[itemName] = value2;
LogHelper.LogDebug($"Reset '{itemName}' weight to {value2} kg.");
}
}
public static float GameWeightToKg(float gameWeight)
{
return (gameWeight - 1f) * 100f * 0.453592f;
}
public static float KgToGameWeight(float kg)
{
float num = kg / 0.453592f;
return 1f + num / 100f;
}
}
public static class SafetyHelpers
{
public static bool ValidateItem(Item item, out string errorMessage)
{
errorMessage = null;
if ((Object)(object)item == (Object)null)
{
errorMessage = "Item is null";
return false;
}
if (string.IsNullOrEmpty(item.itemName))
{
errorMessage = $"Item has no name (InstanceID: {((Object)item).GetInstanceID()})";
return false;
}
if (item.weight < 0f)
{
errorMessage = $"Item '{item.itemName}' has negative weight: {item.weight}";
return false;
}
return true;
}
public static T SafeExecute<T>(Func<T> action, T defaultValue, string context)
{
try
{
return action();
}
catch (Exception ex)
{
LogHelper.LogError("Error in " + context + ": " + ex.Message + "\n" + ex.StackTrace);
return defaultValue;
}
}
public static void SafeExecute(Action action, string context)
{
try
{
action();
}
catch (Exception ex)
{
LogHelper.LogError("Error in " + context + ": " + ex.Message + "\n" + ex.StackTrace);
}
}
}
}
namespace WeightsRebalanced.Presets
{
[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.0.0";
}
}
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;
}
string path = SanitizeFileName(presetName) + ".txt";
string text = Path.Combine(_presetsDirectory, path);
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($"{itemWeight2.Key}={itemWeight2.Value:F1}");
}
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, bool applyImmediately = true)
{
try
{
string path = SanitizeFileName(presetName) + ".txt";
string text = Path.Combine(_presetsDirectory, path);
if (!File.Exists(text))
{
LogHelper.LogWarning("Preset file not found: " + text);
return false;
}
WeightPreset weightPreset = new WeightPreset
{
PresetName = presetName
};
string[] array = File.ReadAllLines(text);
foreach (string text2 in array)
{
if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#"))
{
continue;
}
string[] array2 = text2.Split('=');
if (array2.Length == 2)
{
string key = array2[0].Trim();
if (float.TryParse(array2[1].Trim(), out var result))
{
weightPreset.ItemWeights[key] = result;
}
}
}
if (weightPreset.ItemWeights.Count == 0)
{
LogHelper.LogError("Invalid preset file: " + text);
return false;
}
if (applyImmediately)
{
ApplyPreset(weightPreset);
}
LogHelper.LogInfo($"Loaded preset '{weightPreset.PresetName}' with {weightPreset.ItemWeights.Count} items");
return true;
}
catch (Exception ex)
{
LogHelper.LogError("Failed to load preset '" + presetName + "': " + ex.Message);
return false;
}
}
private static void ApplyPreset(WeightPreset preset)
{
int num = 0;
int num2 = 0;
foreach (KeyValuePair<string, float> itemWeight in preset.ItemWeights)
{
string key = itemWeight.Key;
float value = ConfigValidator.ClampWeight(itemWeight.Value, key);
if (Plugin.ItemWeights.TryGetValue(key, out var value2))
{
value2.Value = value;
Plugin.CurrentItemWeightsKg[key] = value;
num++;
}
else
{
num2++;
}
}
LogHelper.LogInfo($"Applied preset: {num} items updated, {num2} items not yet loaded");
}
public static List<string> GetAvailablePresets()
{
List<string> list = new List<string>();
try
{
if (Directory.Exists(_presetsDirectory))
{
string[] files = Directory.GetFiles(_presetsDirectory, "*.txt");
for (int i = 0; i < files.Length; i++)
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[i]);
list.Add(fileNameWithoutExtension);
}
}
}
catch (Exception ex)
{
LogHelper.LogError("Failed to get preset list: " + ex.Message);
}
return list;
}
public static bool ExportPreset(string presetName, string destinationPath)
{
try
{
string path = SanitizeFileName(presetName) + ".txt";
string text = Path.Combine(_presetsDirectory, path);
if (!File.Exists(text))
{
LogHelper.LogWarning("Preset not found: " + presetName);
return false;
}
File.Copy(text, destinationPath, overwrite: true);
LogHelper.LogInfo("Exported preset to " + destinationPath);
return true;
}
catch (Exception ex)
{
LogHelper.LogError("Failed to export preset: " + ex.Message);
return false;
}
}
public static bool ImportPreset(string sourcePath)
{
try
{
if (!File.Exists(sourcePath))
{
LogHelper.LogWarning("Import file not found: " + sourcePath);
return false;
}
string[] array = File.ReadAllLines(sourcePath);
string text = "ImportedPreset";
int num = 0;
string[] array2 = array;
foreach (string text2 in array2)
{
if (text2.StartsWith("# Preset:"))
{
text = text2.Substring(9).Trim();
}
else if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("#") && text2.Contains("="))
{
num++;
}
}
if (num == 0)
{
LogHelper.LogError("Invalid preset format");
return false;
}
string path = SanitizeFileName(text) + ".txt";
string destFileName = Path.Combine(_presetsDirectory, path);
File.Copy(sourcePath, destFileName, overwrite: true);
LogHelper.LogInfo($"Imported preset '{text}' with {num} items");
return true;
}
catch (Exception ex)
{
LogHelper.LogError("Failed to import preset: " + ex.Message);
return false;
}
}
private static string SanitizeFileName(string fileName)
{
char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
string text = fileName;
char[] array = invalidFileNameChars;
foreach (char oldChar in array)
{
text = text.Replace(oldChar, '_');
}
return text;
}
}
}
namespace WeightsRebalanced.Patches
{
[HarmonyPatch]
public class ItemSpawnPatches
{
private static HashSet<int> _modifiedItems = new HashSet<int>();
[HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")]
[HarmonyPrefix]
public static void SpawnScrapInLevel_Prefix(RoundManager __instance)
{
if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.currentLevel == (Object)null)
{
return;
}
foreach (SpawnableItemWithRarity item in __instance.currentLevel.spawnableScrap)
{
if (!((Object)(object)item?.spawnableItem == (Object)null))
{
ModifyItemWeight(item.spawnableItem);
}
}
}
[HarmonyPatch(typeof(StartOfRound), "Awake")]
[HarmonyPostfix]
public static void StartOfRound_Awake_Postfix(StartOfRound __instance)
{
if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.allItemsList == (Object)null)
{
return;
}
CacheStoreItems();
int num = 0;
int num2 = 0;
int num3 = 0;
int num4 = 0;
foreach (Item items in __instance.allItemsList.itemsList)
{
if ((Object)(object)items != (Object)null)
{
ItemCategory itemCategory = DetermineItemCategory(items);
ModifyItemWeight(items);
switch (itemCategory)
{
case ItemCategory.Scrap:
num++;
break;
case ItemCategory.Equipment:
num2++;
break;
case ItemCategory.Store:
num3++;
break;
case ItemCategory.Modded:
num4++;
break;
}
}
}
LogHelper.LogInfo($"Modified weights: {num} scrap, {num2} equipment, {num3} store, {num4} modded items.");
}
private static void CacheStoreItems()
{
CategoryDetector.CacheStoreItems();
}
private static void ModifyItemWeight(Item itemProperties)
{
if (!SafetyHelpers.ValidateItem(itemProperties, out var errorMessage))
{
LogHelper.LogWarning("Skipping invalid item: " + errorMessage);
return;
}
SafetyHelpers.SafeExecute(delegate
{
int instanceID = ((Object)itemProperties).GetInstanceID();
if (!_modifiedItems.Contains(instanceID))
{
string itemName = itemProperties.itemName;
float weight = itemProperties.weight;
float num = Plugin.GameWeightToKg(weight);
ItemCategory itemCategory = DetermineItemCategory(itemProperties);
float itemWeight = Plugin.GetItemWeight(itemName, Mathf.Max(0.1f, num), itemCategory);
float num2 = Plugin.KgToGameWeight(itemWeight);
float num3 = Plugin.GameWeightToKg(num2);
itemProperties.weight = num2;
_modifiedItems.Add(instanceID);
LogHelper.LogInfo($"[{itemCategory}] '{itemName}':");
LogHelper.LogInfo($" Original: {weight:F3} game weight = {num:F2} kg");
LogHelper.LogInfo($" Config: {itemWeight:F2} kg");
LogHelper.LogInfo($" New: {num2:F3} game weight");
LogHelper.LogInfo($" Verify: {num2:F3} game weight = {num3:F2} kg");
if (Mathf.Abs(num3 - itemWeight) > 0.01f)
{
LogHelper.LogWarning($" WARNING: Conversion mismatch! Config {itemWeight:F2} kg != Verify {num3:F2} kg");
}
}
}, "ModifyItemWeight");
}
private static ItemCategory DetermineItemCategory(Item item)
{
return CategoryDetector.DetermineCategory(item);
}
[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
[HarmonyPostfix]
public static void ResetShip_Postfix()
{
_modifiedItems.Clear();
}
}
[HarmonyPatch]
public class NetworkPatches
{
[CompilerGenerated]
private sealed class <DelaySyncToClients>d__3 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DelaySyncToClients>d__3(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(2f);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost)
{
foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds)
{
if (connectedClientsId != NetworkManager.Singleton.LocalClientId)
{
LogHelper.LogDebug($"Syncing weights to client {connectedClientsId}");
WeightSyncManager.OnClientConnected(connectedClientsId);
}
}
}
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[HarmonyPatch(typeof(NetworkManager), "StartHost")]
[HarmonyPostfix]
public static void StartHost_Postfix()
{
LogHelper.LogInfo("Host started - initializing weight sync manager");
WeightSyncManager.Initialize();
}
[HarmonyPatch(typeof(NetworkManager), "StartClient")]
[HarmonyPostfix]
public static void StartClient_Postfix()
{
LogHelper.LogInfo("Client started - initializing weight sync manager");
WeightSyncManager.Initialize();
}
[HarmonyPatch(typeof(StartOfRound), "Start")]
[HarmonyPostfix]
public static void StartOfRound_Start_Postfix(StartOfRound __instance)
{
if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost)
{
((MonoBehaviour)__instance).StartCoroutine(DelaySyncToClients());
}
}
[IteratorStateMachine(typeof(<DelaySyncToClients>d__3))]
private static IEnumerator DelaySyncToClients()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DelaySyncToClients>d__3(0);
}
[HarmonyPatch(typeof(NetworkManager), "Shutdown")]
[HarmonyPrefix]
public static void Shutdown_Prefix()
{
if (WeightSyncManager.IsClient())
{
LogHelper.LogInfo("Disconnected from host - restoring original weights");
WeightSyncManager.RestoreOriginalWeights();
}
}
}
[HarmonyPatch]
public class ScanNodePatches
{
private static readonly Regex PoundRegex = new Regex("(\\d+(?:\\.\\d+)?)\\s*lb", RegexOptions.IgnoreCase);
[HarmonyPatch(typeof(HUDManager), "AssignNewNodes")]
[HarmonyPostfix]
public static void AssignNewNodes_Postfix(HUDManager __instance)
{
if (__instance.scanElements == null)
{
return;
}
for (int i = 0; i < __instance.scanElements.Length; i++)
{
RectTransform val = __instance.scanElements[i];
if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf)
{
continue;
}
Text[] componentsInChildren = ((Component)val).GetComponentsInChildren<Text>();
foreach (Text val2 in componentsInChildren)
{
if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(val2.text))
{
val2.text = ConvertPoundsToKg(val2.text);
}
}
}
}
private static string ConvertPoundsToKg(string text)
{
if (string.IsNullOrEmpty(text) || text.Contains("kg"))
{
return text;
}
return PoundRegex.Replace(text, delegate(Match match)
{
if (float.TryParse(match.Groups[1].Value, out var result))
{
float num = result * 0.453592f;
return $"{num:F1} kg";
}
return match.Value;
});
}
}
[HarmonyPatch]
public class WeightPatches
{
private static readonly Regex WeightRegex = new Regex("(\\d+(?:[.,]\\d+)?)\\s*lb", RegexOptions.IgnoreCase);
private static int _lastInventoryHash = 0;
private static float _lastCachedWeight = 0f;
[HarmonyPatch(typeof(HUDManager), "Update")]
[HarmonyPostfix]
public static void HUDManager_Update_Postfix(HUDManager __instance)
{
if ((Object)(object)__instance?.weightCounter == (Object)null)
{
return;
}
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).IsOwner)
{
int num = CalculateInventoryHash(val);
if (num != _lastInventoryHash || ((TMP_Text)__instance.weightCounter).text.Contains("lb"))
{
_lastInventoryHash = num;
_lastCachedWeight = GetPlayerCarryWeightKg(val);
((TMP_Text)__instance.weightCounter).text = $"{_lastCachedWeight:F1} kg";
LogHelper.LogVerbose($"[HUD] Updated weight counter: {_lastCachedWeight:F2} kg displayed as '{((TMP_Text)__instance.weightCounter).text}'");
}
}
}
private static int CalculateInventoryHash(PlayerControllerB player)
{
if (player?.ItemSlots == null)
{
return 0;
}
int num = 17;
GrabbableObject[] itemSlots = player.ItemSlots;
foreach (GrabbableObject val in itemSlots)
{
num = num * 31 + ((val != null) ? ((Object)val).GetInstanceID() : 0);
}
return num;
}
[HarmonyPatch(typeof(PlayerControllerB), "GrabObjectClientRpc")]
[HarmonyPostfix]
public static void GrabObject_Postfix(PlayerControllerB __instance)
{
UpdateWeightDisplay(__instance);
}
[HarmonyPatch(typeof(PlayerControllerB), "DiscardHeldObject")]
[HarmonyPostfix]
public static void DiscardObject_Postfix(PlayerControllerB __instance)
{
UpdateWeightDisplay(__instance);
}
private static void UpdateWeightDisplay(PlayerControllerB player)
{
if (!((Object)(object)player == (Object)null) && ((NetworkBehaviour)player).IsOwner)
{
HUDManager instance = HUDManager.Instance;
if ((Object)(object)instance?.weightCounter != (Object)null)
{
float playerCarryWeightKg = GetPlayerCarryWeightKg(player);
((TMP_Text)instance.weightCounter).text = $"{playerCarryWeightKg:F1} kg";
}
}
}
[HarmonyPatch(typeof(HUDManager), "DisplayNewScrapFound")]
[HarmonyPostfix]
public static void DisplayNewScrapFound_Postfix(HUDManager __instance)
{
if (!((Object)(object)__instance == (Object)null) && (Object)(object)__instance.totalValueText != (Object)null)
{
string text = ((TMP_Text)__instance.totalValueText).text;
if (!string.IsNullOrEmpty(text) && text.Contains("lb"))
{
((TMP_Text)__instance.totalValueText).text = ConvertWeightText(text);
}
}
}
public static string ConvertWeightText(string text)
{
if (string.IsNullOrEmpty(text) || text.Contains("kg"))
{
return text;
}
return WeightRegex.Replace(text, delegate(Match match)
{
if (float.TryParse(match.Groups[1].Value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
float num = result * 0.453592f;
return $"{num:F1} kg";
}
return match.Value;
});
}
public static float GetPlayerCarryWeightKg(PlayerControllerB player)
{
if ((Object)(object)player == (Object)null)
{
return 0f;
}
float num = 0f;
GrabbableObject[] itemSlots = player.ItemSlots;
foreach (GrabbableObject val in itemSlots)
{
if ((Object)(object)val != (Object)null && (Object)(object)val.itemProperties != (Object)null)
{
string itemName = val.itemProperties.itemName;
float num2 = 0f;
if (Plugin.CurrentItemWeightsKg.TryGetValue(itemName, out var value))
{
num2 = value;
LogHelper.LogVerbose($"[HUD] '{itemName}': Using config weight {value:F2} kg (game weight: {val.itemProperties.weight:F3})");
}
else
{
num2 = Plugin.GameWeightToKg(val.itemProperties.weight);
LogHelper.LogVerbose($"[HUD] '{itemName}': Fallback conversion from game weight {val.itemProperties.weight:F3} = {num2:F2} kg");
}
num += num2;
}
}
LogHelper.LogVerbose($"[HUD] Total carry weight: {num:F2} kg");
return num;
}
}
}
namespace WeightsRebalanced.Networking
{
public static class WeightSyncManager
{
[CompilerGenerated]
private static class <>O
{
public static HandleNamedMessageDelegate <0>__OnReceiveWeightSync;
}
private const string CustomMessageName = "WeightsRebalanced_SyncWeights";
private static bool _isInitialized = false;
private static bool _isSyncing = false;
private static Dictionary<string, float> _originalWeights = new Dictionary<string, float>();
public static void Initialize()
{
//IL_002d: 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_0038: Expected O, but got Unknown
if (_isInitialized)
{
return;
}
_isInitialized = true;
try
{
CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager;
object obj = <>O.<0>__OnReceiveWeightSync;
if (obj == null)
{
HandleNamedMessageDelegate val = OnReceiveWeightSync;
<>O.<0>__OnReceiveWeightSync = val;
obj = (object)val;
}
customMessagingManager.RegisterNamedMessageHandler("WeightsRebalanced_SyncWeights", (HandleNamedMessageDelegate)obj);
LogHelper.LogInfo("Weight synchronization network handler registered");
}
catch (Exception ex)
{
LogHelper.LogError("Failed to register network handler: " + ex.Message);
}
}
public static void OnClientConnected(ulong clientId)
{
if (IsHost())
{
LogHelper.LogInfo($"Sending weight sync to client {clientId}");
SendWeightsToClient(clientId);
}
}
private static void SendWeightsToClient(ulong clientId)
{
//IL_001e: 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)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
try
{
byte[] array = SerializeWeights();
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(array.Length + 4, (Allocator)2, -1);
try
{
int num = array.Length;
((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
((FastBufferWriter)(ref val)).WriteBytesSafe(array, -1, 0);
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("WeightsRebalanced_SyncWeights", clientId, val, (NetworkDelivery)4);
}
finally
{
((IDisposable)(FastBufferWriter)(ref val)).Dispose();
}
LogHelper.LogDebug($"Sent {array.Length} bytes of weight data to client {clientId}");
}
catch (Exception ex)
{
LogHelper.LogError($"Failed to send weights to client {clientId}: {ex.Message}");
}
}
public static void BroadcastWeightUpdate(string itemName, float weightKg)
{
//IL_0029: 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_004e: Unknown result type (might be due to invalid IL or missing references)
if (!IsHost())
{
return;
}
try
{
byte[] array = SerializeSingleWeight(itemName, weightKg);
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(array.Length + 4, (Allocator)2, -1);
try
{
int num = array.Length;
((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
((FastBufferWriter)(ref val)).WriteBytesSafe(array, -1, 0);
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessageToAll("WeightsRebalanced_SyncWeights", val, (NetworkDelivery)2);
}
finally
{
((IDisposable)(FastBufferWriter)(ref val)).Dispose();
}
LogHelper.LogDebug("Broadcast weight update for '" + itemName + "' to all clients");
}
catch (Exception ex)
{
LogHelper.LogError("Failed to broadcast weight update: " + ex.Message);
}
}
private static void OnReceiveWeightSync(ulong senderId, FastBufferReader reader)
{
//IL_001e: 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)
if (_isSyncing)
{
LogHelper.LogWarning("Already syncing weights, ignoring duplicate message");
return;
}
_isSyncing = true;
try
{
int num = default(int);
((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
byte[] data = new byte[num];
((FastBufferReader)(ref reader)).ReadBytesSafe(ref data, num, 0);
DeserializeAndApplyWeights(data);
LogHelper.LogInfo($"Received and applied {num} bytes of weight data from host");
}
catch (Exception ex)
{
LogHelper.LogError("Failed to receive weight sync: " + ex.Message);
}
finally
{
_isSyncing = false;
}
}
private static byte[] SerializeWeights()
{
using MemoryStream memoryStream = new MemoryStream();
using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
binaryWriter.Write((byte)1);
binaryWriter.Write("2.0.0");
binaryWriter.Write(Plugin.ItemWeights.Count);
foreach (KeyValuePair<string, ConfigEntry<float>> itemWeight in Plugin.ItemWeights)
{
binaryWriter.Write(itemWeight.Key);
binaryWriter.Write(itemWeight.Value.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 DeserializeAndApplyWeights(byte[] data)
{
using MemoryStream input = new MemoryStream(data);
using BinaryReader binaryReader = new BinaryReader(input);
switch (binaryReader.ReadByte())
{
case 1:
{
string arg = binaryReader.ReadString();
int num = binaryReader.ReadInt32();
LogHelper.LogInfo($"Receiving full weight sync from host (version {arg}, {num} items)");
Dictionary<string, float> dictionary = new Dictionary<string, float>();
for (int i = 0; i < num; i++)
{
string key = binaryReader.ReadString();
float value = binaryReader.ReadSingle();
dictionary[key] = value;
}
ApplyReceivedWeights(dictionary);
break;
}
case 2:
{
string itemName = binaryReader.ReadString();
float weightKg = binaryReader.ReadSingle();
ApplySingleWeight(itemName, weightKg);
break;
}
}
}
private static void ApplyReceivedWeights(Dictionary<string, float> weights)
{
int num = 0;
foreach (KeyValuePair<string, float> weight in weights)
{
ApplySingleWeight(weight.Key, weight.Value);
num++;
}
LogHelper.LogInfo($"Applied {num} synced weights from host (local config overridden for this session)");
}
private static void ApplySingleWeight(string itemName, float weightKg)
{
weightKg = ConfigValidator.ClampWeight(weightKg, itemName);
Plugin.CurrentItemWeightsKg[itemName] = weightKg;
if (Plugin.ItemWeights.TryGetValue(itemName, out var value))
{
if (!_originalWeights.ContainsKey(itemName))
{
_originalWeights[itemName] = value.Value;
}
value.Value = weightKg;
}
UpdateLiveItemWeight(itemName, weightKg);
}
private static void UpdateLiveItemWeight(string itemName, float weightKg)
{
GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
int num = 0;
GrabbableObject[] array2 = array;
foreach (GrabbableObject val in array2)
{
if ((Object)(object)val?.itemProperties != (Object)null && val.itemProperties.itemName == itemName)
{
float weight = Plugin.KgToGameWeight(weightKg);
val.itemProperties.weight = weight;
num++;
}
}
if (num > 0)
{
LogHelper.LogDebug($"Updated {num} live instances of '{itemName}'");
}
}
public static void RestoreOriginalWeights()
{
if (_originalWeights.Count == 0)
{
return;
}
LogHelper.LogInfo($"Restoring {_originalWeights.Count} original weight values");
foreach (KeyValuePair<string, float> originalWeight in _originalWeights)
{
if (Plugin.ItemWeights.TryGetValue(originalWeight.Key, out var value))
{
value.Value = originalWeight.Value;
}
}
_originalWeights.Clear();
}
public static bool IsHost()
{
if ((Object)(object)NetworkManager.Singleton != (Object)null)
{
return NetworkManager.Singleton.IsHost;
}
return false;
}
public static bool IsClient()
{
if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsClient)
{
return !NetworkManager.Singleton.IsHost;
}
return false;
}
}
}
namespace WeightsRebalanced.ItemDetection
{
public static class CategoryDetector
{
private static 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 (IsStoreItem(item))
{
return ItemCategory.Store;
}
return ItemCategory.Equipment;
}
private static bool IsStoreItem(Item item)
{
if (_storeItemNames.Contains(item.itemName))
{
return true;
}
Terminal val = Object.FindObjectOfType<Terminal>();
if (val?.buyableItemsList != null)
{
Item[] buyableItemsList = val.buyableItemsList;
for (int i = 0; i < buyableItemsList.Length; i++)
{
if ((Object)(object)buyableItemsList[i] == (Object)(object)item)
{
_storeItemNames.Add(item.itemName);
return true;
}
}
}
return false;
}
public static void CacheStoreItems()
{
_storeItemNames.Clear();
Terminal val = Object.FindObjectOfType<Terminal>();
if (val?.buyableItemsList == null)
{
return;
}
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 dynamically");
}
}
public static class DiagnosticCommands
{
public static void PrintAllItemsWithOrigin()
{
StartOfRound val = Object.FindObjectOfType<StartOfRound>();
if (val?.allItemsList?.itemsList != null)
{
LogHelper.LogInfo("=== Item Detection Diagnostics ===");
int num = 0;
int num2 = 0;
int num3 = 0;
int num4 = 0;
int num5 = 0;
int num6 = 0;
int num7 = 0;
foreach (Item items in val.allItemsList.itemsList)
{
if (!((Object)(object)items == (Object)null))
{
ItemOrigin itemOrigin = ItemClassifier.GetItemOrigin(items);
ItemCategory itemCategory = CategoryDetector.DetermineCategory(items);
float num8 = Plugin.GameWeightToKg(items.weight);
LogHelper.LogInfo($"[{itemOrigin}] [{itemCategory}] {items.itemName} (weight: {num8:F2} kg)");
switch (itemOrigin)
{
case ItemOrigin.Vanilla:
num++;
break;
case ItemOrigin.Modded:
num2++;
break;
case ItemOrigin.Unknown:
num3++;
break;
}
switch (itemCategory)
{
case ItemCategory.Scrap:
num4++;
break;
case ItemCategory.Equipment:
num5++;
break;
case ItemCategory.Store:
num6++;
break;
case ItemCategory.Modded:
num7++;
break;
}
}
}
LogHelper.LogInfo("\n=== Summary ===");
LogHelper.LogInfo($"Origins: {num} vanilla, {num2} modded, {num3} unknown");
LogHelper.LogInfo($"Categories: {num4} scrap, {num5} equipment, {num6} store, {num7} modded");
LogHelper.LogInfo(ItemClassifier.GetDiagnostics());
}
else
{
LogHelper.LogWarning("Could not find StartOfRound or items list");
}
}
}
public enum ItemOrigin
{
Vanilla,
Modded,
Unknown
}
public static class ItemClassifier
{
private static Assembly _vanillaAssembly;
private static HashSet<string> _knownVanillaItems = new HashSet<string>();
private static HashSet<string> _knownModdedItems = new HashSet<string>();
private static Dictionary<int, ItemOrigin> _itemOriginCache = new Dictionary<int, ItemOrigin>();
public static void Initialize()
{
_vanillaAssembly = typeof(StartOfRound).Assembly;
LogHelper.LogInfo("Vanilla assembly identified: " + _vanillaAssembly.GetName().Name);
}
public static ItemOrigin GetItemOrigin(Item item)
{
if ((Object)(object)item == (Object)null)
{
return ItemOrigin.Unknown;
}
int instanceID = ((Object)item).GetInstanceID();
if (_itemOriginCache.TryGetValue(instanceID, out var value))
{
return value;
}
ItemOrigin itemOrigin = DetermineOriginInternal(item);
_itemOriginCache[instanceID] = itemOrigin;
return itemOrigin;
}
private static ItemOrigin DetermineOriginInternal(Item item)
{
try
{
Assembly assembly = ((object)item).GetType().Assembly;
if (assembly == _vanillaAssembly)
{
_knownVanillaItems.Add(item.itemName);
LogHelper.LogVerbose("Detected vanilla item via assembly: " + item.itemName);
return ItemOrigin.Vanilla;
}
if (assembly != null && assembly != _vanillaAssembly)
{
_knownModdedItems.Add(item.itemName);
LogHelper.LogVerbose("Detected modded item via assembly: " + item.itemName + " (from " + assembly.GetName().Name + ")");
return ItemOrigin.Modded;
}
}
catch (Exception ex)
{
LogHelper.LogDebug("Assembly check failed for '" + item.itemName + "': " + ex.Message);
}
if (_knownVanillaItems.Contains(item.itemName))
{
return ItemOrigin.Vanilla;
}
if (_knownModdedItems.Contains(item.itemName))
{
return ItemOrigin.Modded;
}
LogHelper.LogDebug("Could not determine origin for '" + item.itemName + "', assuming modded");
return ItemOrigin.Modded;
}
public static string GetDiagnostics()
{
return $"Known Vanilla Items: {_knownVanillaItems.Count}\n" + $"Known Modded Items: {_knownModdedItems.Count}\n" + $"Cache Size: {_itemOriginCache.Count}";
}
public static void ClearCache()
{
_itemOriginCache.Clear();
}
}
}
namespace WeightsRebalanced.HotReload
{
public static class ConfigWatcher
{
private static bool _initialized;
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 (IsWeightSetting(e.ChangedSetting))
{
string key = e.ChangedSetting.Definition.Key;
float num = (float)e.ChangedSetting.BoxedValue;
LogHelper.LogDebug($"Config changed: {key} = {num:F1} kg");
UpdateLiveItems(key, num);
if (WeightSyncManager.IsHost())
{
WeightSyncManager.BroadcastWeightUpdate(key, num);
}
}
}
private static bool IsWeightSetting(ConfigEntryBase setting)
{
string section = setting.Definition.Section;
if (!section.Contains("Items") && !section.Contains("Equipment") && !section.Contains("Store"))
{
return section.Contains("Modded");
}
return true;
}
private static void UpdateLiveItems(string itemName, float newWeightKg)
{
GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
int num = 0;
GrabbableObject[] array2 = array;
foreach (GrabbableObject val in array2)
{
if ((Object)(object)val?.itemProperties != (Object)null && val.itemProperties.itemName == itemName)
{
float weight = Plugin.KgToGameWeight(newWeightKg);
val.itemProperties.weight = weight;
num++;
}
}
if (num > 0)
{
LogHelper.LogInfo($"Hot-reloaded weight for {num} instances of '{itemName}' to {newWeightKg:F1} kg");
}
}
}
}