Decompiled source of WeightsRebalanced v2.0.1

plugins\WeightsRebalanced.dll

Decompiled 4 days ago
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");
			}
		}
	}
}