Decompiled source of ServerRates v1.4.9

plugins\ServerRates\ServerRates.dll

Decompiled a week ago
using System;
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 HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ServerRates")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.4.9.0")]
[assembly: AssemblyInformationalVersion("1.4.9")]
[assembly: AssemblyProduct("ServerRates")]
[assembly: AssemblyTitle("ServerRates")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.4.9.0")]
[module: UnverifiableCode]
namespace ServerRates;

internal static class CategoryGroundAmp
{
	private static readonly int CatFlag = StringExtensionMethods.GetStableHashCode("SR.Cat");

	private static readonly int NoCatFlag = StringExtensionMethods.GetStableHashCode("SR.NoCat");

	private static readonly HashSet<ZDOID> Done = new HashSet<ZDOID>();

	private static readonly List<ZDO> Near = new List<ZDO>(256);

	private static readonly List<ZDO> Distant = new List<ZDO>(256);

	private static float _nextScan;

	private static int _diag;

	private static int _seen;

	public static void LogStatus()
	{
		if (Plugin.Log != null && Plugin.Settings != null)
		{
			ModConfig settings = Plugin.Settings;
			Plugin.Log.LogInfo((object)("ServerRates loot: GroundAmp=" + settings.CategoryGroundAmp.Value + " SnapGround=" + settings.SnapExtraDropsToGround.Value + " Wood=x" + settings.WoodDropMultiplier.Value + " Hide=x" + settings.HideDropMultiplier.Value + " Meat=x" + settings.MeatDropMultiplier.Value + " Ore=x" + settings.OreDropMultiplier.Value + " (fine settings in 6a/6b/6c)"));
		}
	}

	public static void MarkPlayerDrop(ItemDrop drop)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		ZNetView val = (((Object)(object)drop != (Object)null) ? ((Component)drop).GetComponent<ZNetView>() : null);
		if (!((Object)(object)val == (Object)null) && val.IsValid())
		{
			ZDO zDO = val.GetZDO();
			if (zDO != null)
			{
				zDO.Set(NoCatFlag, 1, false);
				Done.Add(zDO.m_uid);
			}
		}
	}

	public static void Tick()
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		if (!DropScale.Ready() || Plugin.Settings == null || !Plugin.Settings.CategoryGroundAmp.Value || ZDOMan.instance == null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || Time.time < _nextScan)
		{
			return;
		}
		_nextScan = Time.time + 0.35f;
		Near.Clear();
		Distant.Clear();
		List<ZNetPeer> peers = ZNet.instance.GetPeers();
		if (peers == null || peers.Count == 0)
		{
			return;
		}
		SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance();
		for (int i = 0; i < peers.Count; i++)
		{
			ZNetPeer val = peers[i];
			if (val != null && val.IsReady())
			{
				Vector2s zone = ZoneSystem.GetZone(val.GetRefPos());
				ZDOMan.instance.FindSectorObjects(zone, syncedSimulationDistance, Near, Distant);
			}
		}
		for (int j = 0; j < Near.Count; j++)
		{
			TryAmplifyZdo(Near[j]);
		}
		for (int k = 0; k < Distant.Count; k++)
		{
			TryAmplifyZdo(Distant[k]);
		}
		if (Done.Count > 4000)
		{
			Done.Clear();
		}
	}

	public static void TryAmplifyZdo(ZDO zdo)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_029f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		if (zdo == null || !DropScale.Ready() || Plugin.Settings == null || !Plugin.Settings.CategoryGroundAmp.Value || (Object)(object)ZNetScene.instance == (Object)null || Done.Contains(zdo.m_uid))
		{
			return;
		}
		if (zdo.GetInt(NoCatFlag, 0) != 0)
		{
			Done.Add(zdo.m_uid);
			return;
		}
		if (zdo.GetInt(CatFlag, 0) != 0)
		{
			Done.Add(zdo.m_uid);
			return;
		}
		GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab());
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		ItemDrop component = prefab.GetComponent<ItemDrop>();
		if ((Object)(object)component == (Object)null || component.m_itemData == null)
		{
			return;
		}
		DropClassifier.Kind kind = DropClassifier.Classify(prefab);
		if (kind == DropClassifier.Kind.Other)
		{
			kind = DropClassifier.Classify(component.m_itemData);
		}
		float num = Mathf.Max(0f, DropClassifier.MultiplierFor(kind, Plugin.Settings));
		ItemData val = component.m_itemData.Clone();
		ItemDrop.LoadFromZDO(val, zdo, 0);
		int num2 = Mathf.Max(1, val.m_stack);
		if (_seen < 30 && Plugin.Log != null)
		{
			_seen++;
			Plugin.Log.LogInfo((object)("ServerRates ground amp check: '" + ((Object)prefab).name + "' -> " + kind.ToString() + " x" + num.ToString("0.##") + " stack=" + num2));
		}
		Done.Add(zdo.m_uid);
		try
		{
			zdo.Set(CatFlag, 1, false);
		}
		catch
		{
		}
		if (num <= 1.001f)
		{
			return;
		}
		int num3 = Mathf.Max(0, Mathf.RoundToInt((float)num2 * num));
		if (num3 > num2)
		{
			if (_diag < 40 && Plugin.Log != null)
			{
				_diag++;
				Plugin.Log.LogInfo((object)("ServerRates ground amp: " + kind.ToString() + " x" + num.ToString("0.##") + " '" + ((Object)prefab).name + "' " + num2 + " -> " + num3));
			}
			int num4 = 1;
			if (val.m_shared != null)
			{
				num4 = Mathf.Max(1, val.m_shared.m_maxStackSize);
			}
			int num5 = num3 - num2;
			Vector3 position = zdo.GetPosition();
			Quaternion rotation = zdo.GetRotation();
			while (num5 > 0)
			{
				int num6 = Mathf.Min(num5, num4);
				num5 -= num6;
				SpawnExtra(prefab, position, rotation, val, num6);
			}
		}
	}

	private static void SpawnExtra(GameObject prefab, Vector3 pos, Quaternion rot, ItemData template, int stack)
	{
		//IL_0000: 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_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		Vector2 val = Random.insideUnitCircle * 0.45f;
		Vector3 val2 = pos + new Vector3(val.x, 0.15f, val.y);
		ModConfig settings = Plugin.Settings;
		if (settings != null && settings.SnapExtraDropsToGround.Value && (Object)(object)ZoneSystem.instance != (Object)null)
		{
			float solidHeight = ZoneSystem.instance.GetSolidHeight(val2);
			if (solidHeight > -1000f && solidHeight < 5000f && (val2.y > solidHeight + 0.35f || val2.y < solidHeight - 0.1f))
			{
				val2.y = solidHeight + 0.2f;
			}
		}
		GameObject val3 = Object.Instantiate<GameObject>(prefab, val2, rot);
		ItemDrop component = val3.GetComponent<ItemDrop>();
		if ((Object)(object)component == (Object)null)
		{
			Object.Destroy((Object)(object)val3);
			return;
		}
		if (component.m_itemData != null && template != null)
		{
			component.m_itemData = template.Clone();
			component.m_itemData.m_stack = stack;
		}
		ZNetView component2 = ((Component)component).GetComponent<ZNetView>();
		if ((Object)(object)component2 != (Object)null && component2.IsValid())
		{
			ZDO zDO = component2.GetZDO();
			if (zDO != null)
			{
				Done.Add(zDO.m_uid);
				zDO.Set(CatFlag, 1, false);
				zDO.Set(NoCatFlag, 0, false);
				zDO.SetPosition(val2);
				if (component.m_itemData != null)
				{
					ItemDrop.SaveToZDO(component.m_itemData, zDO, 0);
				}
			}
		}
		ItemDrop.OnCreateNew(val3, false);
		float num = ((settings != null) ? Mathf.Max(0f, settings.ExtraDropScatterForce.Value) : 4f);
		Rigidbody component3 = val3.GetComponent<Rigidbody>();
		if ((Object)(object)component3 != (Object)null && num > 0.01f)
		{
			Vector3 val4 = Random.insideUnitSphere * num;
			if (val4.y < 0f)
			{
				val4.y = 0f - val4.y;
			}
			component3.WakeUp();
			component3.AddForce(val4, (ForceMode)2);
		}
	}
}
[HarmonyPatch(typeof(ItemDrop), "OnPlayerDrop")]
internal static class ItemDropOnPlayerDropPatch
{
	private static void Postfix(ItemDrop __instance)
	{
		CategoryGroundAmp.MarkPlayerDrop(__instance);
	}
}
[HarmonyPatch(typeof(ZNetScene), "CreateObject")]
internal static class ZNetSceneCreateObjectPatch
{
	private static void Postfix(ZDO zdo, GameObject __result)
	{
		if (zdo != null)
		{
			CategoryGroundAmp.TryAmplifyZdo(zdo);
		}
		else if ((Object)(object)__result != (Object)null)
		{
			ZNetView component = __result.GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				CategoryGroundAmp.TryAmplifyZdo(component.GetZDO());
			}
		}
	}
}
internal static class ConfigWatch
{
	private static FileSystemWatcher _watcher;

	private static float _reloadAt;

	private static bool _pending;

	private static string _cfgFileName;

	private static float _suppressUntil;

	public static void Start()
	{
		if (_watcher != null)
		{
			return;
		}
		try
		{
			_cfgFileName = (((Object)(object)Plugin.Instance != (Object)null) ? Path.GetFileName(((BaseUnityPlugin)Plugin.Instance).Config.ConfigFilePath) : "com.morda.serverrates.cfg");
			_watcher = new FileSystemWatcher(Paths.ConfigPath);
			_watcher.Filter = _cfgFileName;
			_watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
			_watcher.Changed += OnChanged;
			_watcher.Created += OnChanged;
			_watcher.EnableRaisingEvents = true;
			Plugin.Log.LogInfo((object)("ServerRates config watcher: " + Path.Combine(Paths.ConfigPath, _cfgFileName)));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("ServerRates config watcher failed: " + ex.Message));
		}
	}

	public static void SuppressReloadBriefly(float seconds = 2f)
	{
		_suppressUntil = Time.unscaledTime + Mathf.Max(0.1f, seconds);
		_pending = false;
	}

	public static void Tick()
	{
		if (Time.unscaledTime < _suppressUntil)
		{
			_pending = false;
		}
		else if (_pending && !(Time.unscaledTime < _reloadAt))
		{
			_pending = false;
			Reload();
		}
	}

	private static void OnChanged(object sender, FileSystemEventArgs e)
	{
		_pending = true;
		_reloadAt = Time.unscaledTime + 0.5f;
	}

	private static void Reload()
	{
		try
		{
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((BaseUnityPlugin)Plugin.Instance).Config.Reload();
			}
			RateApplier.NotifyConfigReloaded();
			RateApplier.ApplyGlobalKeys();
			Plugin.Log.LogInfo((object)"ServerRates config reloaded from disk and applied.");
			CategoryGroundAmp.LogStatus();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("ServerRates reload failed: " + ex.Message));
		}
	}
}
internal static class DropClassifier
{
	public enum Kind
	{
		Other,
		Wood,
		FineWood,
		CoreWood,
		SpecialWood,
		Ore,
		Scrap,
		Stone,
		Flint,
		Crystal,
		Fuel,
		Gems,
		BossMats,
		Crops,
		Seeds,
		Mushrooms,
		BerriesHoney,
		Fish,
		Potions,
		Hide,
		Trophy,
		Meat,
		Parts,
		Feathers,
		SpecialParts
	}

	private static readonly HashSet<string> Wood = new HashSet<string> { "wood" };

	private static readonly HashSet<string> FineWood = new HashSet<string> { "finewood" };

	private static readonly HashSet<string> CoreWood = new HashSet<string> { "roundlog" };

	private static readonly HashSet<string> SpecialWood = new HashSet<string> { "elderbark", "yggdrasilwood", "ashwood", "blackwood" };

	private static readonly HashSet<string> Ore = new HashSet<string> { "copperore", "tinore", "ironore", "silverore", "flametalore", "flametalorenew" };

	private static readonly HashSet<string> Scrap = new HashSet<string> { "ironscrap", "blackmetalscrap", "copperscrap" };

	private static readonly HashSet<string> Stone = new HashSet<string> { "stone", "grausten", "sharpeningstone", "thunderstone" };

	private static readonly HashSet<string> Flint = new HashSet<string> { "flint" };

	private static readonly HashSet<string> Crystal = new HashSet<string> { "crystal", "obsidian" };

	private static readonly HashSet<string> Fuel = new HashSet<string> { "coal", "resin", "tar" };

	private static readonly HashSet<string> Gems = new HashSet<string> { "coins", "amber", "amberpearl", "ruby", "silvernecklace" };

	private static readonly HashSet<string> BossMats = new HashSet<string>
	{
		"surtlingcore", "dragontear", "ancientseed", "ymirremains", "queenbee", "yagluthdrop", "vegvisirshard_bonemass", "blackcore", "refinedeitr", "mechanicalspring",
		"dvergrkeyfragment", "trophybonemass", "trophyeikthyr", "trophytheelder", "trophydragonqueen", "trophygoblinking", "trophyseekerqueen", "trophyfader"
	};

	private static readonly HashSet<string> Crops = new HashSet<string>
	{
		"barley", "barleyflour", "flax", "carrot", "onion", "turnip", "thistle", "dandelion", "sap", "royaljelly",
		"jotunpuffs", "smokepuffs"
	};

	private static readonly HashSet<string> Seeds = new HashSet<string> { "carrotseeds", "onionseeds", "turnipseeds", "beechseeds", "birchseeds", "fircone", "pinecone", "acorn", "oakseeds" };

	private static readonly HashSet<string> Mushrooms = new HashSet<string> { "mushroom", "mushroomyellow", "mushroomblue", "magecap" };

	private static readonly HashSet<string> BerriesHoney = new HashSet<string> { "raspberry", "blueberry", "cloudberry", "honey", "bukeperries" };

	private static readonly HashSet<string> Fish = new HashSet<string>
	{
		"fishraw", "fish", "perch", "pike", "tuna", "trollfish", "pufferfish", "anglerfish", "coralcod", "northernsalmon",
		"magmapander", "voltureegg"
	};

	private static readonly HashSet<string> Potions = new HashSet<string>
	{
		"meadbasehealth", "meadbasehealthminor", "meadbasestamina", "meadbasetasty", "meadbasepoisonresist", "meadbasefrostresist", "meadbaseeitr", "meadbaselight", "potion_health_minor", "potion_health_medium",
		"potion_stamina_minor", "potion_stamina_medium", "potion_eitr_minor", "poisonbomb"
	};

	private static readonly HashSet<string> Hide = new HashSet<string>
	{
		"deerhide", "leatherscraps", "trollhide", "wolfpelt", "loxpelt", "serpentscale", "chitin", "scalehide", "asksvinhide", "harepelt",
		"hare_pelt", "wolfhairbundle"
	};

	private static readonly HashSet<string> Meat = new HashSet<string>
	{
		"rawmeat", "necktail", "boarmeat", "deer", "deermeat", "wolfmeat", "loxmeat", "serpentmeat", "chickenmeat", "haremeat",
		"asksvinmeat", "bugmeat", "meatrotten", "necktailgrilled", "cookedmeat", "cookedbackmeat", "serpentmeatcooked", "cookeddeer", "cookedwolf", "loxpie",
		"fishcooked"
	};

	private static readonly HashSet<string> Parts = new HashSet<string>
	{
		"bonefragments", "witheredbone", "entrails", "bloodbag", "needle", "greydwarfeye", "freezegland", "hardantler", "wolffang", "root",
		"mandible", "chain"
	};

	private static readonly HashSet<string> Feathers = new HashSet<string> { "feathers" };

	private static readonly HashSet<string> SpecialParts = new HashSet<string>
	{
		"guck", "ooze", "softtissue", "bilebag", "refinedeitr", "blackcore", "dvergrkeyfragment", "mechanicalspring", "seekerchitin", "carapace",
		"moltencore", "charredskull", "flametal", "proustitepowder"
	};

	public static Kind Classify(GameObject prefab)
	{
		if ((Object)(object)prefab == (Object)null)
		{
			return Kind.Other;
		}
		ItemDrop component = prefab.GetComponent<ItemDrop>();
		if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_shared != null)
		{
			Kind kind = ClassifyKey(Normalize(component.m_itemData.m_shared.m_name));
			if (kind != Kind.Other)
			{
				return kind;
			}
		}
		return ClassifyKey(Normalize(((Object)prefab).name));
	}

	public static Kind Classify(ItemData data)
	{
		if (data == null || data.m_shared == null)
		{
			return Kind.Other;
		}
		return ClassifyKey(Normalize(data.m_shared.m_name));
	}

	private static Kind ClassifyKey(string key)
	{
		if (string.IsNullOrEmpty(key))
		{
			return Kind.Other;
		}
		if (FineWood.Contains(key))
		{
			return Kind.FineWood;
		}
		if (CoreWood.Contains(key))
		{
			return Kind.CoreWood;
		}
		if (SpecialWood.Contains(key))
		{
			return Kind.SpecialWood;
		}
		if (Wood.Contains(key) || key == "wood")
		{
			return Kind.Wood;
		}
		if (key.EndsWith("wood") && !key.Contains("yggdrasil"))
		{
			return Kind.Wood;
		}
		if (Ore.Contains(key) || (key.EndsWith("ore") && !key.Contains("flametalorenew")))
		{
			return Kind.Ore;
		}
		if (Scrap.Contains(key) || (key.EndsWith("scrap") && (key.Contains("iron") || key.Contains("metal") || key.Contains("copper") || key.Contains("black"))))
		{
			return Kind.Scrap;
		}
		if (Flint.Contains(key))
		{
			return Kind.Flint;
		}
		if (Crystal.Contains(key))
		{
			return Kind.Crystal;
		}
		if (Stone.Contains(key))
		{
			return Kind.Stone;
		}
		if (Fuel.Contains(key))
		{
			return Kind.Fuel;
		}
		if (Gems.Contains(key))
		{
			return Kind.Gems;
		}
		if (BossMats.Contains(key))
		{
			return Kind.BossMats;
		}
		if (Seeds.Contains(key) || key.EndsWith("seeds") || key.EndsWith("cone") || key == "acorn")
		{
			return Kind.Seeds;
		}
		if (Mushrooms.Contains(key) || key.Contains("mushroom") || key == "magecap")
		{
			return Kind.Mushrooms;
		}
		if (BerriesHoney.Contains(key) || key.Contains("berry") || key == "honey")
		{
			return Kind.BerriesHoney;
		}
		if (Fish.Contains(key) || key.StartsWith("fish") || key.Contains("salmon") || key.Contains("puffer"))
		{
			return Kind.Fish;
		}
		if (Potions.Contains(key) || key.Contains("mead") || key.Contains("potion"))
		{
			return Kind.Potions;
		}
		if (Crops.Contains(key))
		{
			return Kind.Crops;
		}
		if (key.Contains("trophy"))
		{
			return Kind.Trophy;
		}
		if (Hide.Contains(key) || key.Contains("hide") || key.Contains("pelt") || key.Contains("leather") || key.Contains("scale"))
		{
			return Kind.Hide;
		}
		if (Meat.Contains(key) || key.Contains("meat") || key.Contains("necktail"))
		{
			return Kind.Meat;
		}
		if (Feathers.Contains(key) || key.Contains("feather"))
		{
			return Kind.Feathers;
		}
		if (SpecialParts.Contains(key))
		{
			return Kind.SpecialParts;
		}
		if (Parts.Contains(key) || key.Contains("bone") || key.Contains("entrails") || key.Contains("gland") || key.Contains("fang") || key.Contains("eye"))
		{
			return Kind.Parts;
		}
		return Kind.Other;
	}

	public static float MultiplierFor(Kind kind, ModConfig c)
	{
		if (c == null)
		{
			return 1f;
		}
		return kind switch
		{
			Kind.Wood => c.WoodDropMultiplier.Value, 
			Kind.FineWood => c.FineWoodDropMultiplier.Value, 
			Kind.CoreWood => c.CoreWoodDropMultiplier.Value, 
			Kind.SpecialWood => c.SpecialWoodDropMultiplier.Value, 
			Kind.Ore => c.OreDropMultiplier.Value, 
			Kind.Scrap => c.ScrapDropMultiplier.Value, 
			Kind.Stone => c.StoneDropMultiplier.Value, 
			Kind.Flint => c.FlintDropMultiplier.Value, 
			Kind.Crystal => c.CrystalDropMultiplier.Value, 
			Kind.Fuel => c.FuelDropMultiplier.Value, 
			Kind.Gems => c.GemDropMultiplier.Value, 
			Kind.BossMats => c.BossDropMultiplier.Value, 
			Kind.Crops => c.CropDropMultiplier.Value, 
			Kind.Seeds => c.SeedDropMultiplier.Value, 
			Kind.Mushrooms => c.MushroomDropMultiplier.Value, 
			Kind.BerriesHoney => c.BerryHoneyDropMultiplier.Value, 
			Kind.Fish => c.FishDropMultiplier.Value, 
			Kind.Potions => c.PotionDropMultiplier.Value, 
			Kind.Hide => c.HideDropMultiplier.Value, 
			Kind.Trophy => c.TrophyDropMultiplier.Value, 
			Kind.Meat => c.MeatDropMultiplier.Value, 
			Kind.Parts => c.PartsDropMultiplier.Value, 
			Kind.Feathers => c.FeatherDropMultiplier.Value, 
			Kind.SpecialParts => c.SpecialPartsDropMultiplier.Value, 
			_ => c.OtherDropMultiplier.Value, 
		};
	}

	private static string Normalize(string raw)
	{
		if (string.IsNullOrEmpty(raw))
		{
			return "";
		}
		string text = raw.Trim().ToLowerInvariant();
		int num = text.IndexOf("(clone)");
		if (num >= 0)
		{
			text = text.Substring(0, num).Trim();
		}
		if (text.StartsWith("$item_"))
		{
			text = text.Substring(6);
		}
		return text.Replace(" ", "").Replace("-", "").Replace("_", "");
	}
}
public class ModConfig
{
	private static readonly AcceptableValueList<string> ToggleValues = new AcceptableValueList<string>(new string[3] { "Unchanged", "On", "Off" });

	public ConfigEntry<bool> Enabled { get; }

	public ConfigEntry<bool> ChatCommandsEnabled { get; }

	public ConfigEntry<string> ChatCommandPrefix { get; }

	public ConfigEntry<float> SkillGainPercent { get; }

	public ConfigEntry<float> SkillReductionPercent { get; }

	public ConfigEntry<float> PlayerDamagePercent { get; }

	public ConfigEntry<float> EnemyDamagePercent { get; }

	public ConfigEntry<float> EnemySpeedSizePercent { get; }

	public ConfigEntry<float> EnemyLevelUpPercent { get; }

	public ConfigEntry<float> EventRatePercent { get; }

	public ConfigEntry<int> WorldLevel { get; }

	public ConfigEntry<string> PassiveMobs { get; }

	public ConfigEntry<string> PlayerEvents { get; }

	public ConfigEntry<float> StaminaPercent { get; }

	public ConfigEntry<float> MoveStaminaPercent { get; }

	public ConfigEntry<float> StaminaRegenPercent { get; }

	public ConfigEntry<float> EitrPercent { get; }

	public ConfigEntry<float> AdrenalinePercent { get; }

	public ConfigEntry<float> FoodPercent { get; }

	public ConfigEntry<float> DurabilityPercent { get; }

	public ConfigEntry<float> CarryWeightPercent { get; }

	public ConfigEntry<float> ResourceRatePercent { get; }

	public ConfigEntry<bool> CategoryGroundAmp { get; }

	public ConfigEntry<bool> SnapExtraDropsToGround { get; }

	public ConfigEntry<float> ExtraDropScatterForce { get; }

	public ConfigEntry<float> OtherDropMultiplier { get; }

	public ConfigEntry<float> WoodDropMultiplier { get; }

	public ConfigEntry<float> FineWoodDropMultiplier { get; }

	public ConfigEntry<float> CoreWoodDropMultiplier { get; }

	public ConfigEntry<float> SpecialWoodDropMultiplier { get; }

	public ConfigEntry<float> OreDropMultiplier { get; }

	public ConfigEntry<float> ScrapDropMultiplier { get; }

	public ConfigEntry<float> StoneDropMultiplier { get; }

	public ConfigEntry<float> FlintDropMultiplier { get; }

	public ConfigEntry<float> CrystalDropMultiplier { get; }

	public ConfigEntry<float> FuelDropMultiplier { get; }

	public ConfigEntry<float> GemDropMultiplier { get; }

	public ConfigEntry<float> BossDropMultiplier { get; }

	public ConfigEntry<float> CropDropMultiplier { get; }

	public ConfigEntry<float> SeedDropMultiplier { get; }

	public ConfigEntry<float> MushroomDropMultiplier { get; }

	public ConfigEntry<float> BerryHoneyDropMultiplier { get; }

	public ConfigEntry<float> FishDropMultiplier { get; }

	public ConfigEntry<float> PotionDropMultiplier { get; }

	public ConfigEntry<float> HideDropMultiplier { get; }

	public ConfigEntry<float> TrophyDropMultiplier { get; }

	public ConfigEntry<float> MeatDropMultiplier { get; }

	public ConfigEntry<float> PartsDropMultiplier { get; }

	public ConfigEntry<float> FeatherDropMultiplier { get; }

	public ConfigEntry<float> SpecialPartsDropMultiplier { get; }

	public ConfigEntry<string> DeathKeepEquip { get; }

	public ConfigEntry<string> DeathKeepInventory { get; }

	public ConfigEntry<string> DeathDeleteItems { get; }

	public ConfigEntry<string> DeathDeleteUnequipped { get; }

	public ConfigEntry<string> DeathSkillsReset { get; }

	public ConfigEntry<string> NoBuildCost { get; }

	public ConfigEntry<string> NoCraftCost { get; }

	public ConfigEntry<string> AllPiecesUnlocked { get; }

	public ConfigEntry<string> AllRecipesUnlocked { get; }

	public ConfigEntry<string> NoWorkbench { get; }

	public ConfigEntry<string> WorldLevelLockedTools { get; }

	public ConfigEntry<string> NoMap { get; }

	public ConfigEntry<string> NoPortals { get; }

	public ConfigEntry<string> NoBossPortals { get; }

	public ConfigEntry<string> TeleportAll { get; }

	public ConfigEntry<string> DungeonBuild { get; }

	public ConfigEntry<string> NoPseudoDrops { get; }

	public ConfigEntry<string> NoBuildingFall { get; }

	public ConfigEntry<string> NoHeavySnow { get; }

	public ConfigEntry<string> AllHeavySnow { get; }

	public ConfigEntry<string> Fire { get; }

	public ConfigEntry<float> SmelterSpeedMultiplier { get; }

	public ConfigEntry<float> FermenterSpeedMultiplier { get; }

	public ConfigEntry<float> CookingSpeedMultiplier { get; }

	public ConfigEntry<float> PlantGrowSpeedMultiplier { get; }

	public ModConfig(ConfigFile file)
	{
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Expected O, but got Unknown
		//IL_0324: Unknown result type (might be due to invalid IL or missing references)
		//IL_032e: Expected O, but got Unknown
		Enabled = file.Bind<bool>("1 - General", "Enabled", true, "Master switch. Dedicated server only; vanilla clients need no install.");
		ChatCommandsEnabled = file.Bind<bool>("1 - General", "ChatCommandsEnabled", true, "Allow rate commands in chat (for console players). No admin check — anyone can use. Prefix below.");
		ChatCommandPrefix = file.Bind<string>("1 - General", "ChatCommandPrefix", "!", "Chat prefix, e.g. !sr_wood 9 or !rates status");
		SkillGainPercent = Percent(file, "2 - Skills", "SkillGainPercent", 100f, "XP for ALL skills. 100 = vanilla, 200 = x2, 1000 = x10.");
		SkillReductionPercent = Percent(file, "2 - Skills", "SkillReductionPercent", 100f, "Skill loss on death. 100 = vanilla, 0 = none.");
		PlayerDamagePercent = Percent(file, "3 - Combat", "PlayerDamagePercent", 100f, "Damage players deal. 100 = vanilla.");
		EnemyDamagePercent = Percent(file, "3 - Combat", "EnemyDamagePercent", 100f, "Damage enemies deal. 100 = vanilla.");
		EnemySpeedSizePercent = Percent(file, "3 - Combat", "EnemySpeedSizePercent", 100f, "Enemy move speed / size scaling. 100 = vanilla.");
		EnemyLevelUpPercent = Percent(file, "3 - Combat", "EnemyLevelUpPercent", 100f, "How fast enemies star-up. 100 = vanilla.");
		EventRatePercent = Percent(file, "3 - Combat", "EventRatePercent", 100f, "Raid / event frequency. 100 = vanilla, 0 = none.");
		WorldLevel = file.Bind<int>("3 - Combat", "WorldLevel", 0, new ConfigDescription("World level 0-10. 0 = vanilla start.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>()));
		PassiveMobs = Toggle(file, "3 - Combat", "PassiveMobs", "On = mobs do not attack. Unchanged = leave vanilla/world setting.");
		PlayerEvents = Toggle(file, "3 - Combat", "PlayerEvents", "Player-based raid events.");
		StaminaPercent = Percent(file, "4 - Survival", "StaminaPercent", 100f, "Stamina drain. Lower = less drain. 100 = vanilla.");
		MoveStaminaPercent = Percent(file, "4 - Survival", "MoveStaminaPercent", 100f, "Movement stamina drain. 100 = vanilla.");
		StaminaRegenPercent = Percent(file, "4 - Survival", "StaminaRegenPercent", 100f, "Stamina regen. Higher = faster. 100 = vanilla.");
		EitrPercent = Percent(file, "4 - Survival", "EitrPercent", 100f, "Eitr rate. 100 = vanilla.");
		AdrenalinePercent = Percent(file, "4 - Survival", "AdrenalinePercent", 100f, "Adrenaline rate. 100 = vanilla.");
		FoodPercent = Percent(file, "4 - Survival", "FoodPercent", 100f, "Food duration scaling. 100 = vanilla.");
		DurabilityPercent = Percent(file, "4 - Survival", "DurabilityPercent", 100f, "Tool/weapon durability loss. Lower = lasts longer. 100 = vanilla.");
		CarryWeightPercent = Percent(file, "4 - Survival", "CarryWeightPercent", 100f, "Carry weight. 200 = x2 capacity. 100 = vanilla.");
		ResourceRatePercent = Percent(file, "5 - Resources", "ResourceRatePercent", 100f, "GLOBAL drop rate (vanilla World Modifier ResourceRate). 100 = vanilla, 200 = x2. Synced to all clients.");
		CategoryGroundAmp = file.Bind<bool>("6 - Loot", "CategoryGroundAmp", true, "Server amplifies categorized ground loot after it spawns (vanilla/PS5 clients OK). Player-thrown items are skipped.");
		SnapExtraDropsToGround = file.Bind<bool>("6 - Loot", "SnapExtraDropsToGround", true, "Place amplified extra piles on the ground (fixes floating server extras).");
		ExtraDropScatterForce = file.Bind<float>("6 - Loot", "ExtraDropScatterForce", 4f, new ConfigDescription("Physics push on extra piles (vanilla mob loot uses ~5). 0 = no push.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
		OtherDropMultiplier = Mult(file, "6 - Loot", "OtherDropMultiplier", LegacyFloat(file, "OtherDropMultiplier", 1f), "Anything not matched below. Keep at 1 unless you want everything amplified.");
		float def = LegacyFloat(file, "WoodDropMultiplier", 1f);
		WoodDropMultiplier = Mult(file, "6a - Materials", "WoodDropMultiplier", def, "Basic wood.");
		FineWoodDropMultiplier = Mult(file, "6a - Materials", "FineWoodDropMultiplier", def, "Fine wood.");
		CoreWoodDropMultiplier = Mult(file, "6a - Materials", "CoreWoodDropMultiplier", def, "Core wood (RoundLog).");
		SpecialWoodDropMultiplier = Mult(file, "6a - Materials", "SpecialWoodDropMultiplier", def, "Elder bark, Yggdrasil, Ash, Black wood.");
		float def2 = LegacyFloat(file, "OreDropMultiplier", 1f);
		OreDropMultiplier = Mult(file, "6a - Materials", "OreDropMultiplier", def2, "Raw ores.");
		ScrapDropMultiplier = Mult(file, "6a - Materials", "ScrapDropMultiplier", def2, "Iron/black metal/copper scrap.");
		float def3 = LegacyFloat(file, "StoneDropMultiplier", 1f);
		StoneDropMultiplier = Mult(file, "6a - Materials", "StoneDropMultiplier", def3, "Stone and similar.");
		FlintDropMultiplier = Mult(file, "6a - Materials", "FlintDropMultiplier", def3, "Flint.");
		CrystalDropMultiplier = Mult(file, "6a - Materials", "CrystalDropMultiplier", def3, "Crystal, obsidian.");
		FuelDropMultiplier = Mult(file, "6a - Materials", "FuelDropMultiplier", LegacyFloat(file, "FuelDropMultiplier", 1f), "Coal, resin, tar.");
		GemDropMultiplier = Mult(file, "6a - Materials", "GemDropMultiplier", LegacyFloat(file, "GemDropMultiplier", 1f), "Coins, amber, rubies, necklace.");
		BossDropMultiplier = Mult(file, "6a - Materials", "BossDropMultiplier", LegacyFloat(file, "BossDropMultiplier", 1f), "Boss materials and boss trophies.");
		float def4 = LegacyFloat(file, "CropDropMultiplier", 1f);
		CropDropMultiplier = Mult(file, "6b - Consumables", "CropDropMultiplier", def4, "Crops / plants when they spawn as ground ItemDrops.");
		SeedDropMultiplier = Mult(file, "6b - Consumables", "SeedDropMultiplier", def4, "Seeds and cones.");
		MushroomDropMultiplier = Mult(file, "6b - Consumables", "MushroomDropMultiplier", 1f, "Mushrooms / magecap.");
		BerryHoneyDropMultiplier = Mult(file, "6b - Consumables", "BerryHoneyDropMultiplier", 1f, "Berries, honey.");
		FishDropMultiplier = Mult(file, "6b - Consumables", "FishDropMultiplier", 1f, "Fish and similar.");
		PotionDropMultiplier = Mult(file, "6b - Consumables", "PotionDropMultiplier", 1f, "Meads / potions if they land as ground loot.");
		HideDropMultiplier = Mult(file, "6c - Mob Drops", "HideDropMultiplier", LegacyFloat(file, "HideDropMultiplier", 1f), "Hides, leather, pelts, scales.");
		TrophyDropMultiplier = Mult(file, "6c - Mob Drops", "TrophyDropMultiplier", 1f, "Normal mob trophies.");
		MeatDropMultiplier = Mult(file, "6c - Mob Drops", "MeatDropMultiplier", 1f, "Raw / cooked meat drops.");
		PartsDropMultiplier = Mult(file, "6c - Mob Drops", "PartsDropMultiplier", LegacyFloat(file, "PartsDropMultiplier", 1f), "Bones, entrails, eyes, glands, fangs.");
		FeatherDropMultiplier = Mult(file, "6c - Mob Drops", "FeatherDropMultiplier", 1f, "Feathers.");
		SpecialPartsDropMultiplier = Mult(file, "6c - Mob Drops", "SpecialPartsDropMultiplier", 1f, "Guck, ooze, soft tissue, carapace, bile, …");
		DeathKeepEquip = Toggle(file, "7 - Death", "DeathKeepEquip", "Keep equipped gear on death.");
		DeathKeepInventory = Toggle(file, "7 - Death", "DeathKeepInventory", "Keep full inventory on death.");
		DeathDeleteItems = Toggle(file, "7 - Death", "DeathDeleteItems", "Delete items on death.");
		DeathDeleteUnequipped = Toggle(file, "7 - Death", "DeathDeleteUnequipped", "Delete unequipped items on death.");
		DeathSkillsReset = Toggle(file, "7 - Death", "DeathSkillsReset", "Reset skills on death.");
		NoBuildCost = Toggle(file, "8 - Build Craft", "NoBuildCost", "Building costs nothing.");
		NoCraftCost = Toggle(file, "8 - Build Craft", "NoCraftCost", "Crafting costs nothing.");
		AllPiecesUnlocked = Toggle(file, "8 - Build Craft", "AllPiecesUnlocked", "All build pieces unlocked.");
		AllRecipesUnlocked = Toggle(file, "8 - Build Craft", "AllRecipesUnlocked", "All recipes unlocked.");
		NoWorkbench = Toggle(file, "8 - Build Craft", "NoWorkbench", "No workbench range required.");
		WorldLevelLockedTools = Toggle(file, "8 - Build Craft", "WorldLevelLockedTools", "World-level tool locks.");
		NoMap = Toggle(file, "9 - Map Portals", "NoMap", "Disable map.");
		NoPortals = Toggle(file, "9 - Map Portals", "NoPortals", "Disable portals.");
		NoBossPortals = Toggle(file, "9 - Map Portals", "NoBossPortals", "Disable boss portals.");
		TeleportAll = Toggle(file, "9 - Map Portals", "TeleportAll", "Teleport with ores/metals.");
		DungeonBuild = Toggle(file, "9 - Map Portals", "DungeonBuild", "Allow building in dungeons.");
		NoPseudoDrops = Toggle(file, "10 - World Flags", "NoPseudoDrops", "Disable pseudo drops.");
		NoBuildingFall = Toggle(file, "10 - World Flags", "NoBuildingFall", "Buildings do not fall.");
		NoHeavySnow = Toggle(file, "10 - World Flags", "NoHeavySnow", "No heavy snow.");
		AllHeavySnow = Toggle(file, "10 - World Flags", "AllHeavySnow", "Force heavy snow rules.");
		Fire = Toggle(file, "10 - World Flags", "Fire", "Fire-related world flag.");
		SmelterSpeedMultiplier = Mult(file, "11 - Stations", "SmelterSpeedMultiplier", 1f, "Smelter / kiln / similar. 2 = twice as fast.");
		FermenterSpeedMultiplier = Mult(file, "11 - Stations", "FermenterSpeedMultiplier", 1f, "Fermenter speed. 2 = twice as fast.");
		CookingSpeedMultiplier = Mult(file, "11 - Stations", "CookingSpeedMultiplier", 1f, "Cooking station speed. 2 = twice as fast.");
		PlantGrowSpeedMultiplier = Mult(file, "11 - Stations", "PlantGrowSpeedMultiplier", 1f, "Plant grow speed. 2 = twice as fast (half grow time).");
	}

	private static ConfigEntry<float> Percent(ConfigFile file, string section, string key, float def, string desc)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected O, but got Unknown
		return file.Bind<float>(section, key, def, new ConfigDescription(desc, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10000f), Array.Empty<object>()));
	}

	private static ConfigEntry<float> Mult(ConfigFile file, string section, string key, float def, string desc)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		return file.Bind<float>(section, key, def, new ConfigDescription(desc + " 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
	}

	private static float LegacyFloat(ConfigFile file, string key, float fallback)
	{
		try
		{
			string configFilePath = file.ConfigFilePath;
			if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
			{
				return fallback;
			}
			string text = null;
			string[] array = File.ReadAllLines(configFilePath);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2.StartsWith("#"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					text = text2.Substring(1, text2.Length - 2);
				}
				else
				{
					if (text != "6 - Drop Categories")
					{
						continue;
					}
					int num = text2.IndexOf('=');
					if (num > 0 && string.Equals(text2.Substring(0, num).Trim(), key, StringComparison.OrdinalIgnoreCase))
					{
						string text3 = text2.Substring(num + 1).Trim();
						int num2 = text3.IndexOf('#');
						if (num2 >= 0)
						{
							text3 = text3.Substring(0, num2).Trim();
						}
						if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
						{
							return result;
						}
					}
				}
			}
		}
		catch
		{
		}
		return fallback;
	}

	private static ConfigEntry<string> Toggle(ConfigFile file, string section, string key, string desc)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected O, but got Unknown
		return file.Bind<string>(section, key, "Unchanged", new ConfigDescription(desc + " Values: Unchanged | On | Off", (AcceptableValueBase)(object)ToggleValues, Array.Empty<object>()));
	}
}
[HarmonyPatch(typeof(ZNet), "Awake")]
internal static class ZNetAwakePatch
{
	private static void Postfix(ZNet __instance)
	{
		Plugin.TryActivate(__instance);
	}
}
[HarmonyPatch(typeof(ZoneSystem), "Start")]
internal static class ZoneSystemStartPatch
{
	private static void Postfix()
	{
		if ((Object)(object)ZNet.instance != (Object)null)
		{
			Plugin.TryActivate(ZNet.instance);
		}
		RateApplier.ApplyGlobalKeys();
	}
}
[HarmonyPatch(typeof(Game), "UpdateWorldRates")]
internal static class UpdateWorldRatesPatch
{
	private static void Postfix()
	{
		RateApplier.ApplyGlobalKeys();
	}
}
internal static class DropScale
{
	public static bool Ready()
	{
		if (Plugin.Active && Plugin.Settings != null && Plugin.Settings.Enabled.Value && (Object)(object)ZNet.instance != (Object)null)
		{
			return ZNet.instance.IsDedicated();
		}
		return false;
	}
}
[HarmonyPatch(typeof(Smelter), "GetDeltaTime")]
internal static class SmelterSpeedPatch
{
	private static void Postfix(ref double __result)
	{
		float num = StationRates.Mult((Plugin.Settings != null) ? Plugin.Settings.SmelterSpeedMultiplier.Value : 1f);
		if (num > 0f && !Mathf.Approximately(num, 1f))
		{
			__result *= num;
		}
	}
}
[HarmonyPatch(typeof(Fermenter), "GetFermentationTime")]
internal static class FermenterSpeedPatch
{
	private static void Postfix(ref double __result)
	{
		float num = StationRates.Mult((Plugin.Settings != null) ? Plugin.Settings.FermenterSpeedMultiplier.Value : 1f);
		if (num > 0f && !Mathf.Approximately(num, 1f))
		{
			__result *= num;
		}
	}
}
[HarmonyPatch(typeof(CookingStation), "GetDeltaTime")]
internal static class CookingSpeedPatch
{
	private static void Postfix(ref float __result)
	{
		float num = StationRates.Mult((Plugin.Settings != null) ? Plugin.Settings.CookingSpeedMultiplier.Value : 1f);
		if (num > 0f && !Mathf.Approximately(num, 1f))
		{
			__result *= num;
		}
	}
}
[HarmonyPatch(typeof(Plant), "GetGrowTime")]
internal static class PlantGrowSpeedPatch
{
	private static void Postfix(ref float __result)
	{
		float num = StationRates.Mult((Plugin.Settings != null) ? Plugin.Settings.PlantGrowSpeedMultiplier.Value : 1f);
		if (num > 0f && !Mathf.Approximately(num, 1f))
		{
			__result /= num;
		}
	}
}
internal static class StationRates
{
	public static float Mult(float value)
	{
		if (!DropScale.Ready())
		{
			return 1f;
		}
		return Mathf.Max(0.01f, value);
	}
}
[BepInPlugin("com.morda.serverrates", "ServerRates", "1.4.9")]
public class Plugin : BaseUnityPlugin
{
	public const string ModGuid = "com.morda.serverrates";

	public const string ModName = "ServerRates";

	public const string ModVersion = "1.4.9";

	public const string ModAuthor = "Morda";

	private Harmony _harmony;

	internal static Plugin Instance { get; private set; }

	internal static ManualLogSource Log { get; private set; }

	internal static ModConfig Settings { get; private set; }

	internal static bool Active { get; private set; }

	private void Awake()
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Settings = new ModConfig(((BaseUnityPlugin)this).Config);
		_harmony = new Harmony("com.morda.serverrates");
		_harmony.PatchAll(Assembly.GetExecutingAssembly());
		ConfigWatch.Start();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"ServerRates v1.4.9 by Morda loaded (dedicated server only — vanilla clients OK).");
	}

	private void Update()
	{
		ConfigWatch.Tick();
		if (Active)
		{
			CategoryGroundAmp.Tick();
		}
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
		Active = false;
	}

	internal static void TryActivate(ZNet net)
	{
		if ((Object)(object)net == (Object)null || !net.IsDedicated())
		{
			Active = false;
			if ((Object)(object)net != (Object)null && net.IsServer() && !net.IsDedicated())
			{
				Log.LogWarning((object)"ServerRates: listen/host worlds are not supported. Use a dedicated server.");
			}
		}
		else
		{
			Active = true;
			Log.LogInfo((object)"ServerRates: dedicated server detected — rates enabled.");
			RateApplier.ApplyGlobalKeys();
			CategoryGroundAmp.LogStatus();
			RatesCommands.Register();
		}
	}
}
internal static class RateApplier
{
	private static bool _busy;

	private static bool _logged;

	public static void ApplyGlobalKeys()
	{
		if (_busy || !Plugin.Active || Plugin.Settings == null || !Plugin.Settings.Enabled.Value || (Object)(object)ZoneSystem.instance == (Object)null)
		{
			return;
		}
		ModConfig settings = Plugin.Settings;
		_busy = true;
		try
		{
			bool flag = false;
			flag |= SetScalar((GlobalKeys)12, settings.SkillGainPercent.Value);
			flag |= SetScalar((GlobalKeys)13, settings.SkillReductionPercent.Value);
			flag |= SetScalar((GlobalKeys)0, settings.PlayerDamagePercent.Value);
			flag |= SetScalar((GlobalKeys)1, settings.EnemyDamagePercent.Value);
			flag |= SetScalar((GlobalKeys)14, settings.EnemySpeedSizePercent.Value);
			flag |= SetScalar((GlobalKeys)15, settings.EnemyLevelUpPercent.Value);
			flag |= SetScalar((GlobalKeys)3, settings.EventRatePercent.Value);
			flag |= SetWorldLevel(settings.WorldLevel.Value);
			flag |= SetScalar((GlobalKeys)5, settings.StaminaPercent.Value);
			flag |= SetScalar((GlobalKeys)10, settings.MoveStaminaPercent.Value);
			flag |= SetScalar((GlobalKeys)11, settings.StaminaRegenPercent.Value);
			flag |= SetScalar((GlobalKeys)7, settings.EitrPercent.Value);
			flag |= SetScalar((GlobalKeys)6, settings.AdrenalinePercent.Value);
			flag |= SetScalar((GlobalKeys)9, settings.FoodPercent.Value);
			flag |= SetScalar((GlobalKeys)8, settings.DurabilityPercent.Value);
			flag |= SetScalar((GlobalKeys)16, settings.CarryWeightPercent.Value);
			flag |= SetScalar((GlobalKeys)4, settings.ResourceRatePercent.Value);
			flag |= SetToggle((GlobalKeys)30, settings.PassiveMobs.Value);
			flag |= SetToggle((GlobalKeys)17, settings.PlayerEvents.Value);
			flag |= SetToggle((GlobalKeys)19, settings.DeathKeepEquip.Value);
			flag |= SetToggle((GlobalKeys)23, settings.DeathKeepInventory.Value);
			flag |= SetToggle((GlobalKeys)20, settings.DeathDeleteItems.Value);
			flag |= SetToggle((GlobalKeys)21, settings.DeathDeleteUnequipped.Value);
			flag |= SetToggle((GlobalKeys)22, settings.DeathSkillsReset.Value);
			flag |= SetToggle((GlobalKeys)24, settings.NoBuildCost.Value);
			flag |= SetToggle((GlobalKeys)25, settings.NoCraftCost.Value);
			flag |= SetToggle((GlobalKeys)26, settings.AllPiecesUnlocked.Value);
			flag |= SetToggle((GlobalKeys)28, settings.AllRecipesUnlocked.Value);
			flag |= SetToggle((GlobalKeys)27, settings.NoWorkbench.Value);
			flag |= SetToggle((GlobalKeys)29, settings.WorldLevelLockedTools.Value);
			flag |= SetToggle((GlobalKeys)31, settings.NoMap.Value);
			flag |= SetToggle((GlobalKeys)32, settings.NoPortals.Value);
			flag |= SetToggle((GlobalKeys)33, settings.NoBossPortals.Value);
			flag |= SetToggle((GlobalKeys)35, settings.TeleportAll.Value);
			flag |= SetToggle((GlobalKeys)34, settings.DungeonBuild.Value);
			flag |= SetToggle((GlobalKeys)36, settings.NoPseudoDrops.Value);
			flag |= SetToggle((GlobalKeys)37, settings.NoBuildingFall.Value);
			flag |= SetToggle((GlobalKeys)38, settings.NoHeavySnow.Value);
			flag |= SetToggle((GlobalKeys)39, settings.AllHeavySnow.Value);
			flag |= SetToggle((GlobalKeys)18, settings.Fire.Value);
			if (!_logged)
			{
				_logged = true;
				Plugin.Log.LogInfo((object)"ServerRates global keys applied (percent keys: 100 = vanilla).");
			}
			else if (flag)
			{
				Plugin.Log.LogInfo((object)"ServerRates global keys refreshed live (no restart).");
			}
		}
		finally
		{
			_busy = false;
		}
	}

	public static void NotifyConfigReloaded()
	{
		_logged = false;
	}

	private static bool SetScalar(GlobalKeys key, float value)
	{
		//IL_0012: 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)
		value = Mathf.Max(0f, value);
		float num = default(float);
		if (ZoneSystem.instance.GetGlobalKey(key, ref num) && Mathf.Abs(num - value) < 0.001f)
		{
			return false;
		}
		ZoneSystem.instance.SetGlobalKey(key, value);
		return true;
	}

	private static bool SetWorldLevel(int level)
	{
		level = Mathf.Clamp(level, 0, 10);
		float num = default(float);
		if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)2, ref num) && Mathf.Abs(num - (float)level) < 0.001f)
		{
			return false;
		}
		ZoneSystem.instance.SetGlobalKey((GlobalKeys)2, (float)level);
		return true;
	}

	private static bool SetToggle(GlobalKeys key, string mode)
	{
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		string text = (mode ?? "Unchanged").Trim();
		if (text.Equals("Unchanged", StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		bool flag = text.Equals("On", StringComparison.OrdinalIgnoreCase) || text.Equals("True", StringComparison.OrdinalIgnoreCase) || text.Equals("1", StringComparison.OrdinalIgnoreCase);
		bool flag2 = text.Equals("Off", StringComparison.OrdinalIgnoreCase) || text.Equals("False", StringComparison.OrdinalIgnoreCase) || text.Equals("0", StringComparison.OrdinalIgnoreCase);
		if (!flag && !flag2)
		{
			return false;
		}
		bool globalKey = ZoneSystem.instance.GetGlobalKey(key);
		if (flag)
		{
			if (globalKey)
			{
				return false;
			}
			ZoneSystem.instance.SetGlobalKey(key);
			return true;
		}
		if (!globalKey)
		{
			return false;
		}
		ZoneSystem.instance.RemoveGlobalKey(key);
		return true;
	}
}
internal static class RatesChat
{
	[HarmonyPatch(typeof(Chat), "RPC_ChatMessage")]
	private static class ChatRpcPatch
	{
		private static bool Prefix(long sender, Vector3 position, int type, UserInfo userInfo, string text)
		{
			if (!Plugin.Active || Plugin.Settings == null)
			{
				return true;
			}
			if (!Plugin.Settings.ChatCommandsEnabled.Value)
			{
				return true;
			}
			if (string.IsNullOrWhiteSpace(text))
			{
				return true;
			}
			string text2 = Plugin.Settings.ChatCommandPrefix.Value ?? "!";
			if (string.IsNullOrEmpty(text2))
			{
				text2 = "!";
			}
			string text3 = text.Trim();
			if (!text3.StartsWith(text2, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			string text4 = text3.Substring(text2.Length).Trim();
			if (text4.Length == 0)
			{
				return true;
			}
			if (!LooksLikeRatesCommand(text4))
			{
				return true;
			}
			string text5 = ((userInfo != null && !string.IsNullOrEmpty(userInfo.Name)) ? userInfo.Name : "player");
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("ServerRates chat cmd from " + text5 + ": " + text4));
			}
			RatesCommands.EnsureMaps();
			RatesCommands.ProcessLine(text4, delegate(string s)
			{
				RatesFeedback.Say(s, yellowHud: false);
			});
			return false;
		}
	}

	private static bool LooksLikeRatesCommand(string body)
	{
		string text = body.ToLowerInvariant();
		if (text.StartsWith("rates") || text.StartsWith("sr_") || text.StartsWith("sr "))
		{
			return true;
		}
		int num = body.IndexOf(' ');
		return RatesCommands.IsKnownShortOrKey((num > 0) ? body.Substring(0, num) : body);
	}
}
internal static class RatesCommands
{
	private static bool _added;

	private static string[] _keyCache;

	private static readonly Dictionary<string, string> ShortToKey = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, string> KeyToShort = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

	private static bool _mapBuilt;

	public static void EnsureMaps()
	{
		if (!_mapBuilt)
		{
			BuildShortNameMap();
			_mapBuilt = true;
		}
	}

	public static bool IsKnownShortOrKey(string token)
	{
		EnsureMaps();
		if (string.IsNullOrEmpty(token))
		{
			return false;
		}
		if (ShortToKey.ContainsKey(token))
		{
			return true;
		}
		if (token.StartsWith("sr_", StringComparison.OrdinalIgnoreCase) && ShortToKey.ContainsKey(token.Substring(3)))
		{
			return true;
		}
		return FindProp(token) != null;
	}

	public static void ProcessLine(string line, Action<string> reply)
	{
		if (reply == null)
		{
			return;
		}
		EnsureMaps();
		if (!Plugin.Active || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated())
		{
			reply("ServerRates: commands only work on the dedicated server.");
			return;
		}
		string[] array = SplitArgs(line);
		if (array.Length == 0)
		{
			PrintHelp(reply);
			return;
		}
		string text = array[0];
		string text2 = text.ToLowerInvariant();
		if (text2.StartsWith("sr_") || ShortToKey.ContainsKey(text) || FindProp(text) != null)
		{
			string text3 = ResolveKey(text);
			if (text3 == null)
			{
				reply("ServerRates: unknown '" + text + "' — try !rates cmds");
				return;
			}
			string value;
			string text4 = (KeyToShort.TryGetValue(text3, out value) ? value : text);
			if (array.Length < 2)
			{
				if (TryGet(text3, out var text5, out var error))
				{
					reply("ServerRates: " + text5 + "  (set: !sr_" + text4 + " <value>)");
				}
				else
				{
					reply("rates get failed: " + error);
				}
			}
			else
			{
				ApplySet(reply, text3, JoinParts(array, 1));
			}
		}
		else if (text2 == "rates")
		{
			ProcessRatesMeta(array, reply);
		}
		else
		{
			PrintHelp(reply);
		}
	}

	private static void ProcessRatesMeta(string[] parts, Action<string> reply)
	{
		if (parts.Length < 2)
		{
			PrintHelp(reply);
			return;
		}
		string text = parts[1].ToLowerInvariant();
		if (ShortToKey.TryGetValue(text, out var _) || KeyToShort.ContainsKey(parts[1]))
		{
			string value2;
			string text2 = (ShortToKey.TryGetValue(text, out value2) ? value2 : FindProp(parts[1])?.Name);
			if (text2 == null)
			{
				PrintHelp(reply);
			}
			else if (parts.Length < 3)
			{
				if (TryGet(text2, out var text3, out var error))
				{
					reply("ServerRates: " + text3);
				}
				else
				{
					reply("rates get failed: " + error);
				}
			}
			else
			{
				ApplySet(reply, text2, JoinParts(parts, 2));
			}
			return;
		}
		switch (text)
		{
		case "help":
		case "?":
			PrintHelp(reply);
			break;
		case "status":
			PrintStatus(reply, all: false);
			break;
		case "list":
		case "keys":
			PrintStatus(reply, all: true);
			break;
		case "cmds":
		case "commands":
			PrintAllCommands(reply);
			break;
		case "get":
		{
			if (parts.Length < 3)
			{
				reply("Usage: !rates get <Key|short>");
				break;
			}
			string text5 = ResolveKey(parts[2]);
			string text6;
			string error2;
			if (text5 == null)
			{
				reply("unknown key '" + parts[2] + "' (!rates cmds)");
			}
			else if (!TryGet(text5, out text6, out error2))
			{
				reply("rates get failed: " + error2);
			}
			else
			{
				reply("ServerRates: " + text6);
			}
			break;
		}
		case "set":
		case "apply":
		{
			if (parts.Length < 4)
			{
				reply("Usage: !rates set <Key|short> <value>");
				break;
			}
			string text4 = ResolveKey(parts[2]);
			if (text4 == null)
			{
				reply("unknown key '" + parts[2] + "' (!rates cmds)");
			}
			else
			{
				ApplySet(reply, text4, JoinParts(parts, 3));
			}
			break;
		}
		default:
			PrintHelp(reply);
			break;
		}
	}

	private static string[] SplitArgs(string line)
	{
		if (string.IsNullOrWhiteSpace(line))
		{
			return Array.Empty<string>();
		}
		return line.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
	}

	private static string JoinParts(string[] parts, int startIndex)
	{
		if (parts == null || startIndex >= parts.Length)
		{
			return "";
		}
		if (startIndex == parts.Length - 1)
		{
			return parts[startIndex];
		}
		return string.Join(" ", parts, startIndex, parts.Length - startIndex);
	}

	public static void Register()
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		//IL_0043: Expected O, but got Unknown
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Expected O, but got Unknown
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		if (_added)
		{
			return;
		}
		_added = true;
		try
		{
			EnsureMaps();
			new ConsoleCommand("rates", "ServerRates: status | list | cmds | get <Key> | set <Key> <value> | help", new ConsoleEvent(OnRatesConsole), false, true, true, false, true, false, new ConsoleOptionsFetcher(FetchRatesTabOptions), true, true, false);
			foreach (KeyValuePair<string, string> item in ShortToKey)
			{
				string key = item.Key;
				string value = item.Value;
				string text = "sr_" + key;
				string keyCapture = value;
				string shortCapture = key;
				new ConsoleCommand(text, "ServerRates " + value + " — sr_" + shortCapture + " [value]", (ConsoleEvent)delegate(ConsoleEventArgs args)
				{
					OnSingleRate(args, keyCapture, shortCapture);
				}, false, true, true, false, true, false, (ConsoleOptionsFetcher)null, false, true, false);
			}
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("ServerRates: registered " + ShortToKey.Count + " rate commands (sr_* + chat !" + (Plugin.Settings?.ChatCommandPrefix?.Value ?? "!") + ")."));
			}
		}
		catch (Exception ex)
		{
			_added = false;
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogError((object)("ServerRates: failed to register console commands: " + ex));
			}
		}
	}

	private static void BuildShortNameMap()
	{
		ShortToKey.Clear();
		KeyToShort.Clear();
		AddShort("Enabled", "enabled");
		AddShort("SkillGainPercent", "skillgain");
		AddShort("SkillReductionPercent", "skillloss");
		AddShort("PlayerDamagePercent", "playerdmg");
		AddShort("EnemyDamagePercent", "enemydmg");
		AddShort("EnemySpeedSizePercent", "enemyspeed");
		AddShort("EnemyLevelUpPercent", "enemystars");
		AddShort("EventRatePercent", "events");
		AddShort("WorldLevel", "worldlevel");
		AddShort("PassiveMobs", "passivemobs");
		AddShort("PlayerEvents", "playerevents");
		AddShort("StaminaPercent", "stamina");
		AddShort("MoveStaminaPercent", "movestamina");
		AddShort("StaminaRegenPercent", "staminaregen");
		AddShort("EitrPercent", "eitr");
		AddShort("AdrenalinePercent", "adrenaline");
		AddShort("FoodPercent", "food");
		AddShort("DurabilityPercent", "durability");
		AddShort("CarryWeightPercent", "carry");
		AddShort("ResourceRatePercent", "resourcerate");
		AddShort("CategoryGroundAmp", "groundamp");
		AddShort("SnapExtraDropsToGround", "snapground");
		AddShort("ExtraDropScatterForce", "scatter");
		AddShort("OtherDropMultiplier", "other");
		AddShort("WoodDropMultiplier", "wood");
		AddShort("FineWoodDropMultiplier", "finewood");
		AddShort("CoreWoodDropMultiplier", "corewood");
		AddShort("SpecialWoodDropMultiplier", "specialwood");
		AddShort("OreDropMultiplier", "ore");
		AddShort("ScrapDropMultiplier", "scrap");
		AddShort("StoneDropMultiplier", "stone");
		AddShort("FlintDropMultiplier", "flint");
		AddShort("CrystalDropMultiplier", "crystal");
		AddShort("FuelDropMultiplier", "fuel");
		AddShort("GemDropMultiplier", "gems");
		AddShort("BossDropMultiplier", "boss");
		AddShort("CropDropMultiplier", "crops");
		AddShort("SeedDropMultiplier", "seeds");
		AddShort("MushroomDropMultiplier", "mushrooms");
		AddShort("BerryHoneyDropMultiplier", "berries");
		AddShort("FishDropMultiplier", "fish");
		AddShort("PotionDropMultiplier", "potions");
		AddShort("HideDropMultiplier", "hide");
		AddShort("TrophyDropMultiplier", "trophy");
		AddShort("MeatDropMultiplier", "meat");
		AddShort("PartsDropMultiplier", "parts");
		AddShort("FeatherDropMultiplier", "feathers");
		AddShort("SpecialPartsDropMultiplier", "specialparts");
		AddShort("DeathKeepEquip", "keepequip");
		AddShort("DeathKeepInventory", "keepinv");
		AddShort("DeathDeleteItems", "deleteitems");
		AddShort("DeathDeleteUnequipped", "deleteunequipped");
		AddShort("DeathSkillsReset", "skillreset");
		AddShort("NoBuildCost", "nobuildcost");
		AddShort("NoCraftCost", "nocraftcost");
		AddShort("AllPiecesUnlocked", "allpieces");
		AddShort("AllRecipesUnlocked", "allrecipes");
		AddShort("NoWorkbench", "noworkbench");
		AddShort("WorldLevelLockedTools", "toollocks");
		AddShort("NoMap", "nomap");
		AddShort("NoPortals", "noportals");
		AddShort("NoBossPortals", "nobossportals");
		AddShort("TeleportAll", "teleportall");
		AddShort("DungeonBuild", "dungeonbuild");
		AddShort("NoPseudoDrops", "nopseudo");
		AddShort("NoBuildingFall", "nobuildfall");
		AddShort("NoHeavySnow", "noheavysnow");
		AddShort("AllHeavySnow", "allheavysnow");
		AddShort("Fire", "fire");
		AddShort("SmelterSpeedMultiplier", "smelter");
		AddShort("FermenterSpeedMultiplier", "fermenter");
		AddShort("CookingSpeedMultiplier", "cooking");
		AddShort("PlantGrowSpeedMultiplier", "plants");
		PropertyInfo[] properties = typeof(ModConfig).GetProperties(BindingFlags.Instance | BindingFlags.Public);
		foreach (PropertyInfo propertyInfo in properties)
		{
			if (IsConfigEntry(propertyInfo.PropertyType) && !KeyToShort.ContainsKey(propertyInfo.Name))
			{
				string shortName = AutoShort(propertyInfo.Name);
				AddShort(propertyInfo.Name, shortName);
			}
		}
	}

	private static void AddShort(string propName, string shortName)
	{
		if (ShortToKey.ContainsKey(shortName))
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("ServerRates: duplicate short name '" + shortName + "' for " + propName));
			}
			shortName += "2";
		}
		ShortToKey[shortName] = propName;
		KeyToShort[propName] = shortName;
	}

	private static string AutoShort(string propName)
	{
		string text = propName;
		if (text.EndsWith("DropMultiplier", StringComparison.Ordinal))
		{
			text = text.Substring(0, text.Length - "DropMultiplier".Length);
		}
		else if (text.EndsWith("SpeedMultiplier", StringComparison.Ordinal))
		{
			text = text.Substring(0, text.Length - "SpeedMultiplier".Length) + "speed";
		}
		else if (text.EndsWith("Percent", StringComparison.Ordinal))
		{
			text = text.Substring(0, text.Length - "Percent".Length);
		}
		else if (text.EndsWith("Multiplier", StringComparison.Ordinal))
		{
			text = text.Substring(0, text.Length - "Multiplier".Length);
		}
		return text.ToLowerInvariant();
	}

	private static List<string> FetchRatesTabOptions()
	{
		List<string> list = new List<string>();
		list.Add("status");
		list.Add("list");
		list.Add("cmds");
		list.Add("get");
		list.Add("set");
		list.Add("help");
		list.AddRange(ShortToKey.Keys);
		list.AddRange(GetAllKeys());
		return list;
	}

	private static string[] GetAllKeys()
	{
		if (_keyCache != null)
		{
			return _keyCache;
		}
		List<string> list = new List<string>();
		PropertyInfo[] properties = typeof(ModConfig).GetProperties(BindingFlags.Instance | BindingFlags.Public);
		foreach (PropertyInfo propertyInfo in properties)
		{
			if (IsConfigEntry(propertyInfo.PropertyType))
			{
				list.Add(propertyInfo.Name);
			}
		}
		list.Sort(StringComparer.OrdinalIgnoreCase);
		_keyCache = list.ToArray();
		return _keyCache;
	}

	private static bool IsConfigEntry(Type t)
	{
		if (t != null && t.IsGenericType)
		{
			return t.GetGenericTypeDefinition() == typeof(ConfigEntry<>);
		}
		return false;
	}

	private static void OnSingleRate(ConsoleEventArgs args, string propName, string shortName)
	{
		if (!((Object)(object)args?.Context == (Object)null))
		{
			Action<string> reply = delegate(string s)
			{
				RatesFeedback.Say(s, yellowHud: false);
			};
			if (args.Length < 2)
			{
				ProcessLine("sr_" + shortName, reply);
			}
			else
			{
				ProcessLine("sr_" + shortName + " " + JoinArgsFrom(args, 1), reply);
			}
		}
	}

	private static void OnRatesConsole(ConsoleEventArgs args)
	{
		if (!((Object)(object)args?.Context == (Object)null))
		{
			Action<string> reply = delegate(string s)
			{
				RatesFeedback.Say(s, yellowHud: false);
			};
			if (args.Length < 2)
			{
				ProcessLine("rates", reply);
			}
			else
			{
				ProcessLine("rates " + JoinArgsFrom(args, 1), reply);
			}
		}
	}

	private static string ResolveKey(string raw)
	{
		if (ShortToKey.TryGetValue(raw, out var value))
		{
			return value;
		}
		if (raw.StartsWith("sr_", StringComparison.OrdinalIgnoreCase) && ShortToKey.TryGetValue(raw.Substring(3), out var value2))
		{
			return value2;
		}
		return FindProp(raw)?.Name;
	}

	private static string JoinArgsFrom(ConsoleEventArgs args, int startIndex)
	{
		if (args.Args == null || startIndex >= args.Args.Length)
		{
			if (args.Length <= startIndex)
			{
				return "";
			}
			return args[startIndex];
		}
		if (startIndex == args.Args.Length - 1)
		{
			return args.Args[startIndex];
		}
		return string.Join(" ", args.Args, startIndex, args.Args.Length - startIndex);
	}

	private static void ApplySet(Action<string> reply, string propName, string value)
	{
		if (!TrySet(propName, value, out var error, out var applied))
		{
			reply("rates set failed: " + error);
			return;
		}
		ConfigWatch.SuppressReloadBriefly();
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			((BaseUnityPlugin)Plugin.Instance).Config.Save();
		}
		RateApplier.NotifyConfigReloaded();
		RateApplier.ApplyGlobalKeys();
		CategoryGroundAmp.LogStatus();
		string value2;
		string text = (KeyToShort.TryGetValue(propName, out value2) ? value2 : propName);
		RatesFeedback.Say(FormatHudMessage(propName, applied), yellowHud: true);
		ManualLogSource log = Plugin.Log;
		if (log != null)
		{
			log.LogInfo((object)("ServerRates command live-applied: " + propName + "=" + applied + " (sr_" + text + ")"));
		}
	}

	private static string FormatHudMessage(string propName, string applied)
	{
		string text = FriendlyHudLabel(propName);
		if (propName.EndsWith("Percent", StringComparison.Ordinal))
		{
			return text + " " + applied + "%";
		}
		if (propName.EndsWith("Multiplier", StringComparison.Ordinal) || propName.Equals("ExtraDropScatterForce", StringComparison.OrdinalIgnoreCase) || propName.Equals("WorldLevel", StringComparison.OrdinalIgnoreCase))
		{
			return text + " Multiplier x" + applied;
		}
		if (!propName.EndsWith("Enabled", StringComparison.Ordinal) && !propName.Contains("Amp"))
		{
			propName.Contains("Snap");
		}
		return text + ": " + applied;
	}

	private static string FriendlyHudLabel(string propName)
	{
		if (KeyToShort.TryGetValue(propName, out var value))
		{
			switch (value)
			{
			case "wood":
				return "Wood";
			case "finewood":
				return "Fine Wood";
			case "corewood":
				return "Core Wood";
			case "specialwood":
				return "Special Wood";
			case "ore":
				return "Ore";
			case "scrap":
				return "Scrap";
			case "stone":
				return "Stone";
			case "flint":
				return "Flint";
			case "crystal":
				return "Crystal";
			case "fuel":
				return "Fuel";
			case "gems":
				return "Gems";
			case "boss":
				return "Boss Mats";
			case "crops":
				return "Crops";
			case "seeds":
				return "Seeds";
			case "mushrooms":
				return "Mushrooms";
			case "berries":
				return "Berries";
			case "fish":
				return "Fish";
			case "potions":
				return "Potions";
			case "hide":
				return "Hide";
			case "trophy":
				return "Trophy";
			case "meat":
				return "Meat";
			case "parts":
				return "Parts";
			case "feathers":
				return "Feathers";
			case "specialparts":
				return "Special Parts";
			case "resourcerate":
				return "Resource Rate";
			case "skillgain":
				return "Skill Gain";
			case "skillloss":
				return "Skill Loss";
			case "playerdmg":
				return "Player Damage";
			case "enemydmg":
				return "Enemy Damage";
			case "groundamp":
				return "Ground Amp";
			case "teleportall":
				return "Teleport All";
			case "smelter":
				return "Smelter";
			case "fermenter":
				return "Fermenter";
			case "cooking":
				return "Cooking";
			case "plants":
				return "Plant Grow";
			case "other":
				return "Other Loot";
			case "scatter":
				return "Drop Scatter";
			}
		}
		string text = propName;
		if (text.EndsWith("DropMultiplier"))
		{
			text = text.Substring(0, text.Length - "DropMultiplier".Length);
		}
		else if (text.EndsWith("SpeedMultiplier"))
		{
			text = text.Substring(0, text.Length - "SpeedMultiplier".Length);
		}
		else if (text.EndsWith("Percent"))
		{
			text = text.Substring(0, text.Length - "Percent".Length);
		}
		else if (text.EndsWith("Multiplier"))
		{
			text = text.Substring(0, text.Length - "Multiplier".Length);
		}
		return Regex.Replace(text, "([a-z])([A-Z])", "$1 $2");
	}

	private static void ShowYellowHud(string text)
	{
		RatesFeedback.ShowYellowHud(text);
	}

	private static void PrintHelp(Action<string> reply)
	{
		string text = Plugin.Settings?.ChatCommandPrefix?.Value ?? "!";
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("ServerRates — chat or F5 (no admin needed):");
		stringBuilder.AppendLine("  " + text + "sr_wood 9   " + text + "sr_meat 5   " + text + "sr_teleportall On");
		stringBuilder.AppendLine("  " + text + "sr_resourcerate 200");
		stringBuilder.AppendLine("  " + text + "sr_wood            (no value = show current)");
		stringBuilder.AppendLine("  rates cmds | status | list | help");
		stringBuilder.AppendLine("  rates set wood 9");
		stringBuilder.AppendLine("Percent: 100=vanilla | Mult: 1=vanilla | Toggle: Unchanged|On|Off");
		stringBuilder.Append("Applies LIVE.");
		reply(stringBuilder.ToString());
	}

	private static void PrintAllCommands(Action<string> reply)
	{
		string text = Plugin.Settings?.ChatCommandPrefix?.Value ?? "!";
		List<string> list = new List<string>();
		foreach (KeyValuePair<string, string> item in ShortToKey)
		{
			list.Add(text + "sr_" + item.Key + "  ->  " + item.Value);
		}
		list.Sort(StringComparer.OrdinalIgnoreCase);
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("ServerRates commands (" + list.Count + "):");
		foreach (string item2 in list)
		{
			stringBuilder.AppendLine(item2);
		}
		reply(stringBuilder.ToString().TrimEnd(Array.Empty<char>()));
	}

	private static void PrintStatus(Action<string> reply, bool all)
	{
		if (Plugin.Settings == null)
		{
			reply("ServerRates: settings not ready");
			return;
		}
		ModConfig settings = Plugin.Settings;
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("ServerRates Active=" + Plugin.Active + " Enabled=" + settings.Enabled.Value);
		if (!all)
		{
			stringBuilder.AppendLine("ResourceRatePercent=" + settings.ResourceRatePercent.Value + " (sr_resourcerate)");
			stringBuilder.AppendLine("WoodDropMultiplier=" + settings.WoodDropMultiplier.Value + " (sr_wood)");
			stringBuilder.AppendLine("MeatDropMultiplier=" + settings.MeatDropMultiplier.Value + " (sr_meat)");
			stringBuilder.AppendLine("HideDropMultiplier=" + settings.HideDropMultiplier.Value + " (sr_hide)");
			stringBuilder.AppendLine("OreDropMultiplier=" + settings.OreDropMultiplier.Value + " (sr_ore)");
			stringBuilder.AppendLine("CategoryGroundAmp=" + settings.CategoryGroundAmp.Value + " (sr_groundamp)");
			stringBuilder.Append("rates list / rates cmds for more");
			reply(stringBuilder.ToString());
			return;
		}
		string[] allKeys = GetAllKeys();
		foreach (string key in allKeys)
		{
			if (TryGet(key, out var text, out var _))
			{
				string value;
				string text2 = (KeyToShort.TryGetValue(key, out value) ? ("  [sr_" + value + "]") : "");
				stringBuilder.AppendLine(text + text2);
			}
		}
		reply(stringBuilder.ToString().TrimEnd(Array.Empty<char>()));
	}

	private static bool TryGet(string key, out string text, out string error)
	{
		text = null;
		error = null;
		if (Plugin.Settings == null)
		{
			error = "settings not ready";
			return false;
		}
		PropertyInfo propertyInfo = FindProp(key);
		if (propertyInfo == null)
		{
			error = "unknown key '" + key + "'";
			return false;
		}
		object value = propertyInfo.GetValue(Plugin.Settings);
		PropertyInfo propertyInfo2 = value?.GetType().GetProperty("Value");
		if (propertyInfo2 == null)
		{
			error = "no Value";
			return false;
		}
		text = propertyInfo.Name + "=" + FormatValue(propertyInfo2.GetValue(value, null));
		return true;
	}

	private static string FormatValue(object val)
	{
		if (val == null)
		{
			return "";
		}
		if (val is float num)
		{
			return num.ToString("0.###", CultureInfo.InvariantCulture);
		}
		if (val is bool)
		{
			if (!(bool)val)
			{
				return "false";
			}
			return "true";
		}
		return Convert.ToString(val, CultureInfo.InvariantCulture) ?? "";
	}

	private static PropertyInfo FindProp(string key)
	{
		return typeof(ModConfig).GetProperty(key, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
	}

	private static bool TrySet(string key, string raw, out string error, out string applied)
	{
		error = null;
		applied = null;
		if (Plugin.Settings == null)
		{
			error = "settings not ready";
			return false;
		}
		PropertyInfo propertyInfo = FindProp(key);
		if (propertyInfo == null)
		{
			error = "unknown key '" + key + "'";
			return false;
		}
		object value = propertyInfo.GetValue(Plugin.Settings);
		if (value == null)
		{
			error = "null entry";
			return false;
		}
		Type type = value.GetType();
		if (!IsConfigEntry(type))
		{
			error = "not a ConfigEntry";
			return false;
		}
		Type valueType = type.GetGenericArguments()[0];
		PropertyInfo property = type.GetProperty("Value");
		if (property == null)
		{
			error = "no Value";
			return false;
		}
		try
		{
			object obj = ParseValue(valueType, raw, out error);
			if (obj == null && error != null)
			{
				return false;
			}
			property.SetValue(value, obj, null);
			applied = FormatValue(obj);
			return true;
		}
		catch (Exception ex)
		{
			error = ex.Message;
			return false;
		}
	}

	private static object ParseValue(Type valueType, string raw, out string error)
	{
		error = null;
		string text = (raw ?? "").Trim();
		if (valueType == typeof(bool))
		{
			if (bool.TryParse(text, out var result))
			{
				return result;
			}
			if (text == "1" || text.Equals("on", StringComparison.OrdinalIgnoreCase) || text.Equals("yes", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (text == "0" || text.Equals("off", StringComparison.OrdinalIgnoreCase) || text.Equals("no", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			error = "bool expected (true/false/on/off)";
			return null;
		}
		if (valueType == typeof(int))
		{
			if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return result2;
			}
			error = "int expected";
			return null;
		}
		if (valueType == typeof(float))
		{
			if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return result3;
			}
			error = "number expected";
			return null;
		}
		if (valueType == typeof(string))
		{
			if (text.Equals("on", StringComparison.OrdinalIgnoreCase))
			{
				return "On";
			}
			if (text.Equals("off", StringComparison.OrdinalIgnoreCase))
			{
				return "Off";
			}
			if (text.Equals("unchanged", StringComparison.OrdinalIgnoreCase))
			{
				return "Unchanged";
			}
			return text;
		}
		error = "unsupported type " + valueType.Name;
		return null;
	}
}
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
internal static class RatesTerminalPatch
{
	private static void Postfix()
	{
		RatesCommands.Register();
	}
}
internal static class RatesFeedback
{
	[HarmonyPatch(typeof(ZNet), "RPC_RemoteCommand")]
	private static class RemoteCommandPatch
	{
		private static void Prefix(ZRpc rpc)
		{
			CurrentRemoteRpc = rpc;
		}
	}

	[HarmonyPatch(typeof(ZNet), "InternalCommand")]
	private static class InternalCommandPatch
	{
		private static void Prefix(ZRpc rpc)
		{
			CurrentRemoteRpc = rpc;
		}

		private static void Postfix()
		{
			CurrentRemoteRpc = null;
		}

		private static Exception Finalizer(Exception __exception)
		{
			CurrentRemoteRpc = null;
			return __exception;
		}
	}

	private static bool _guard;

	internal static ZRpc CurrentRemoteRpc { get; private set; }

	public static void Say(string msg, bool yellowHud)
	{
		if (string.IsNullOrEmpty(msg))
		{
			return;
		}
		string[] array = SplitLines(msg);
		foreach (string text in array)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)text);
			}
		}
		if (_guard)
		{
			return;
		}
		_guard = true;
		try
		{
			ZRpc currentRemoteRpc = CurrentRemoteRpc;
			if (currentRemoteRpc != null && currentRemoteRpc.IsConnected() && (Object)(object)ZNet.instance != (Object)null)
			{
				array = SplitLines(msg);
				foreach (string text2 in array)
				{
					try
					{
						ZNet.instance.RemotePrint(currentRemoteRpc, text2);
					}
					catch (Exception ex)
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogWarning((object)("ServerRates RemotePrint failed: " + ex.Message));
						}
						break;
					}
				}
			}
			else if ((Object)(object)Console.instance != (Object)null)
			{
				array = SplitLines(msg);
				foreach (string text3 in array)
				{
					Console.instance.Print(text3);
				}
			}
			if (yellowHud)
			{
				ShowYellowHud(SplitLines(msg)[0]);
			}
			BroadcastChat(TrimForChat(msg));
		}
		finally
		{
			_guard = false;
		}
	}

	private static string[] SplitLines(string msg)
	{
		return msg.Replace("\r\n", "\n").Replace('\r', '\n').Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
	}

	private static string TrimForChat(string msg)
	{
		string text = msg.Replace("\r\n", " | ").Replace('\n', '|').Replace('\r', '|');
		if (text.Length <= 450)
		{
			return text;
		}
		return text.Substring(0, 447) + "...";
	}

	public static void ShowYellowHud(string text)
	{
		if (string.IsNullOrEmpty(text) || ZRoutedRpc.instance == null)
		{
			return;
		}
		try
		{
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.MessageAll((MessageType)2, text);
				return;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ShowMessage", new object[2] { 2, text });
		}
		catch (Exception ex)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("ServerRates HUD failed: " + ex.Message));
			}
		}
	}

	public static void BroadcastChat(string text)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrEmpty(text) || ZRoutedRpc.instance == null)
		{
			return;
		}
		try
		{
			UserInfo val = new UserInfo
			{
				Name = "ServerRates"
			};
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ChatMessage", new object[4]
			{
				Vector3.zero,
				1,
				val,
				text
			});
		}
		catch (Exception ex)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("ServerRates chat broadcast failed: " + ex.Message));
			}
		}
	}
}