Decompiled source of SimpleOptimize v0.1.16

SimpleOptimize.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dissonance.Integrations.Unity_NFGO;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SimpleOptimize.Core;
using SimpleOptimize.Features;
using Steamworks;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("ClientNetworkTransform")]
[assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")]
[assembly: IgnoresAccessChecksTo("DissonanceVoip")]
[assembly: IgnoresAccessChecksTo("DunGen")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")]
[assembly: IgnoresAccessChecksTo("EasyTextEffects")]
[assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Jobs")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")]
[assembly: IgnoresAccessChecksTo("Unity.Services.QoS")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Relay")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")]
[assembly: IgnoresAccessChecksTo("Unity.XR.Management")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SimpleOptimize
{
	internal static class ChatUtil
	{
		internal static void Send(string message, bool broadcast = false)
		{
			try
			{
				HUDManager instance = HUDManager.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					string text = "<color=#7dc47a>Optimize</color>: <color=#e8e86a>" + message + "</color>";
					if (broadcast)
					{
						instance.AddTextMessageServerRpc(text);
					}
					else
					{
						instance.AddChatMessage(text, "", -1, false);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ChatUtil.Send] {arg}");
			}
		}
	}
	internal static class CommandRouter
	{
		internal static bool IsSenderTheLocalPlayer(int playerId)
		{
			try
			{
				if (playerId < 0)
				{
					return true;
				}
				PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return playerId == (int)val.playerClientId || playerId == (int)val.actualClientId;
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[CommandRouter.IsSenderTheLocalPlayer] {arg}");
				return false;
			}
		}

		internal static bool Route(string raw)
		{
			try
			{
				if (string.IsNullOrWhiteSpace(raw))
				{
					return false;
				}
				string[] array = raw.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
				string text = array[0].ToLowerInvariant();
				if (TournamentGate.Active && (text == "opt" || text == "optimize"))
				{
					ChatUtil.Send("Tournament mode: mod commands are off until the run ends.");
					return true;
				}
				switch (text)
				{
				case "optimize":
				case "opt":
				{
					string text3 = ((array.Length >= 2) ? array[1].ToLowerInvariant() : "");
					string text4 = ((array.Length >= 3) ? array[2].ToLowerInvariant() : "");
					switch (text3)
					{
					case "":
					{
						float num2 = Time.smoothDeltaTime * 1000f;
						string text5 = $"Sleeping {ItemSleeper.SleepingCount} item(s) · {RenderOptimizer.DeshadowedCount} de-shadowed · {RenderOptimizer.HiddenCount} culled · " + $"frame {num2:0.0}ms (~{((num2 > 0.01f) ? (1000f / num2) : 0f):0} FPS). " + "sleep=" + (ModConfig.SleepShipItems.Value ? "on" : "off") + " shadows=" + (ModConfig.DisableShipItemShadows.Value ? "on" : "off") + " snap=" + (ModConfig.SnapAboardItems.Value ? "on" : "off") + " cull=" + (ModConfig.CullDistantShipItems.Value ? "on" : "off") + " save-cap=" + ((ShipCapacityFix.CurrentCap > 0) ? ShipCapacityFix.CurrentCap.ToString() : "?") + " voices=" + VoiceLimit.Status;
						ChatUtil.Send(text5);
						Plugin.Logger.LogInfo((object)($"[Optimizer] {text5} · truckLogStorms muted={TruckCollectDespam.SuppressedCalls}" + $" · zeroAreaEmitters stopped={ZeroAreaParticleFix.DisabledCount}"));
						return true;
					}
					case "sleep":
						if (text4 == "on" || text4 == "off")
						{
							ModConfig.SleepShipItems.Value = text4 == "on";
							ChatUtil.Send("Item sleeping " + ((text4 == "on") ? "ON" : "OFF") + ".");
						}
						else
						{
							ChatUtil.Send("Usage: /opt sleep on|off");
						}
						return true;
					case "snap":
						if (text4 == "on" || text4 == "off")
						{
							ModConfig.SnapAboardItems.Value = text4 == "on";
							ChatUtil.Send("Instant settle aboard " + ((text4 == "on") ? "ON" : "OFF") + ".");
						}
						else
						{
							ChatUtil.Send("Usage: /opt snap on|off");
						}
						return true;
					case "paint":
					case "spray":
						switch (text4)
						{
						case "":
							ChatUtil.Send(SprayColor.StatusFragment());
							ChatUtil.Send("Usage: /opt spray <index> · /opt spray #RRGGBB · /opt spray off");
							return true;
						case "off":
						case "vanilla":
							ModConfig.SprayColorMode.Value = "off";
							ChatUtil.Send($"Spray colour back to vanilla (seeded) — {SuiteHandshake.PushSpray()} can(s) refreshed. New cans only; already-sprayed decals keep their colour.");
							return true;
						default:
						{
							if (int.TryParse(text4, out var result))
							{
								int paletteSize = SprayColor.PaletteSize;
								if (paletteSize > 0 && (result < 0 || result >= paletteSize))
								{
									ChatUtil.Send($"Palette index must be 0-{paletteSize - 1} ({paletteSize} colours on this prefab).");
									return true;
								}
								ModConfig.SprayColorIndex.Value = result;
								ModConfig.SprayColorMode.Value = "palette";
								ChatUtil.Send($"Spray colour: palette #{result} — {SuiteHandshake.PushSpray()} can(s) refreshed.");
								return true;
							}
							if (SprayColor.TryParseColor(text4, out var _))
							{
								ModConfig.SprayColorHex.Value = (text4.StartsWith("#") ? text4 : ("#" + text4));
								ModConfig.SprayColorMode.Value = "custom";
								int num = SuiteHandshake.PushSpray();
								ChatUtil.Send($"Spray colour: {ModConfig.SprayColorHex.Value} — {num} can(s) refreshed (tint via {SprayColor.LastTintProperty}).");
								if (SprayColor.LastTintProperty == "none")
								{
									ChatUtil.Send("⚠ That material exposes no known colour property — try a palette index instead.");
								}
								return true;
							}
							ChatUtil.Send("Usage: /opt spray <index> · /opt spray #RRGGBB · /opt spray off");
							return true;
						}
						}
					case "cargo":
					case "dropship":
						switch (text4)
						{
						case "on":
						case "off":
							ModConfig.FastDropship.Value = text4 == "on";
							ChatUtil.Send("Fast cargo dropship " + ((text4 == "on") ? "ON" : "OFF") + " — " + FastDropship.StatusFragment());
							return true;
						case "wait":
						case "hold":
						{
							string text6 = ((array.Length >= 4) ? array[3].ToLowerInvariant() : "");
							if (text6 == "on" || text6 == "off")
							{
								ModConfig.DropshipWaitForOpen.Value = text6 == "on";
								ChatUtil.Send((text6 == "on") ? "Dropship will WAIT until opened before leaving." : "Dropship leaves on the timer even if unopened — vanilla takes the items AND the credits if you miss it.");
							}
							else
							{
								ChatUtil.Send("Wait-until-opened is " + (ModConfig.DropshipWaitForOpen.Value ? "ON" : "off") + ". Usage: /opt dropship wait on|off");
							}
							return true;
						}
						case "speed":
						case "s":
						{
							string s = ((array.Length >= 4) ? array[3] : "");
							if (int.TryParse(s, out var result2) && result2 >= 1 && result2 <= 4)
							{
								ModConfig.FastDropshipSpeed.Value = result2;
								ChatUtil.Send($"Dropship speed {result2} — {FastDropship.Describe(result2)}" + (ModConfig.FastDropship.Value ? "" : " (feature is OFF — /opt dropship on)"));
							}
							else
							{
								ChatUtil.Send($"Usage: /opt dropship speed {1}-{4}");
								ChatUtil.Send(FastDropship.SpeedTable());
							}
							return true;
						}
						default:
							ChatUtil.Send(FastDropship.StatusFragment() ?? "");
							ChatUtil.Send("Speeds: " + FastDropship.SpeedTable() + " · /opt dropship speed <#> · /opt dropship on|off");
							return true;
						}
					case "transit":
						if (text4 == "on" || text4 == "off")
						{
							ModConfig.FastCompanyTransit.Value = text4 == "on";
							FastCompanyTransit.SyncAnimatorSpeed();
							ChatUtil.Send("Fast transit " + ((text4 == "on") ? "ON" : "OFF") + " — " + SuiteHandshake.StatusFragment());
						}
						else
						{
							ChatUtil.Send("Fast transit is " + (ModConfig.FastCompanyTransit.Value ? "ON" : "off") + " " + string.Format("(x{0:0.#}, {1}) · ", ModConfig.FastTransitSpeed.Value, ModConfig.FastTransitCompanyOnly.Value ? "Company only" : "all moons") + SuiteHandshake.StatusFragment() + " · latched: " + FastCompanyTransit.LatchState);
						}
						return true;
					case "cull":
						if (text4 == "on" || text4 == "off")
						{
							ModConfig.CullDistantShipItems.Value = text4 == "on";
							ChatUtil.Send(string.Format("Distance culling {0} (beyond {1:0}m).", (text4 == "on") ? "ON" : "OFF", ModConfig.CullDistance.Value));
						}
						else
						{
							ChatUtil.Send("Usage: /opt cull on|off");
						}
						return true;
					case "yeet":
						if (text4 == "on" || text4 == "off")
						{
							ModConfig.FixCruiserYeet.Value = text4 == "on";
							ChatUtil.Send("Cruiser yeet fix " + ((text4 == "on") ? "ON" : "OFF") + ".");
						}
						else
						{
							ChatUtil.Send("Cruiser yeet fix is " + (ModConfig.FixCruiserYeet.Value ? "ON" : "off") + ". Usage: /opt yeet on|off");
						}
						return true;
					case "shadows":
						if (text4 == "on" || text4 == "off")
						{
							ModConfig.DisableShipItemShadows.Value = text4 == "on";
							ChatUtil.Send("Shadow killing " + ((text4 == "on") ? "ON" : "OFF") + ".");
						}
						else
						{
							ChatUtil.Send("Usage: /opt shadows on|off");
						}
						return true;
					default:
						ChatUtil.Send("Usage: /opt [sleep on|off | shadows on|off]");
						return true;
					}
				}
				case "help":
				{
					string text2 = ((array.Length >= 2) ? array[1].ToLowerInvariant() : "");
					if (text2 == "")
					{
						ChatUtil.Send("SimpleOptimize — /help optimize");
						return true;
					}
					if (text2 != "optimize" && text2 != "opt" && text2 != "simpleoptimize")
					{
						return false;
					}
					ChatUtil.Send("Ship-item optimizer (your machine only): sleeping settled items + killing their shadows.");
					ChatUtil.Send("/opt = status (counts + FPS) · /opt sleep on|off · /opt shadows on|off — items wake instantly when grabbed");
					ChatUtil.Send("Also fixes the vanilla 250-item save cap (items past it despawn on reload) — host-side, automatic.");
					return true;
				}
				default:
					return false;
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[CommandRouter.Route] {arg}");
				ChatUtil.Send("Command failed — see BepInEx log.");
				return true;
			}
		}
	}
	internal static class ModConfig
	{
		internal static ConfigEntry<string> CommandPrefix { get; private set; }

		internal static ConfigEntry<string> AltCommandPrefix { get; private set; }

		internal static ConfigEntry<bool> SleepShipItems { get; private set; }

		internal static ConfigEntry<bool> DisableShipItemShadows { get; private set; }

		internal static ConfigEntry<int> ShipItemCapacity { get; private set; }

		internal static ConfigEntry<bool> FixSpiderWebSlow { get; private set; }

		internal static ConfigEntry<bool> FixStuckInteract { get; private set; }

		internal static ConfigEntry<bool> FixCruiserYeet { get; private set; }

		internal static ConfigEntry<bool> FixCadaverKillLag { get; private set; }

		internal static ConfigEntry<bool> MuteThrowRpcSpam { get; private set; }

		internal static ConfigEntry<bool> SnapAboardItems { get; private set; }

		internal static ConfigEntry<bool> CullDistantShipItems { get; private set; }

		internal static ConfigEntry<float> CullDistance { get; private set; }

		internal static ConfigEntry<bool> MuteTruckCollectSpam { get; private set; }

		internal static ConfigEntry<bool> FixWeedKillerIndexSpam { get; private set; }

		internal static ConfigEntry<bool> FixExactScan { get; private set; }

		internal static ConfigEntry<bool> FixUnknownNames { get; private set; }

		internal static ConfigEntry<bool> HostNameSync { get; private set; }

		internal static ConfigEntry<bool> ResendSteamIds { get; private set; }

		internal static ConfigEntry<bool> FixRadarNames { get; private set; }

		internal static ConfigEntry<bool> SyncRadarOrder { get; private set; }

		internal static ConfigEntry<bool> FixZeroAreaParticleSpam { get; private set; }

		internal static ConfigEntry<string> SprayColorMode { get; private set; }

		internal static ConfigEntry<int> SprayColorIndex { get; private set; }

		internal static ConfigEntry<string> SprayColorHex { get; private set; }

		internal static ConfigEntry<bool> SprayAcceptHostColor { get; private set; }

		internal static ConfigEntry<bool> FastDropship { get; private set; }

		internal static ConfigEntry<float> FastDropshipSpeed { get; private set; }

		internal static ConfigEntry<bool> DropshipWaitForOpen { get; private set; }

		internal static ConfigEntry<bool> FastCompanyTransit { get; private set; }

		internal static ConfigEntry<bool> FastTransitCompanyOnly { get; private set; }

		internal static ConfigEntry<float> FastTransitSpeed { get; private set; }

		internal static ConfigEntry<bool> FixJoinScrapValues { get; private set; }

		internal static ConfigEntry<int> JoinSyncMaxItems { get; private set; }

		internal static ConfigEntry<bool> FixCruiserSeatDisconnect { get; private set; }

		internal static ConfigEntry<bool> FixLateJoinVoice { get; private set; }

		internal static ConfigEntry<bool> FixSpectatorAvatars { get; private set; }

		internal static ConfigEntry<bool> RaiseVoiceLimit { get; private set; }

		internal static ConfigEntry<int> RealVoices { get; private set; }

		internal static ConfigEntry<int> VirtualVoices { get; private set; }

		internal static void Init(ConfigFile file)
		{
			CommandPrefix = file.Bind<string>("General", "CommandPrefix", "/", "Prefix for chat commands (e.g. /opt).");
			AltCommandPrefix = file.Bind<string>("General", "AltCommandPrefix", "!", "Second prefix that always works, e.g. !opt. Other chat-command mods (ChatCommandAPI and similar) often claim every '/' message and reply 'unknown command' before SimpleOptimize ever sees it. They ignore '!', so this gets through. Empty to disable.");
			SleepShipItems = file.Bind<bool>("Optimizer", "SleepShipItems", true, "Skip per-frame Update() for items resting on the ship (not held, settled). They wake instantly when grabbed or moved. Toggle in-game: /opt sleep on|off.");
			DisableShipItemShadows = file.Bind<bool>("Optimizer", "DisableShipItemShadows", true, "Turn off shadow casting for items resting on the ship (restored when picked up or off the ship). Toggle in-game: /opt shadows on|off.");
			ShipItemCapacity = file.Bind<int>("Optimizer", "ShipItemCapacity", 9999, "Vanilla only SAVES the first 250 ship items (StartOfRound.maxShipItemCapacity) — the rest silently despawn on the next load. This raises the cap (0 = leave vanilla). Matters on the host, who writes the save.");
			FixSpiderWebSlow = file.Bind<bool>("Fixes", "FixSpiderWebSlow", true, "Fix the vanilla bug where a spider web broken/removed while you're in it leaves you permanently slowed (RemoveWeb uses the wrong cleanup factor and double-cleans via OnDestroy). Also self-heals already-corrupted hindered state.");
			FixStuckInteract = file.Bind<bool>("Fixes", "FixStuckInteract", true, "Fix the vanilla 'interact glitch' (can't interact or scroll-swap until relog): the throwingObject flag is only reset by a server RPC round trip; if that's lost it sticks forever. Watchdog clears it after 2s.");
			FixCruiserYeet = file.Bind<bool>("Fixes", "FixCruiserYeet", true, "Stop the cruiser being launched by physics depenetration when a player capsule ends up inside it (door-exit teleports, TZP-drunk movement). Overlapping players briefly ghost through the truck instead — vanilla's own seated-player collision toggle.");
			SnapAboardItems = file.Bind<bool>("Optimizer", "SnapAboardItems", true, "Skip the fall animation for items dropped/moved aboard the ship or cruiser — they settle instantly and start sleeping the same frame (falling items can't sleep). Items dropped outside still fall normally. Toggle: /opt snap on|off.");
			CullDistantShipItems = file.Bind<bool>("Optimizer", "CullDistantShipItems", true, "Stop rendering aboard items entirely while you're far from them (draw calls are the big hoard cost while farming away from the ship); restored as you approach. Toggle: /opt cull on|off.");
			CullDistance = file.Bind<float>("Optimizer", "CullDistance", 45f, "Distance (meters) beyond which aboard items stop rendering.");
			MuteThrowRpcSpam = file.Bind<bool>("Fixes", "MuteThrowRpcSpam", true, "Mute the vanilla 'ThrowObjectClientRpc called for an object...' error spam from desynced client drops (thousands of log lines per session = real frame cost; vanilla already ignores the bad request).");
			FixCadaverKillLag = file.Bind<bool>("Fixes", "FixCadaverKillLag", true, "Fix the FPS collapse when killing Cadaver growth plants: vanilla throws an exception per kill (logged twice with stack traces) plus ~5 debug log lines per kill. Swallows the exceptions and mutes exactly those log lines.");
			FixWeedKillerIndexSpam = file.Bind<bool>("Fixes", "FixWeedKillerIndexSpam", true, "Stop the weed killer throwing an exception EVERY FRAME after the plant or mold it was killing gets destroyed (stale cached index in SprayPaintItem.LateUpdate). Measured at 25,691 exceptions with stack traces in one session. Clears the stale target so it fails once instead of thousands of times.");
			FixExactScan = file.Bind<bool>("Fixes", "FixExactScan", true, "Make the terminal 'scan' command exact: vanilla adds a random guess per item instead of its real value after landing, and before the doors open it counts the whole moon's scrap as 'in the ship'. Counts real values of scrap not in the ship (on the moon), or in the ship while in orbit. Your terminal only, no network messages.");
			FixUnknownNames = file.Bind<bool>("Fixes", "FixUnknownNames", true, "Show real names instead of 'unknown' for players who joined late. The game looks names up through Steam, which only knows friends and people in the same Steam lobby, and everyone leaves that lobby when the ship first lands. This asks Steam for the missing names directly, and takes the names a SimpleOptimize host sends ([Names] HostNameSync). Only fixes what YOUR screen shows.");
			HostNameSync = file.Bind<bool>("Names", "HostNameSync", true, "Host: fix late joiners seeing 'unknown' for everyone but the host. Names never travel over the network: each game asks its own Steam, and Steam only names friends and people in the same Steam lobby, which after the first landing holds only the host. A few seconds after each join (2, 8 and 20 s) the host sends its own names to players running SimpleOptimize, who apply them at once. Players without SimpleOptimize keep the names their own Steam knows (see ResendSteamIds).");
			ResendSteamIds = file.Bind<bool>("Names", "ResendSteamIds", true, "Host, part of HostNameSync: on each of those passes also re-send the game's own 'player Steam IDs' message (the one vanilla sends on every join), so every player, modded or not, looks the names up again once their Steam has caught up. Only helps vanilla players with names their Steam can know (friends, people in the same Steam lobby).");
			FixRadarNames = file.Bind<bool>("Radar", "FixRadarNames", true, "Fix the ship monitor showing one player's name while it is really following someone else (so the teleporter beams the wrong person). Vanilla bug, happens in any lobby: turning on a radar booster re-sorts the monitor's list, and the next time anyone joins the game writes names by player slot number into the re-sorted list. Names are put back on the right players; two players with the same name show as Name and Name2. Fixes your own screen; other players need SimpleOptimize 0.1.14 too.");
			SyncRadarOrder = file.Bind<bool>("Radar", "SyncRadarOrder", true, "Host: send the monitor's player/booster order to players who also run SimpleOptimize, so pressing 'switch' shows the same person on everyone's monitor. Players without it are unaffected. As a player: follow the host's order when it is sent.");
			FixZeroAreaParticleSpam = file.Bind<bool>("Fixes", "FixZeroAreaParticleSpam", true, "Stop the 'Particle System is trying to spawn on a mesh with zero surface area' flood. A dead body's blood effect can be pointed at a mesh with no surface, and the game then warns every single frame: 57,398 lines in one measured session, which costs real frames. Switches off just that emitter, which could never have drawn anything.");
			MuteTruckCollectSpam = file.Bind<bool>("Fixes", "MuteTruckCollectSpam", true, "Mute the Debug.Log flood inside VehicleController.CollectItemsInTruck. Vanilla logs THREE lines per collider inside its 25m sweep, so a few hundred items aboard is ~700 formatted log writes per magnet event — measured at 772ms with 237 items, and it is the whole cost of StartMagneting. Only that method's own logging is dropped.");
			SprayColorMode = file.Bind<string>("Spray", "ColorMode", "off", "off | palette | custom. Vanilla rolls a spray can's colour from the map seed, so overriding it is LOCAL to your screen — vanilla friends still see the seeded colour for the same can and the same decals (nothing desyncs). Set with /opt spray.");
			SprayColorIndex = file.Bind<int>("Spray", "ColorIndex", 0, "Which of the game's own spray materials to use in palette mode (and which slot custom mode tints). Range depends on the prefab — /opt spray reports how many exist.");
			SprayColorHex = file.Bind<string>("Spray", "ColorHex", "#FF00FF", "Colour for custom mode, e.g. #FF00FF. The decals are HDRP DecalProjector materials, so the shader's colour property varies; every known name is tried and /opt spray reports which one took.");
			SprayAcceptHostColor = file.Bind<bool>("Spray", "AcceptHostColor", true, "As a client, adopt the host's spray colour so the lobby matches. Set false to keep your own. Has no effect when you ARE the host. Players without SimpleOptimize always see the vanilla seeded colour — the colour never travels in vanilla netcode.");
			FastDropship = file.Bind<bool>("Dropship", "FastDropship", true, "Speed up the CARGO dropship that delivers items bought at the terminal. Vanilla makes you wait 40s for it to arrive and 30s before it leaves. Host-authoritative, so only the host needs this — vanilla clients see the early arrival with no desync. Toggle: /opt dropship on|off.");
			FastDropshipSpeed = file.Bind<float>("Dropship", "Speed", 4f, "Speed level 1-4 for the dropship's countdown and landing animation. Set in-game with /opt dropship speed <#>. 1 = vanilla (40s arrive, 30s parked), 2 = 20s/15s, 3 = 13s/10s, 4 = 10s/8s.");
			DropshipWaitForOpen = file.Bind<bool>("Dropship", "WaitForOpen", true, "Hold the landed dropship until someone opens it, instead of leaving on vanilla's 30s timer. Vanilla takes your items AND your credits if it leaves unopened (\"Items missed! ... delivery fee cannot be refunded\"), and speeding the countdown up made that window too short to reach. Once opened, Speed applies and it leaves quickly. Toggle: /opt dropship wait on|off.");
			FastCompanyTransit = file.Bind<bool>("Transit", "FastCompanyTransit", false, "Speed up the PLAYER SHIP's landing and takeoff. OFF by default (Round 12) — Josh wanted the cargo dropship, not this; see [Dropship] FastDropship. ONLY engages when every player in the lobby is running SimpleOptimize (or you're solo) — the sequences are local on each machine, so a lone fast client would get control before everyone else. Toggle: /opt transit on|off.");
			FastTransitCompanyOnly = file.Bind<bool>("Transit", "CompanyOnly", true, "Restrict the speed-up to Gordion (The Company). Other moons keep vanilla pacing so normal runs feel untouched.");
			FastTransitSpeed = file.Bind<float>("Transit", "Speed", 3f, "Divisor for transit waits and matching animator speed. 3 turns the ~21s landing into ~7s. Higher values risk audio/animation popping, since some sounds run on their own timers.");
			FixJoinScrapValues = file.Bind<bool>("Fixes", "FixJoinScrapValues", true, "When someone joins, the host sends them the value of every item on the map, but vanilla stops after the first ~500 items (nearest the map centre first, scrap and gear alike), so on a big hoard the rest show $0 for the joiner. This lifts that cap. Host-side only; vanilla joiners get the right values too.");
			JoinSyncMaxItems = file.Bind<int>("Fixes", "JoinSyncMaxItems", 4000, "Upper limit for FixJoinScrapValues (500-20000). The values go in ONE vanilla message (it can't be split: vanilla joiners read it in one go), about 8 bytes per item, so 4000 items is ~32 KB. Raise it only if you really have more items than this.");
			FixCruiserSeatDisconnect = file.Bind<bool>("Fixes", "FixCruiserSeatDisconnect", true, "When a player disconnects or is kicked while sitting in the cruiser (driver or passenger), vanilla keeps their seat taken forever: nobody can drive or sit there until the lobby is restarted. This frees the seat on your own machine when the disconnect happens. No network messages; the host also takes the truck back if the leaver was driving. Works best when everyone has it (each machine frees its own copy of the seat).");
			FixLateJoinVoice = file.Bind<bool>("Fixes", "FixLateJoinVoice", true, "Vanilla bug: when someone joins into a slot a kicked or disconnected player had, the game can keep that slot tied to the old player's voice, so once you are dead you can't hear the new player (alive you hear them normally). This re-checks every player's voice link a few seconds after a join, after a disconnect, when you die or switch spectate (and every few seconds while dead), and re-links any wrong one with the game's own voice refresh. Your machine only, no network messages; does nothing when every link is right. Logged as [Voice].");
			FixSpectatorAvatars = file.Bind<bool>("Fixes", "FixSpectatorAvatars", true, "Vanilla bug: the dead players' boxes (left side while spectating) are made once per player slot, so a player who joined into someone else's old slot shows the previous player's Steam picture. This redraws the picture and name when the slot's player changed. Your screen only. Logged as [Avatars].");
			RaiseVoiceLimit = file.Bind<bool>("Audio", "RaiseVoiceLimit", true, "Raise how many sounds Unity plays at once on your machine (the game's limit is low, so in busy moments footsteps, doors and enemies drop out). Applied once at startup, before the menu. Your machine only.");
			RealVoices = file.Bind<int>("Audio", "RealVoices", 128, "Sounds actually played at the same time (32-255). Only ever raised, never lowered.");
			VirtualVoices = file.Bind<int>("Audio", "VirtualVoices", 512, "Sounds tracked at once (the quietest beyond RealVoices are silenced but kept in time), at least RealVoices, up to 4095. Only ever raised.");
		}
	}
	[BepInPlugin("local.simpleoptimize", "SimpleOptimize", "0.1.16")]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("local.simpleoptimize");

		public static Plugin Instance { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			VoiceLimit.Apply();
			_harmony.PatchAll();
			Logger.LogInfo((object)"SimpleOptimize v0.1.16 loaded. Ship-item optimizer armed.");
		}
	}
	public static class PluginInfo
	{
		public const string Guid = "local.simpleoptimize";

		public const string Name = "SimpleOptimize";

		public const string Version = "0.1.16";
	}
}
namespace SimpleOptimize.Patches
{
	[HarmonyPatch(typeof(HUDManager), "AddTextToChatOnServer")]
	internal static class ChatCommandPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix(string chatMessage, int playerId)
		{
			try
			{
				if (string.IsNullOrEmpty(chatMessage))
				{
					return true;
				}
				string text = MatchPrefix(chatMessage);
				if (text == null)
				{
					return true;
				}
				if (!CommandRouter.IsSenderTheLocalPlayer(playerId))
				{
					return true;
				}
				string raw = chatMessage.Substring(text.Length).Trim();
				bool flag = CommandRouter.Route(raw);
				return !flag;
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ChatCommandPatch] {arg}");
				return true;
			}
		}

		private static string MatchPrefix(string message)
		{
			string value = ModConfig.CommandPrefix.Value;
			if (!string.IsNullOrEmpty(value) && message.StartsWith(value))
			{
				return value;
			}
			string value2 = ModConfig.AltCommandPrefix.Value;
			if (!string.IsNullOrEmpty(value2) && message.StartsWith(value2))
			{
				return value2;
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(ItemDropship), "Update")]
	internal static class FastDropshipUpdatePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ItemDropship __instance, out float __state)
		{
			__state = (((Object)(object)__instance != (Object)null) ? __instance.shipTimer : 0f);
		}

		[HarmonyPostfix]
		private static void Postfix(ItemDropship __instance, float __state)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || !FastDropship.Enabled)
				{
					return;
				}
				FastDropship.SyncAnimator(__instance);
				NetworkManager singleton = NetworkManager.Singleton;
				if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && !(__instance.shipTimer <= __state))
				{
					if (FastDropship.ShouldHold(__instance))
					{
						__instance.shipTimer = __state;
					}
					else
					{
						__instance.shipTimer += FastDropship.ExtraTime();
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[FastDropshipUpdatePatch] {arg}");
			}
		}
	}
	[HarmonyPatch]
	internal static class FastTransitTranspilerPatch
	{
		[HarmonyTargetMethods]
		private static IEnumerable<MethodBase> Targets()
		{
			return FastCompanyTransit.TargetMethods();
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return FastCompanyTransit.Transpiler(instructions);
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "Start")]
	internal static class FastTransitStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			try
			{
				SuiteHandshake.Reset();
				SuiteHandshake.Register();
				FastCompanyTransit.SyncAnimatorSpeed();
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[FastTransitStartPatch] {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(RoundManager), "FinishGeneratingNewLevelClientRpc")]
	internal static class FastTransitLandingLatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			FastCompanyTransit.Latch("landing");
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "EndOfGameClientRpc")]
	internal static class FastTransitDepartureLatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			FastCompanyTransit.Latch("departure");
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "SetShipReadyToLand")]
	internal static class FastTransitLandPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			FastCompanyTransit.SyncAnimatorSpeed();
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "ShipLeave")]
	internal static class FastTransitLeavePatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			FastCompanyTransit.SyncAnimatorSpeed();
		}
	}
	[HarmonyPatch(typeof(SprayPaintItem), "Start")]
	internal static class SprayColorStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix(SprayPaintItem __instance)
		{
			try
			{
				SprayColor.RememberVanilla(__instance);
				SprayColor.ApplyForHolder(__instance);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[SprayColorStartPatch] {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(SprayPaintItem), "AddSprayPaintLocal")]
	internal static class SprayColorPerPlayerPatch
	{
		[HarmonyPrefix]
		private static void Prefix(SprayPaintItem __instance)
		{
			try
			{
				SprayColor.ApplyForHolder(__instance);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[SprayColorPerPlayerPatch] {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "Start")]
	internal static class StartupPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			try
			{
				RenderOptimizer.EnsureRunning();
				TournamentGate.Register();
				RadarNames.StartSession();
				JoinNameSync.StartSession();
				VoiceRelinkFix.StartSession();
				SpectateAvatarFix.StartSession();
				((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(Line());
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[StartupPatch] {arg}");
			}
		}

		private static IEnumerator Line()
		{
			yield return (object)new WaitForSeconds(3f);
			SuiteAnnounce.MaybeAnnounce("local.simpleoptimize");
		}
	}
}
namespace SimpleOptimize.Features
{
	internal static class BeltBagWatch
	{
		[HarmonyPatch(typeof(BeltBagItem), "PutObjectInBagLocalClient")]
		internal static class PutPatch
		{
			[HarmonyPrefix]
			private static void Prefix(GrabbableObject gObject)
			{
				try
				{
					if (!((Object)(object)gObject == (Object)null))
					{
						_inBags.Add(gObject);
						RenderOptimizer.Unhide(gObject);
					}
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)string.Format("{0} put prefix: {1}", "[BeltBag]", arg));
				}
			}

			[HarmonyPostfix]
			private static void Postfix(BeltBagItem __instance, GrabbableObject gObject)
			{
				try
				{
					if (!((Object)(object)gObject == (Object)null))
					{
						Plugin.Logger.LogInfo((object)("[BeltBag] " + (IsHost ? "host" : "client") + ": " + Name(gObject) + " going into " + Who(__instance) + " " + $"({__instance.objectsInBag?.Count ?? 0} inside); {Where(gObject)}"));
						((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(CheckParked(__instance, gObject));
					}
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)string.Format("{0} put postfix: {1}", "[BeltBag]", arg));
				}
			}
		}

		[HarmonyPatch(typeof(BeltBagItem), "RemoveFromBagLocalClient")]
		internal static class RemovePatch
		{
			[HarmonyPostfix]
			private static void Postfix(BeltBagItem __instance, NetworkObjectReference objectRef)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				Removed(__instance, objectRef, "floor");
			}
		}

		[HarmonyPatch(typeof(BeltBagItem), "RemoveFromBagLocalClientNonElevatorParent")]
		internal static class RemoveParentPatch
		{
			[HarmonyPostfix]
			private static void Postfix(BeltBagItem __instance, NetworkObjectReference objectRef)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				Removed(__instance, objectRef, "moving floor");
			}
		}

		internal const string Tag = "[BeltBag]";

		private static readonly HashSet<GrabbableObject> _inBags = new HashSet<GrabbableObject>();

		internal static int Count
		{
			get
			{
				_inBags.RemoveWhere((GrabbableObject o) => (Object)(object)o == (Object)null);
				return _inBags.Count;
			}
		}

		private static bool IsHost
		{
			get
			{
				if ((Object)(object)NetworkManager.Singleton != (Object)null)
				{
					return NetworkManager.Singleton.IsServer;
				}
				return false;
			}
		}

		internal static bool InBag(GrabbableObject o)
		{
			if ((Object)(object)o == (Object)null)
			{
				return false;
			}
			if (_inBags.Contains(o))
			{
				return true;
			}
			if ((Object)(object)((Component)o).transform.parent == (Object)null && !o.isHeld && !o.isHeldByEnemy)
			{
				if (!o.isInShipRoom)
				{
					return o.isInElevator;
				}
				return true;
			}
			return false;
		}

		internal static void Clear()
		{
			_inBags.Clear();
		}

		private static string Who(BeltBagItem bag)
		{
			if (!((Object)(object)bag == (Object)null))
			{
				if (!((Object)(object)((GrabbableObject)bag).playerHeldBy != (Object)null))
				{
					return "a bag on the floor";
				}
				return ((GrabbableObject)bag).playerHeldBy.playerUsername + "'s bag";
			}
			return "?";
		}

		internal static string Name(GrabbableObject o)
		{
			if (!((Object)(object)o == (Object)null))
			{
				if (!((Object)(object)o.itemProperties != (Object)null))
				{
					return ((Object)o).name;
				}
				return o.itemProperties.itemName;
			}
			return "?";
		}

		private static string Where(GrabbableObject o)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)o == (Object)null))
			{
				return string.Format("pos {0:F1} parent {1} ", ((Component)o).transform.position, ((Object)(object)((Component)o).transform.parent != (Object)null) ? ((Object)((Component)o).transform.parent).name : "none") + $"ship {o.isInShipRoom}/{o.isInElevator} held {o.isHeld} serverHeld {o.heldByPlayerOnServer} " + "renderers " + RenderState(o);
			}
			return "?";
		}

		private static string RenderState(GrabbableObject o)
		{
			RenderCounts(o, out var on, out var all);
			return string.Format("{0}/{1} on{2}", on, all, RenderOptimizer.HidesAny(o) ? ", culled by SimpleOptimize" : "");
		}

		private static void RenderCounts(GrabbableObject o, out int on, out int all)
		{
			on = 0;
			all = 0;
			Renderer[] componentsInChildren = ((Component)o).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !(val is ParticleSystemRenderer))
				{
					all++;
					if (val.enabled)
					{
						on++;
					}
				}
			}
		}

		private static IEnumerator CheckParked(BeltBagItem bag, GrabbableObject o)
		{
			yield return (object)new WaitForSeconds(0.5f);
			if (!((Object)(object)o == (Object)null) && !((Object)(object)bag == (Object)null) && bag.objectsInBag != null && bag.objectsInBag.Contains(o))
			{
				if (((Component)o).transform.position.y < -300f)
				{
					Plugin.Logger.LogInfo((object)("[BeltBag] " + Name(o) + " is stored (parked out of sight as vanilla does)."));
					yield break;
				}
				Plugin.Logger.LogWarning((object)("[BeltBag] GHOST? " + Name(o) + " is in " + Who(bag) + " but still drawn in the world: " + Where(o) + " " + $"fallTime {o.fallTime:F2} reached {o.reachedFloorTarget} target {o.targetFloorPosition:F1}"));
			}
		}

		private static GrabbableObject Resolve(NetworkObjectReference r)
		{
			NetworkObject val = default(NetworkObject);
			if (!((NetworkObjectReference)(ref r)).TryGet(ref val, (NetworkManager)null) || !((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).GetComponentInChildren<GrabbableObject>();
		}

		private static void Removed(BeltBagItem bag, NetworkObjectReference objectRef, string onto)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GrabbableObject val = Resolve(objectRef);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Logger.LogWarning((object)("[BeltBag] " + (IsHost ? "host" : "client") + ": remove from " + Who(bag) + " for an item this machine can't find."));
					return;
				}
				bool flag = (Object)(object)bag != (Object)null && bag.objectsInBag != null && bag.objectsInBag.Contains(val);
				if (!flag)
				{
					_inBags.Remove(val);
				}
				RenderOptimizer.Unhide(val);
				Plugin.Logger.LogInfo((object)("[BeltBag] " + (IsHost ? "host" : "client") + ": " + Name(val) + " taken out of " + Who(bag) + " onto the " + onto + (flag ? " (bag still lists it!)" : "") + "; " + Where(val)));
				((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(CheckVisible(val));
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)string.Format("{0} remove: {1}", "[BeltBag]", arg));
			}
		}

		private static IEnumerator CheckVisible(GrabbableObject o)
		{
			yield return (object)new WaitForSeconds(1f);
			if (!((Object)(object)o == (Object)null) && !o.isHeld && !o.isPocketed && !_inBags.Contains(o))
			{
				RenderCounts(o, out var on, out var all);
				bool flag = ((Component)o).transform.position.y < -300f;
				bool flag2 = RenderOptimizer.HidesAny(o);
				if (flag || flag2 || (all > 0 && on == 0))
				{
					Plugin.Logger.LogWarning((object)("[BeltBag] INVISIBLE? " + Name(o) + " left the bag but " + (flag ? "is still parked out of sight" : (flag2 ? "SimpleOptimize still culls it" : "every renderer is off")) + ": " + Where(o)));
				}
				else
				{
					Plugin.Logger.LogInfo((object)("[BeltBag] " + Name(o) + " is back in the world and visible."));
				}
			}
		}
	}
	internal static class CadaverKillFix
	{
		[HarmonyPatch(typeof(CadaverGrowthAI), "RemoveWeedFromTile")]
		private static class RemoveWeedPatch
		{
			[HarmonyFinalizer]
			private static Exception Finalizer(Exception __exception)
			{
				return Swallow(__exception);
			}
		}

		[HarmonyPatch(typeof(CadaverGrowthAI), "DestroyPlantAtPosition")]
		private static class DestroyPlantPatch
		{
			[HarmonyFinalizer]
			private static Exception Finalizer(Exception __exception)
			{
				return Swallow(__exception);
			}
		}

		[HarmonyPatch(typeof(Debug), "Log", new Type[] { typeof(object) })]
		private static class LogSpamPatch
		{
			private static readonly string[] SpamPrefixes = new string[6] { "Got nearby cadaver plant", "Checking nearby cadaver plant", "Destroying: planttype", "planttype:", "moldPositions[", "setting to -1:" };

			[HarmonyPrefix]
			private static bool Prefix(object message)
			{
				try
				{
					if (!ModConfig.FixCadaverKillLag.Value || !(message is string text))
					{
						return true;
					}
					string[] spamPrefixes = SpamPrefixes;
					foreach (string value in spamPrefixes)
					{
						if (text.StartsWith(value, StringComparison.Ordinal))
						{
							return false;
						}
					}
					if (text.Contains("cadaver plants found at pos"))
					{
						return false;
					}
					return true;
				}
				catch
				{
					return true;
				}
			}
		}

		internal static int Swallowed;

		private static Exception Swallow(Exception __exception)
		{
			if (__exception == null || !ModConfig.FixCadaverKillLag.Value)
			{
				return __exception;
			}
			Swallowed++;
			if (Swallowed % 100 == 1)
			{
				Plugin.Logger.LogWarning((object)$"[CadaverKillFix] Swallowed vanilla plant-kill exception #{Swallowed}: {__exception.GetType().Name}");
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "OnPlayerDC")]
	internal static class CruiserSeatDisconnectFix
	{
		[HarmonyPostfix]
		private static void Postfix(StartOfRound __instance, int playerObjectNumber, ulong clientId)
		{
			try
			{
				if (!ModConfig.FixCruiserSeatDisconnect.Value || __instance.allPlayerScripts == null || playerObjectNumber < 0 || playerObjectNumber >= __instance.allPlayerScripts.Length)
				{
					return;
				}
				PlayerControllerB val = __instance.allPlayerScripts[playerObjectNumber];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)GameNetworkManager.Instance?.localPlayerController))
				{
					VehicleController[] array = Object.FindObjectsByType<VehicleController>((FindObjectsSortMode)0);
					foreach (VehicleController v in array)
					{
						Free(v, val, playerObjectNumber, clientId);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[CruiserSeatDC] {arg}");
			}
		}

		private static void Free(VehicleController v, PlayerControllerB p, int idx, ulong clientId)
		{
			if ((Object)(object)v == (Object)null)
			{
				return;
			}
			string text = "";
			if ((Object)(object)v.currentDriver == (Object)(object)p)
			{
				try
				{
					v.CancelIgnitionAnimation();
				}
				catch
				{
				}
				v.currentDriver = null;
				v.steeringAnimValue = 0f;
				text += " driver seat";
			}
			if ((Object)(object)v.currentPassenger == (Object)(object)p)
			{
				v.currentPassenger = null;
				if ((Object)(object)v.passengerSeatTrigger != (Object)null)
				{
					v.passengerSeatTrigger.interactable = true;
				}
				text += " passenger seat";
			}
			if (Release(v.driverSeatTrigger, p, idx))
			{
				text += " driver trigger";
			}
			if (Release(v.passengerSeatTrigger, p, idx))
			{
				text += " passenger trigger";
			}
			p.inVehicleAnimation = false;
			try
			{
				v.SetVehicleCollisionForPlayer(true, p);
			}
			catch
			{
			}
			NetworkManager singleton = NetworkManager.Singleton;
			NetworkObject networkObject = ((NetworkBehaviour)v).NetworkObject;
			if ((Object)(object)singleton != (Object)null && singleton.IsServer && (Object)(object)networkObject != (Object)null && networkObject.IsSpawned && networkObject.OwnerClientId != 0L && (networkObject.OwnerClientId == clientId || !singleton.ConnectedClientsIds.Contains(networkObject.OwnerClientId)))
			{
				networkObject.ChangeOwnership(0uL);
				text += " ownership->host";
			}
			if (text.Length > 0)
			{
				Plugin.Logger.LogInfo((object)("[CruiserSeatDC] " + p.playerUsername + " left the cruiser by disconnecting: freed" + text + "."));
			}
		}

		private static bool Release(InteractTrigger t, PlayerControllerB p, int idx)
		{
			if ((Object)(object)t == (Object)null)
			{
				return false;
			}
			if (!((Object)(object)t.lockedPlayer == (Object)(object)((Component)p).transform) && !((Object)(object)t.playerScriptInSpecialAnimation == (Object)(object)p) && (!t.isPlayingSpecialAnimation || !((Object)(object)p.currentTriggerInAnimationWith == (Object)(object)t)))
			{
				return false;
			}
			if ((Object)(object)t.lockedPlayer == (Object)(object)((Component)p).transform)
			{
				t.lockedPlayer = null;
			}
			if ((Object)(object)t.playerScriptInSpecialAnimation == (Object)(object)p)
			{
				t.playerScriptInSpecialAnimation = null;
			}
			t.SetInteractTriggerNotInAnimation(idx);
			return true;
		}
	}
	[HarmonyPatch(typeof(VehicleController), "Update")]
	internal static class CruiserYeetFix
	{
		private const float TeleportJumpMeters = 3f;

		private const float TeleportGraceSeconds = 1.5f;

		private const float DrunkThreshold = 0.2f;

		private const float ReleaseMargin = 1.5f;

		private static readonly Dictionary<ulong, Vector3> _lastRelPos = new Dictionary<ulong, Vector3>();

		private static readonly Dictionary<ulong, float> _graceUntil = new Dictionary<ulong, float>();

		private static readonly HashSet<ulong> _wasDeepInside = new HashSet<ulong>();

		private static readonly HashSet<ulong> _ghosted = new HashSet<ulong>();

		[HarmonyPostfix]
		private static void Postfix(VehicleController __instance)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if (instance?.allPlayerScripts == null || (Object)(object)__instance.boundsCollider == (Object)null)
				{
					return;
				}
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				Bounds bounds = ((Collider)__instance.boundsCollider).bounds;
				Bounds val = bounds;
				((Bounds)(ref val)).Expand(1.5f);
				Transform transform = ((Component)__instance).transform;
				PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
				foreach (PlayerControllerB val2 in allPlayerScripts)
				{
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					ulong playerClientId = val2.playerClientId;
					if (!val2.isPlayerControlled)
					{
						Release(__instance, val2, playerClientId);
						_wasDeepInside.Remove(playerClientId);
						continue;
					}
					Transform val3 = (((Object)(object)val2.overridePhysicsParent != (Object)null) ? val2.overridePhysicsParent : val2.physicsParent);
					bool flag = val2.inVehicleAnimation || ((Object)(object)val3 != (Object)null && ((Object)(object)val3 == (Object)(object)transform || val3.IsChildOf(transform)));
					Vector3 val4 = transform.InverseTransformPoint(((Component)val2).transform.position);
					if (_lastRelPos.TryGetValue(playerClientId, out var value))
					{
						Vector3 val5 = val4 - value;
						if (((Vector3)(ref val5)).sqrMagnitude > 9f)
						{
							_graceUntil[playerClientId] = realtimeSinceStartup + 1.5f;
						}
					}
					_lastRelPos[playerClientId] = val4;
					Vector3 position = ((Component)val2).transform.position;
					bool flag2 = ((Bounds)(ref bounds)).Contains(position + Vector3.up * 0.1f) && ((Bounds)(ref bounds)).Contains(position + Vector3.up);
					if (!ModConfig.FixCruiserYeet.Value || flag)
					{
						Release(__instance, val2, playerClientId);
						_wasDeepInside.Remove(playerClientId);
						continue;
					}
					if (_ghosted.Contains(playerClientId))
					{
						if (!((Bounds)(ref val)).Contains(position + Vector3.up))
						{
							Release(__instance, val2, playerClientId);
							Plugin.Logger.LogInfo((object)("[CruiserYeetFix] " + val2.playerUsername + " clear of cruiser — collision restored."));
						}
						continue;
					}
					float value2;
					bool flag3 = val2.drunkness > 0.2f || (_graceUntil.TryGetValue(playerClientId, out value2) && realtimeSinceStartup < value2);
					if (flag2 && !_wasDeepInside.Contains(playerClientId) && flag3)
					{
						__instance.SetVehicleCollisionForPlayer(false, val2);
						_ghosted.Add(playerClientId);
						Plugin.Logger.LogWarning((object)("[CruiserYeetFix] " + val2.playerUsername + " arrived inside cruiser bounds (" + ((val2.drunkness > 0.2f) ? "TZP" : "teleport") + ") — ghosted to prevent yeet."));
					}
					if (flag2)
					{
						_wasDeepInside.Add(playerClientId);
					}
					else
					{
						_wasDeepInside.Remove(playerClientId);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[CruiserYeetFix] {arg}");
			}
		}

		private static void Release(VehicleController v, PlayerControllerB p, ulong id)
		{
			if (_ghosted.Remove(id))
			{
				v.SetVehicleCollisionForPlayer(true, p);
			}
		}
	}
	internal static class ExactScan
	{
		[HarmonyPatch(typeof(Terminal), "TextPostProcess")]
		private static class TextPostProcessPatch
		{
			[HarmonyPrefix]
			private static void Prefix(ref string modifiedDisplayText)
			{
				if (!ModConfig.FixExactScan.Value || modifiedDisplayText == null || !modifiedDisplayText.Contains("[scanForItems]"))
				{
					return;
				}
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				try
				{
					int num = 0;
					int num2 = 0;
					GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
					foreach (GrabbableObject val in array)
					{
						if ((Object)(object)val == (Object)null || (Object)(object)val.itemProperties == (Object)null || !val.itemProperties.isScrap)
						{
							continue;
						}
						bool flag = val.isInShipRoom || val.isInElevator;
						bool num3;
						if (!instance.inShipPhase)
						{
							num3 = flag;
						}
						else
						{
							if (flag)
							{
								goto IL_00a0;
							}
							num3 = !val.isHeld;
						}
						if (num3)
						{
							continue;
						}
						goto IL_00a0;
						IL_00a0:
						num++;
						num2 += val.scrapValue;
					}
					string newValue = (instance.inShipPhase ? $"There are {num} objects in the ship, totalling at ${num2}." : $"There are {num} objects outside the ship, totalling at ${num2}.");
					modifiedDisplayText = modifiedDisplayText.Replace("[scanForItems]", newValue);
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogWarning((object)("[ExactScan] failed, falling back to vanilla scan: " + ex.Message));
				}
			}
		}

		private const string Token = "[scanForItems]";
	}
	internal static class FastCompanyTransit
	{
		private static readonly string[] TargetCoroutines = new string[4] { "openingDoorsSequence", "EndOfGame", "unloadSceneForAllPlayers", "TravelToLevelEffects" };

		private static bool _latchedFast;

		private static string _latchedWhy = "not latched";

		internal static bool LastAppliedFast { get; private set; }

		internal static string LatchState => (_latchedFast ? "fast" : "normal") + " (" + _latchedWhy + ")";

		internal static void Latch(string sequence)
		{
			try
			{
				string text = StartOfRound.Instance?.currentLevel?.PlanetName ?? "?";
				_latchedFast = Active();
				_latchedWhy = sequence + " @ " + text;
				Plugin.Logger.LogInfo((object)("[FastCompanyTransit] latched " + (_latchedFast ? "FAST" : "normal") + " for " + sequence + " on '" + text + "' " + $"(handshake fast={SuiteHandshake.AllPlayersHaveIt})."));
				SyncAnimatorSpeed();
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[FastCompanyTransit.Latch] {arg}");
				_latchedFast = false;
			}
		}

		internal static bool Active()
		{
			try
			{
				if (!ModConfig.FastCompanyTransit.Value || TournamentGate.Active)
				{
					return false;
				}
				if (!SuiteHandshake.AllPlayersHaveIt)
				{
					return false;
				}
				if (!ModConfig.FastTransitCompanyOnly.Value)
				{
					return true;
				}
				SelectableLevel val = StartOfRound.Instance?.currentLevel;
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return val.PlanetName != null && val.PlanetName.Contains("Gordion");
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[FastCompanyTransit.Active] {arg}");
				return false;
			}
		}

		internal static float ScaleWait(float seconds)
		{
			try
			{
				if (!_latchedFast)
				{
					return seconds;
				}
				float num = Mathf.Max(1f, ModConfig.FastTransitSpeed.Value);
				return seconds / num;
			}
			catch
			{
				return seconds;
			}
		}

		internal static void SyncAnimatorSpeed()
		{
			try
			{
				Animator val = StartOfRound.Instance?.shipAnimator;
				if (!((Object)(object)val == (Object)null))
				{
					bool latchedFast = _latchedFast;
					float num = (latchedFast ? Mathf.Max(1f, ModConfig.FastTransitSpeed.Value) : 1f);
					if (!Mathf.Approximately(val.speed, num))
					{
						val.speed = num;
					}
					LastAppliedFast = latchedFast;
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[FastCompanyTransit.SyncAnimatorSpeed] {arg}");
			}
		}

		internal static IEnumerable<MethodBase> TargetMethods()
		{
			List<MethodBase> list = new List<MethodBase>();
			string[] targetCoroutines = TargetCoroutines;
			foreach (string fragment in targetCoroutines)
			{
				try
				{
					Type type = AccessTools.FirstInner(typeof(StartOfRound), (Func<Type, bool>)((Type t) => t.Name.Contains(fragment)));
					if (type == null)
					{
						Plugin.Logger.LogWarning((object)("[FastCompanyTransit] coroutine '" + fragment + "' not found — skipped."));
						continue;
					}
					MethodInfo methodInfo = AccessTools.Method(type, "MoveNext", (Type[])null, (Type[])null);
					if (methodInfo == null)
					{
						Plugin.Logger.LogWarning((object)("[FastCompanyTransit] '" + fragment + "' has no MoveNext — skipped."));
					}
					else
					{
						list.Add(methodInfo);
					}
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)$"[FastCompanyTransit.TargetMethods:{fragment}] {arg}");
				}
			}
			return list;
		}

		internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			ConstructorInfo ctor = AccessTools.Constructor(typeof(WaitForSeconds), new Type[1] { typeof(float) }, false);
			MethodInfo scale = AccessTools.Method(typeof(FastCompanyTransit), "ScaleWait", (Type[])null, (Type[])null);
			int hits = 0;
			foreach (CodeInstruction ins in instructions)
			{
				if (ctor != null && ins.opcode == OpCodes.Newobj && ins.operand as ConstructorInfo == ctor)
				{
					hits++;
					yield return new CodeInstruction(OpCodes.Call, (object)scale);
				}
				yield return ins;
			}
			Plugin.Logger.LogInfo((object)$"[FastCompanyTransit] scaled {hits} WaitForSeconds call(s).");
		}
	}
	internal static class FastDropship
	{
		private const float VanillaArrive = 40f;

		private const float VanillaParked = 30f;

		internal const int MinSpeed = 1;

		internal const int MaxSpeed = 4;

		internal static float LastMultiplier { get; private set; } = 1f;

		internal static bool Enabled
		{
			get
			{
				if (ModConfig.FastDropship != null && ModConfig.FastDropship.Value && !TournamentGate.Active)
				{
					return AtCompany;
				}
				return false;
			}
		}

		internal static bool AtCompany
		{
			get
			{
				try
				{
					SelectableLevel val = StartOfRound.Instance?.currentLevel;
					return (Object)(object)val != (Object)null && (val.PlanetName.Contains("Gordion") || val.PlanetName.Contains("Company"));
				}
				catch
				{
					return false;
				}
			}
		}

		internal static float Multiplier => Mathf.Max(1f, ModConfig.FastDropshipSpeed.Value);

		internal static bool ShouldHold(ItemDropship ship)
		{
			try
			{
				if ((Object)(object)ship == (Object)null || !ModConfig.DropshipWaitForOpen.Value || TournamentGate.Active || !AtCompany)
				{
					return false;
				}
				if (ship.deliveringVehicle)
				{
					return false;
				}
				return ship.shipLanded && !ship.shipDoorsOpened;
			}
			catch
			{
				return false;
			}
		}

		internal static float ExtraTime()
		{
			float num = (LastMultiplier = Multiplier);
			return (num - 1f) * Time.deltaTime;
		}

		internal static void SyncAnimator(ItemDropship ship)
		{
			try
			{
				Animator val = ship?.shipAnimator;
				if (!((Object)(object)val == (Object)null))
				{
					float num = (Enabled ? Multiplier : 1f);
					if (!Mathf.Approximately(val.speed, num))
					{
						val.speed = num;
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[FastDropship.SyncAnimator] {arg}");
			}
		}

		internal static string Describe(float m)
		{
			m = Mathf.Max(1f, m);
			string text = (ModConfig.DropshipWaitForOpen.Value ? ", waits until opened" : "");
			if (!(m <= 1f))
			{
				return $"arrive ~{40f / m:0}s{text}, then leaves in ~{30f / m:0}s";
			}
			return "vanilla (40s arrive" + ((text.Length > 0) ? text : ", 30s parked") + ")";
		}

		internal static string SpeedTable()
		{
			List<string> list = new List<string>();
			for (int i = 1; i <= 4; i++)
			{
				list.Add(string.Format("{0}={1}", i, (i <= 1) ? "vanilla" : $"{40f / (float)i:0}s/{30f / (float)i:0}s"));
			}
			return string.Join(" · ", list);
		}

		internal static string StatusFragment()
		{
			if (ModConfig.FastDropship == null || !ModConfig.FastDropship.Value)
			{
				return "dropship: off (vanilla 40s arrive, 30s parked)";
			}
			if (TournamentGate.Active)
			{
				return "dropship: vanilla (tournament mode)";
			}
			float multiplier = Multiplier;
			if (!AtCompany)
			{
				return $"dropship: vanilla here, speed {multiplier:0.#} at the Company — {Describe(multiplier)}";
			}
			return $"dropship: speed {multiplier:0.#} — {Describe(multiplier)}";
		}
	}
	[HarmonyPatch(typeof(SandSpiderAI), "RemoveWeb")]
	internal static class WebSlowFix
	{
		[HarmonyPrefix]
		private static void Prefix(SandSpiderAI __instance, int trapID)
		{
			try
			{
				if (!ModConfig.FixSpiderWebSlow.Value || __instance.webTraps == null || trapID < 0 || trapID >= __instance.webTraps.Count)
				{
					return;
				}
				SandSpiderWebTrap val = __instance.webTraps[trapID];
				PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController;
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && val.hinderingLocalPlayer)
				{
					val.hinderingLocalPlayer = false;
					val2.isMovementHindered = Mathf.Max(0, val2.isMovementHindered - 1);
					val2.hinderedMultiplier *= 0.4f;
					if ((Object)(object)val.currentTrappedPlayer == (Object)(object)val2)
					{
						val.currentTrappedPlayer = null;
					}
					if ((Object)(object)val.webAudio != (Object)null)
					{
						val.webAudio.Stop();
					}
					Plugin.Logger.LogInfo((object)"[HighQuotaFixes] Cleaned up removed web trapping the local player.");
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[WebSlowFix] {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Debug), "LogError", new Type[] { typeof(object) })]
	internal static class RpcErrorDespam
	{
		[HarmonyPrefix]
		private static bool Prefix(object message)
		{
			try
			{
				if (!ModConfig.MuteThrowRpcSpam.Value || !(message is string text))
				{
					return true;
				}
				return !text.StartsWith("ThrowObjectClientRpc called for an object", StringComparison.Ordinal);
			}
			catch
			{
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")]
	internal static class StuckStateWatchdog
	{
		private const float ThrowStuckSeconds = 2f;

		private static float _throwingSince;

		[HarmonyPostfix]
		private static void Postfix(PlayerControllerB __instance)
		{
			try
			{
				PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
				if ((Object)(object)val == (Object)null || (Object)(object)__instance != (Object)(object)val)
				{
					return;
				}
				if (ModConfig.FixStuckInteract.Value)
				{
					if (!val.throwingObject)
					{
						_throwingSince = 0f;
					}
					else
					{
						float realtimeSinceStartup = Time.realtimeSinceStartup;
						if (_throwingSince == 0f)
						{
							_throwingSince = realtimeSinceStartup;
						}
						else if (realtimeSinceStartup - _throwingSince > 2f)
						{
							val.throwingObject = false;
							_throwingSince = 0f;
							Plugin.Logger.LogWarning((object)$"[HighQuotaFixes] throwingObject stuck >{2f}s — cleared (interact glitch).");
						}
					}
				}
				if (ModConfig.FixSpiderWebSlow.Value)
				{
					if (val.isMovementHindered < 0)
					{
						Plugin.Logger.LogWarning((object)$"[HighQuotaFixes] isMovementHindered was {val.isMovementHindered} — reset to 0.");
						val.isMovementHindered = 0;
					}
					if (val.isMovementHindered == 0 && val.hinderedMultiplier != 1f)
					{
						Plugin.Logger.LogWarning((object)$"[HighQuotaFixes] hinderedMultiplier drifted to {val.hinderedMultiplier:0.###} with nothing hindering — reset to 1.");
						val.hinderedMultiplier = 1f;
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[StuckStateWatchdog] {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(GrabbableObject), "Update")]
	internal static class ItemSleeper
	{
		private static int _sleptThisFrame;

		private static int _sleptLastFrame;

		private static int _frame;

		private static VehicleController _vehicle;

		private static Transform _vehicleRoot;

		private static float _nextVehicleRefresh;

		private static int _dropSoundFrame;

		private static int _dropSoundsThisFrame;

		private static BeltBagItem[] _bags = (BeltBagItem[])(object)new BeltBagItem[0];

		private static float _nextBagRefresh;

		internal static int SleepingCount => _sleptLastFrame;

		private static bool InBeltBag(GrabbableObject o)
		{
			if (BeltBagWatch.InBag(o))
			{
				return true;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup >= _nextBagRefresh)
			{
				_nextBagRefresh = realtimeSinceStartup + 3f;
				_bags = Object.FindObjectsByType<BeltBagItem>((FindObjectsSortMode)0);
			}
			BeltBagItem[] bags = _bags;
			foreach (BeltBagItem val in bags)
			{
				if (!((Object)(object)val == (Object)null) && val.placingItemsInBag > 0)
				{
					return true;
				}
			}
			return false;
		}

		[HarmonyPrefix]
		private static bool Prefix(GrabbableObject __instance)
		{
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: 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)
			try
			{
				if (!ModConfig.SleepShipItems.Value)
				{
					return true;
				}
				if (__instance.isHeld || __instance.isPocketed || __instance.isBeingUsed)
				{
					return true;
				}
				if (InBeltBag(__instance))
				{
					return true;
				}
				bool flag = __instance.isInShipRoom || __instance.isInElevator;
				bool flag2 = flag;
				if (!flag2)
				{
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					if (realtimeSinceStartup >= _nextVehicleRefresh)
					{
						_nextVehicleRefresh = realtimeSinceStartup + 5f;
						_vehicle = Object.FindObjectOfType<VehicleController>();
						_vehicleRoot = (((Object)(object)_vehicle != (Object)null) ? ((Component)_vehicle).transform : null);
					}
					flag2 = (Object)(object)_vehicleRoot != (Object)null && ((Component)__instance).transform.IsChildOf(_vehicleRoot) && (!((Object)(object)_vehicle != (Object)null) || !_vehicle.magnetedToShip);
				}
				if (!__instance.reachedFloorTarget || !__instance.hasHitGround)
				{
					Transform val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.elevatorTransform : null);
					bool flag3 = (Object)(object)val != (Object)null && (Object)(object)((Component)__instance).transform.parent == (Object)(object)val;
					if (!flag || !flag3 || !ModConfig.SnapAboardItems.Value)
					{
						return true;
					}
					((Component)__instance).transform.localPosition = __instance.targetFloorPosition;
					if ((Object)(object)__instance.itemProperties != (Object)null)
					{
						((Component)__instance).transform.rotation = Quaternion.Euler(__instance.itemProperties.restingRotation.x, (__instance.floorYRot == -1) ? ((Component)__instance).transform.eulerAngles.y : ((float)__instance.floorYRot), __instance.itemProperties.restingRotation.z);
					}
					__instance.reachedFloorTarget = true;
					__instance.hasHitGround = true;
					__instance.fallTime = 1f;
					int frameCount = Time.frameCount;
					if (frameCount != _dropSoundFrame)
					{
						_dropSoundFrame = frameCount;
						_dropSoundsThisFrame = 0;
					}
					if (_dropSoundsThisFrame < 3)
					{
						_dropSoundsThisFrame++;
						__instance.PlayDropSFX();
						__instance.OnHitGround();
					}
				}
				else if (!flag2)
				{
					return true;
				}
				int frameCount2 = Time.frameCount;
				if (frameCount2 != _frame)
				{
					_sleptLastFrame = _sleptThisFrame;
					_sleptThisFrame = 0;
					_frame = frameCount2;
				}
				_sleptThisFrame++;
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ItemSleeper] {arg}");
				return true;
			}
		}
	}
	internal static class JoinNameSync
	{
		[HarmonyPatch(typeof(PlayerControllerB), "SendNewPlayerValuesServerRpc")]
		private static class ServerRpcPatch
		{
			[HarmonyPrefix]
			private static void Prefix(PlayerControllerB __instance, out bool __state)
			{
				__state = __instance.sentPlayerValues;
			}

			[HarmonyPostfix]
			private static void Postfix(PlayerControllerB __instance, bool __state)
			{
				try
				{
					NetworkManager singleton = NetworkManager.Singleton;
					if (!__state && __instance.sentPlayerValues && !((Object)(object)singleton == (Object)null) && singleton.IsHost && HostOn && !((Object)(object)__instance == (Object)(object)GameNetworkManager.Instance?.localPlayerController))
					{
						if (_co != null)
						{
							((MonoBehaviour)CoroutineRunner.Instance).StopCoroutine(_co);
						}
						_co = ((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(AfterJoin(__instance));
					}
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)$"[JoinNameSync.ServerRpcPatch] {arg}");
				}
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "SendNewPlayerValuesClientRpc")]
		private static class ClientRpcPatch
		{
			[HarmonyPrefix]
			private static void Prefix(ulong[] playerSteamIds)
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton != (Object)null && singleton.IsServer && playerSteamIds != null && playerSteamIds.Length != 0)
				{
					_slotCount = playerSteamIds.Length;
				}
			}

			[HarmonyPostfix]
			private static void Postfix()
			{
				try
				{
					NetworkManager singleton = NetworkManager.Singleton;
					if ((Object)(object)singleton != (Object)null && !singleton.IsHost && ClientOn && _hostNames.Count > 0)
					{
						ApplyHostNames(fromMessage: false);
					}
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)$"[JoinNameSync.ClientRpcPatch] {arg}");
				}
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnNames;
		}

		internal const string Msg = "SimpleOptimize.Names";

		private const byte Proto = 1;

		private static readonly float[] Passes = new float[3] { 2f, 8f, 20f };

		private static int _slotCount = 4;

		private static Coroutine _co;

		private static readonly Dictionary<ulong, string> _hostNames = new Dictionary<ulong, string>();

		private static bool HostOn
		{
			get
			{
				if (ModConfig.HostNameSync != null)
				{
					return ModConfig.HostNameSync.Value;
				}
				return true;
			}
		}

		private static bool ResendOn
		{
			get
			{
				if (HostOn)
				{
					if (ModConfig.ResendSteamIds != null)
					{
						return ModConfig.ResendSteamIds.Value;
					}
					return true;
				}
				return false;
			}
		}

		private static bool ClientOn
		{
			get
			{
				if (ModConfig.FixUnknownNames != null)
				{
					return ModConfig.FixUnknownNames.Value;
				}
				return true;
			}
		}

		internal static void StartSession()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			try
			{
				_hostNames.Clear();
				_slotCount = 4;
				if (_co != null)
				{
					((MonoBehaviour)CoroutineRunner.Instance).StopCoroutine(_co);
					_co = null;
				}
				NetworkManager singleton = NetworkManager.Singleton;
				if (((singleton != null) ? singleton.CustomMessagingManager : null) != null && singleton.IsClient && !singleton.IsHost)
				{
					CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
					object obj = <>O.<0>__OnNames;
					if (obj == null)
					{
						HandleNamedMessageDelegate val = OnNames;
						<>O.<0>__OnNames = val;
						obj = (object)val;
					}
					customMessagingManager.RegisterNamedMessageHandler("SimpleOptimize.Names", (HandleNamedMessageDelegate)obj);
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[JoinNameSync.StartSession] {arg}");
			}
		}

		private static IEnumerator AfterJoin(PlayerControllerB joiner)
		{
			string text = (((Object)(object)joiner != (Object)null) ? $"player {joiner.playerClientId}" : "a player");
			Plugin.Logger.LogInfo((object)$"[Names] host: {text} joined; re-sending names in {Passes[0]:0}/{Passes[1]:0}/{Passes[2]:0} s.");
			float num = 0f;
			for (int pass = 0; pass < Passes.Length; pass++)
			{
				yield return (object)new WaitForSecondsRealtime(Passes[pass] - num);
				num = Passes[pass];
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null || !singleton.IsHost || !singleton.IsListening || (Object)(object)StartOfRound.Instance == (Object)null || !HostOn)
				{
					break;
				}
				try
				{
					Pass(joiner, pass + 1);
				}
				catch (Exception arg)
				{
					Plugin.Logger.LogError((object)$"[JoinNameSync.Pass] {arg}");
				}
			}
			_co = null;
		}

		private static void Pass(PlayerControllerB joiner, int pass)
		{
			StartOfRound instance = StartOfRound.Instance;
			PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
			if (allPlayerScripts == null)
			{
				return;
			}
			List<(int, ulong, string)> list = new List<(int, ulong, string)>();
			for (int i = 0; i < allPlayerScripts.Length; i++)
			{
				PlayerControllerB val = allPlayerScripts[i];
				if (!((Object)(object)val == (Object)null) && val.playerSteamId != 0L && (val.isPlayerControlled || val.isPlayerDead) && !UnknownNameFix.IsUnresolved(val.playerUsername) && !val.playerUsername.StartsWith("Player #"))
				{
					list.Add((i, val.playerSteamId, val.playerUsername));
				}
			}
			int num = SendNames(list);
			string arg = "off";
			if (ResendOn && (Object)(object)joiner != (Object)null && joiner.isPlayerControlled)
			{
				int num2 = Mathf.Min(_slotCount, allPlayerScripts.Length);
				ulong[] array = new ulong[num2];
				for (int j = 0; j < num2; j++)
				{
					array[j] = (((Object)(object)allPlayerScripts[j] != (Object)null) ? allPlayerScripts[j].playerSteamId : 0);
				}
				joiner.SendNewPlayerValuesClientRpc(array);
				arg = $"{num2} slots";
			}
			Plugin.Logger.LogInfo((object)($"[Names] host pass {pass}/{Passes.Length}: vanilla Steam-ID re-send {arg}; " + $"{list.Count} names to {num} other player(s)."));
		}

		private unsafe static int SendNames(List<(int slot, ulong steamId, string name)> rows)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if (((singleton != null) ? singleton.CustomMessagingManager : null) == null || rows.Count == 0)
			{
				return 0;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, 16384);
			try
			{
				byte b = 1;
				((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
				int count = rows.Count;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				foreach (var row in rows)
				{
					(int, ulong, string) current = row;
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref current.Item1, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref current.Item2, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe(current.Item3, false);
				}
				int num = 0;
				foreach (ulong connectedClientsId in singleton.ConnectedClientsIds)
				{
					if (connectedClientsId != singleton.LocalClientId)
					{
						singleton.CustomMessagingManager.SendNamedMessage("SimpleOptimize.Names", connectedClientsId, val, (NetworkDelivery)3);
						num++;
					}
				}
				return num;
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void OnNames(ulong sender, FastBufferReader reader)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (sender != 0L || !ClientOn)
				{
					return;
				}
				byte b = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
				if (b != 1)
				{
					return;
				}
				int num = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				if (num < 0 || num > 1024)
				{
					return;
				}
				int num2 = default(int);
				ulong num3 = default(ulong);
				string value = default(string);
				for (int i = 0; i < num; i++)
				{
					((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives));
					((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num3, default(ForPrimitives));
					((FastBufferReader)(ref reader)).ReadValueSafe(ref value, false);
					if (num3 != 0L && !string.IsNullOrEmpty(value))
					{
						_hostNames[num3] = value;
					}
				}
				ApplyHostNames(fromMessage: true);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[JoinNameSync.OnNames] {arg}");
			}
		}

		private static void ApplyHostNames(bool fromMessage)
		{
			StartOfRound instance = StartOfRound.Instance;
			if (instance?.allPlayerScripts == null)
			{
				return;
			}
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			for (int i = 0; i < instance.allPlayerScripts.Length; i++)
			{
				PlayerControllerB val2 = instance.allPlayerScripts[i];
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)val) && val2.playerSteamId != 0L && (val2.isPlayerControlled || val2.isPlayerDead) && UnknownNameFix.IsUnresolved(val2.playerUsername) && _hostNames.TryGetValue(val2.playerSteamId, out var value))
				{
					string name = UnknownNameFix.Clean(val2, value);
					if (!UnknownNameFix.IsUnresolved(name))
					{
						UnknownNameFix.SetName(val2, i, name, fromMessage ? "host" : "host, kept");
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "SyncShipUnlockablesServerRpc")]
	internal static class JoinScrapValueFix
	{
		private const int Vanilla = 500;

		private static bool _warned;

		private static int _cap = 500;

		public static int Limit(int vanilla)
		{
			if (_cap >= vanilla)
			{
				return _cap;
			}
			return vanilla;
		}

		[HarmonyPrefix]
		private static void Prefix()
		{
			try
			{
				_cap = (ModConfig.FixJoinScrapValues.Value ? Mathf.Clamp(ModConfig.JoinSyncMaxItems.Value, 500, 20000) : 500);
				if (!ModConfig.FixJoinScrapValues.Value || (Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer)
				{
					return;
				}
				int num = Object.FindObjectsByType<GrabbableObject>((FindObjectsInactive)0, (FindObjectsSortMode)0).Length;
				if (num > 500)
				{
					Plugin.Logger.LogInfo((object)$"[JoinScrapValues] join sync: {num} item(s) on the map, sending up to {_cap} (vanilla stops at {500}).");
					if (num > _cap && !_warned)
					{
						_warned = true;
						Plugin.Logger.LogWarning((object)$"[JoinScrapValues] {num} items is more than [Fixes] JoinSyncMaxItems = {_cap}: the farthest {num - _cap} from the map centre still show $0 to joiners.");
					}
				}
			}
			catch (Exception arg)
			{
				_cap = 500;
				Plugin.Logger.LogError((object)$"[JoinScrapValues] {arg}");
			}
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> codes)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(codes);
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (val.opcode == OpCodes.Ldc_I4 && val.operand is int num2 && num2 == 500)
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(JoinScrapValueFix), "Limit", (Type[])null, (Type[])null)));
					num++;
					i++;
				}
			}
			if (num != 1)
			{
				Plugin.Logger.LogWarning((object)($"[JoinScrapValues] expected one '500' in SyncShipUnlockablesServerRpc, found {num} (game update?)" + ((num == 0) ? "; vanilla cap kept." : "; all of them were lifted.")));
			}
			return list;
		}
	}
	internal static class RadarNames
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnOrder;
		}

		internal const string Msg = "SimpleOptimize.RadarOrder";

		private const byte Proto = 1;

		private const byte KindPlayer = 0;

		private const byte KindOther = 1;

		private static readonly Dictionary<int, string> _hostNames = new Dictionary<int, string>();

		private static bool _dirty;

		private static string _lastSent = "";

		private static Coroutine _tick;

		private static bool On
		{
			get
			{
				if (ModConfig.FixRadarNames != null)
				{
					return ModConfig.FixRadarNames.Value;
				}
				return false;
			}
		}

		private static bool SyncOn
		{
			get
			{
				if (On && ModConfig.SyncRadarOrder != null)
				{
					return ModConfig.SyncRadarOrder.Value;
				}
				return false;
			}
		}

		internal static void StartSession()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			try
			{
				_hostNames.Clear();
				_lastSent = "";
				_dirty = true;
				NetworkManager singleton = NetworkManager.Singleton;
				if (((singleton != null) ? singleton.CustomMessagingManager : null) != null && singleton.IsClient && !singleton.IsHost)
				{
					CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
					object obj = <>O.<0>__OnOrder;
					if (obj == null)
					{
						HandleNamedMessageDelegate val = OnOrder;
						<>O.<0>__OnOrder = val;
						obj = (object)val;
					}
					customMessagingManager.RegisterNamedMessageHandler("SimpleOptimize.RadarOrder", (HandleNamedMessageDelegate)obj);
				}
				if (_tick != null)
				{
					((MonoBehaviour)CoroutineRunner.Instance).StopCoroutine(_tick);
				}
				_tick = ((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(Tick());
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[RadarNames.StartSession] {arg}");
			}
		}

		internal static void MarkDirty()
		{
			_dirty = true;
		}

		private static IEnumerator Tick()
		{
			WaitForSecondsRealtime wait = new WaitForSecondsRealtime(1f);
			int n = 0;
			while (true)
			{
				yield return wait;
				if ((Object)(object)StartOfRound.Instance == (Object)null)
				{
					continue;
				}
				Relabel(StartOfRound.Instance.mapScreen);
				NetworkManager singleton = NetworkManager.Singleton;
				if (!((Object)(object)singleton != (Object)null) || !singleton.IsHost)
				{
					continue;
				}
				if (!_dirty)
				{
					int num = n + 1;
					n = num;
					if (num % 10 != 0)
					{
						continue;
					}
				}
				_dirty = false;
				Broadcast(force: false);
			}
		}

		private static bool Unresolved(string n)
		{
			if (!string.IsNullOrEmpty(n) && !(n == "unknown"))
			{
				return n == "Nameless";
			}
			return true;
		}

		private static string NameFor(PlayerControllerB p)
		{
			string playerUsername = p.playerUsername;
			NetworkManager singleton = NetworkManager.Singleton;
			if (Unresolved(playerUsername) && (Object)(object)singleton != (Object)null && !singleton.IsHost && (p.isPlayerControlled || p.isPlayerDead) && _hostNames.TryGetValue((int)p.playerClientId, out var value) && !Unresolved(value))
			{
				return value;
			}
			return playerUsername;
		}

		internal static void Relabel(ManualCameraRenderer map)
		{
			try
			{
				if (!On || map?.radarTargets == null)
				{
					return;
				}
				List<TransformAndName> radarTargets = map.radarTargets;
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				bool changed = false;
				for (int i = 0; i < radarTargets.Count; i++)
				{
					TransformAndName val = radarTargets[i];
					if (val == null || val.isNonPlayer || (Object)(object)val.transform == (Object)null)
					{
						continue;
					}
					PlayerControllerB component = ((Component)val.transform).GetComponent<PlayerControllerB>();
					if ((Object)(object)component == (Object)null)
					{
						continue;
					}
					string text = NameFor(component);
					if (string.IsNullOrEmpty(text))
					{
						continue;
					}
					if (component.isPlayerControlled || component.isPlayerDead)
					{
						dictionary.TryGetValue(text, out var value);
						value = (dictionary[text] = value + 1);
						if (value > 1)
						{
							text += value;
						}
					}
					if (val.name != text)
					{
						Plugin.Logger.LogInfo((object)($"[Radar] monitor entry #{i} follows {((Object)((Component)component).gameObject).name} (slot {component.playerClientId}) " + "but was labelled '" + val.name + "'; now '" + text + "'."));
						val.name = text;
						changed = true;
					}
				}
				RefreshLabel(map, changed);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[RadarNames.Relabel] {arg}");
			}
		}

		private static void RefreshLabel(ManualCameraRenderer map, bool changed)
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)map != (Object)(object)instance.mapScreen || (Object)(object)instance.mapScreenPlayerName == (Object)null)
			{
				return;
			}
			int targetTransformIndex = map.targetTransformIndex;
			if (targetTransformIndex >= 0 && targetTransformIndex < map.radarTargets.Count && map.radarTargets[targetTransformIndex] != null)
			{
				string text = map.radarTargets[targetTransformIndex].name ?? "";
				if (changed || ((TMP_Text)instance.mapScreenPlayerName).text != text)
				{
					((TMP_Text)instance.mapScreenPlayerName).text = text;
				}
			}
		}

		internal static void OnHostClientConnected(ulong clientId)
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (SyncOn && !((Object)(object)singleton == (Object)null) && singleton.IsHost && clientId != singleton.LocalClientId)
				{
					((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(SendLater(clientId));
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[RadarNames.OnHostClientConnected] {arg}");
			}
		}

		private static IEnumerator SendLater(ulong clientId)
		{
			float[] array = new float[3] { 3f, 8f, 20f };
			foreach (float num in array)
			{
				yield return (object)new WaitForSecondsRealtime(num);
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null || !singleton.IsHost || !singleton.ConnectedClientsIds.Contains(clientId))
				{
					break;
				}
				Send(new ulong[1] { clientId }, Build(out var _));
			}
		}

		private static List<(byte kind, ulong key, string name)> Build(out string fingerprint)
		{
			List<(byte, ulong, string)> list = new List<(byte, ulong, string)>();
			StringBuilder stringBuilder = new StringBuilder();
			ManualCameraRenderer val = StartOfRound.Instance?.mapScreen;
			if (val?.radarTargets != null)
			{
				foreach (TransformAndName radarTarget in val.radarTargets)
				{
					if ((Object)(object)radarTarget?.transform == (Object)null)
					{
						continue;
					}
					PlayerControllerB val2 = (radarTarget.isNonPlayer ? null : ((Component)radarTarget.transform).GetComponent<PlayerControllerB>());
					if ((Object)(object)val2 != (Object)null)
					{
						list.Add((0, val2.playerClientId, radarTarget.name ?? ""));
					}
					else
					{
						NetworkObject component = ((Component)radarTarget.transform).GetComponent<NetworkObject>();
						if ((Object)(object)component == (Object)null)
						{
							continue;
						}
						list.Add((1, component.NetworkObjectId, radarTarget.name ?? ""));
					}
					(byte, ulong, string) tuple = list[list.Count - 1];
					stringBuilder.Append(tuple.Item1).Append(':').Append(tuple.Item2)
						.Append(':')
						.Append(tuple.Item3)
						.Append('|');
				}
			}
			fingerprint = stringBuilder.ToString();
			return list;
		}

		private static void Broadcast(bool force)
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (!SyncOn || (Object)(object)singleton == (Object)null || !singleton.IsHost)
				{
					return;
				}
				string fingerprint;
				List<(byte, ulong, string)> rows = Build(out fingerprint);
				if (!force && fingerprint == _lastSent)
				{
					return;
				}
				_lastSent = fingerprint;
				List<ulong> list = new List<ulong>();
				foreach (ulong connectedClientsId in singleton.ConnectedClientsIds)
				{
					if (connectedClientsId != singleton.LocalClientId)
					{
						list.Add(connectedClientsId);
					}
				}
				if (list.Count > 0)
				{
					Send(list, rows);
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[RadarNames.Broadcast] {arg}");
			}
		}

		private unsafe static void Send(IReadOnlyList<ulong> to, List<(byte kind, ulong key, string name)> rows)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if (((singleton != null) ? singleton.CustomMessagingManager : null) == null || rows.Count == 0)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, 16384);
			try
			{
				byte b = 1;
				((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
				int count = rows.Count;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				foreach (var row in rows)
				{
					(byte, ulong, string) current = row;
					((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref current.Item1, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref current.Item2, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe(current.Item3, false);
				}
				foreach (ulong item in to)
				{
					singleton.CustomMessagingManager.SendNamedMessage("SimpleOptimize.RadarOrder", item, val, (NetworkDelivery)3);
				}
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void OnOrder(ulong sender, FastBufferReader reader)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (sender != 0L || !SyncOn)
				{
					return;
				}
				byte b = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
				if (b != 1)
				{
					return;
				}
				int num = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				if (num >= 0 && num <= 1024)
				{
					List<(byte, ulong, string)> list = new List<(byte, ulong, string)>(num);
					byte item = default(byte);
					ulong item2 = default(ulong);
					string item3 = default(string);
					for (int i = 0; i < num; i++)
					{
						((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref item, default(ForPrimitives));
						((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref item2, default(ForPrimitives));
						((FastBufferReader)(ref reader)).ReadValueSafe(ref item3, false);
						list.Add((item, item2, item3));
					}
					Apply(list);
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[RadarNames.OnOrder] {arg}");
			}
		}

		private static void Apply(List<(byte kind, ulong key, string name)> rows)
		{
			ManualCameraRenderer val = StartOfRound.Instance?.mapScreen;
			if (val?.radarTargets == null)
			{
				return;
			}
			_hostNames.Clear();
			foreach (var row in rows)
			{
				if (row.kind == 0 && !string.IsNullOrEmpty(row.name))
				{
					_hostNames[(int)row.key] = StripDupSuffix(row.name, (int)row.key);
				}
			}
			List<TransformAndName> radarTargets = val.radarTargets;
			Transform followed = ((val.targetTransformIndex < 0 || val.targetTransformIndex >= radarTargets.Count) ? null : radarTargets[val.targetTransformIndex]?.transform);
			List<TransformAndName> list = new List<TransformAndName>(radarTargets);
			List<TransformAndName> list2 = new List<TransformAndName>(radarTargets.Count);
			foreach (var r in rows)
			{
				int num = list.FindIndex((TransformAndName t) => Matches(t, r.kind, r.key));
				if (num >= 0)
				{
					list2.Add(list[num]);
					list.RemoveAt(num);
				}
			}
			list2.AddRange(list);
			bool flag = false;
			for (int num2 = 0; num2 < list2.Count; num2++)
			{
				if (flag)
				{
					break;
				}
				flag = list2[num2] != radarTargets[num2];
			}
			if (flag)
			{
				val.radarTargets = list2;
				if ((Object)(object)followed != (Object)null)
				{
					int num3 = list2.FindIndex((TransformAndName t) => (Object)(object)t?.transform == (Object)(object)followed);
					if (num3 >= 0)
					{
						val.targetTransformIndex = num3;
					}
				}
				Plugin.Logger.LogInfo((object)$"[Radar] lined up the monitor list with the host's order ({list2.Count} entries).");
			}
			Relabel(val);
		}

		private static string StripDupSuffix(string label, int slot)
		{
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if (array != null && slot >= 0 && slot < array.Length && (Object)(object)array[slot] != (Object)null)
			{
				string playerUsername = array[slot].playerUsername;
				if (!string.IsNullOrEmpty(playerUsername) && label.StartsWith(playerUsername) && label.Le