Decompiled source of PraetorisClient v0.1.31

PraetorisClient.dll

Decompiled 18 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PraetorisClient")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PraetorisClient")]
[assembly: AssemblyCopyright("Copyright ©  2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("3A303D15-4E60-4110-8EDF-EDF38560FBE2")]
[assembly: AssemblyFileVersion("0.1.31")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.31.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace PraetorisClient
{
	internal static class BotApiClient
	{
		public static IEnumerator PostLinkRoutine(LinkRequest link, Action<long, string, bool, string> sendResult)
		{
			string linkApiUrl = PraetorisClientPlugin.GetLinkApiUrl();
			string botApiKey = PraetorisClientPlugin.GetBotApiKey();
			if (string.IsNullOrWhiteSpace(linkApiUrl))
			{
				sendResult(link.Sender, link.RequestId, arg3: false, "Link API URL is not configured on the server.");
				yield break;
			}
			if (string.IsNullOrWhiteSpace(botApiKey))
			{
				sendResult(link.Sender, link.RequestId, arg3: false, "Bot API key is not configured on the server.");
				yield break;
			}
			string s = JsonLinkRequest(link);
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			UnityWebRequest request = new UnityWebRequest(linkApiUrl, "POST")
			{
				uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes),
				downloadHandler = (DownloadHandler)new DownloadHandlerBuffer()
			};
			try
			{
				request.SetRequestHeader("Content-Type", "application/json");
				request.SetRequestHeader("X-API-Key", botApiKey);
				request.SetRequestHeader("User-Agent", "PraetorisClient/0.1");
				yield return request.SendWebRequest();
				string text = ((request.downloadHandler != null) ? request.downloadHandler.text : "");
				if ((int)request.result != 1 || request.responseCode < 200 || request.responseCode >= 300)
				{
					string text2 = ((!string.IsNullOrWhiteSpace(text)) ? text : (string.IsNullOrWhiteSpace(request.error) ? ("HTTP " + request.responseCode) : (request.error + " (HTTP " + request.responseCode + ")")));
					PraetorisClientPlugin.Log.LogWarning((object)("Discord link API failed for " + link.PlayerId + ": " + text2));
					sendResult(link.Sender, link.RequestId, arg3: false, text2);
				}
				else
				{
					string arg = (string.IsNullOrWhiteSpace(text) ? "Discord link complete." : text);
					PraetorisClientPlugin.Log.LogInfo((object)("Discord link API accepted " + link.PlayerId + "."));
					sendResult(link.Sender, link.RequestId, arg3: true, arg);
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private static string JsonLinkRequest(LinkRequest link)
		{
			return "{\"requestId\":\"" + EscapeJson(link.RequestId) + "\",\"code\":\"" + EscapeJson(link.Code) + "\",\"playerId\":\"" + EscapeJson(link.PlayerId) + "\",\"playerName\":\"" + EscapeJson(link.PlayerName) + "\",\"endpoint\":\"" + EscapeJson(link.Endpoint) + "\",\"platformDisplayName\":\"" + EscapeJson(link.PlatformDisplayName) + "\",\"receivedAtUtc\":\"" + EscapeJson(link.ReceivedAtUtc.ToString("O", CultureInfo.InvariantCulture)) + "\"}";
		}

		private static string EscapeJson(string value)
		{
			return (value ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r")
				.Replace("\n", "\\n");
		}
	}
	internal static class CreativeBiomeOverride
	{
		private sealed class OverrideZone
		{
			public Vector3 Center { get; }

			public float Radius { get; }

			public float RadiusSquared { get; }

			public float TerrainOuterRadius { get; }

			public float TerrainOuterRadiusSquared { get; }

			public float TerrainEdgeFalloffWidth { get; }

			public float TerrainEdgeFloorHeight { get; }

			public float VisualRadius { get; }

			public float VisualRadiusSquared { get; }

			public float TerrainPatchHalfSize { get; }

			public float GrassResetRadius { get; }

			public Biome Biome { get; }

			public bool SuppressSpawns { get; }

			public bool UseTerrainSource { get; }

			public Vector3 TerrainSourceCenter { get; }

			public bool SuppressVegetationDrops { get; }

			public OverrideZone(Vector3 center, float radius, Biome biome, bool suppressSpawns, bool useTerrainSource, Vector3 terrainSourceCenter, float terrainPatchHalfSize, float terrainEdgeFalloffWidth, float terrainEdgeFloorHeight, bool suppressVegetationDrops)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
				Center = center;
				Radius = radius;
				RadiusSquared = radius * radius;
				TerrainOuterRadius = radius + Mathf.Max(0f, terrainEdgeFalloffWidth);
				TerrainOuterRadiusSquared = TerrainOuterRadius * TerrainOuterRadius;
				TerrainEdgeFalloffWidth = Mathf.Max(0f, terrainEdgeFalloffWidth);
				TerrainEdgeFloorHeight = terrainEdgeFloorHeight;
				VisualRadius = radius + 16f;
				VisualRadiusSquared = VisualRadius * VisualRadius;
				TerrainPatchHalfSize = (useTerrainSource ? Mathf.Max(32f, terrainPatchHalfSize) : radius);
				GrassResetRadius = (useTerrainSource ? (Mathf.Sqrt(2f) * (TerrainPatchHalfSize + 32f)) : radius);
				Biome = biome;
				SuppressSpawns = suppressSpawns;
				UseTerrainSource = useTerrainSource;
				TerrainSourceCenter = terrainSourceCenter;
				SuppressVegetationDrops = suppressVegetationDrops;
			}

			public bool Contains(float x, float z)
			{
				return ContainsRadius(x, z, RadiusSquared);
			}

			public bool ContainsTerrain(float x, float z)
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: 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)
				if (!UseTerrainSource)
				{
					return ContainsRadius(x, z, RadiusSquared);
				}
				Vector3 zonePos = ZoneSystem.GetZonePos(ZoneSystem.GetZone(new Vector3(x, 0f, z)));
				if (Mathf.Abs(zonePos.x - Center.x) <= TerrainPatchHalfSize && Mathf.Abs(zonePos.z - Center.z) <= TerrainPatchHalfSize)
				{
					return ContainsRadius(x, z, TerrainOuterRadiusSquared);
				}
				return false;
			}

			public bool IntersectsTerrain(Heightmap heightmap)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_006d: 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)
				//IL_0091: 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)
				Vector3 position = ((Component)heightmap).transform.position;
				Vector3 zonePos = ZoneSystem.GetZonePos(ZoneSystem.GetZone(position));
				if (Mathf.Abs(zonePos.x - Center.x) > TerrainPatchHalfSize || Mathf.Abs(zonePos.z - Center.z) > TerrainPatchHalfSize)
				{
					return false;
				}
				float num = (float)heightmap.m_width * heightmap.m_scale * 0.5f;
				float num2 = Math.Max(Math.Abs(position.x - Center.x) - num, 0f);
				float num3 = Math.Max(Math.Abs(position.z - Center.z) - num, 0f);
				return num2 * num2 + num3 * num3 <= TerrainOuterRadiusSquared;
			}

			private bool ContainsRadius(float x, float z, float radiusSquared)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				float num = x - Center.x;
				float num2 = z - Center.z;
				return num * num + num2 * num2 <= radiusSquared;
			}

			public Vector2 MapToSource(float x, float z)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				return new Vector2(TerrainSourceCenter.x + (x - Center.x), TerrainSourceCenter.z + (z - Center.z));
			}

			public TerrainSample CreateTerrainSample(float x, float z)
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				float num = DistanceFromCenter(x, z);
				float sourceWeight = 1f;
				if (TerrainEdgeFalloffWidth > 0f && num > Radius)
				{
					float num2 = Mathf.Clamp01((num - Radius) / TerrainEdgeFalloffWidth);
					sourceWeight = 1f - Mathf.SmoothStep(0f, 1f, num2);
				}
				return new TerrainSample(MapToSource(x, z), sourceWeight, TerrainEdgeFloorHeight);
			}

			public bool ContainsVisual(float x, float z)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				if (UseTerrainSource)
				{
					return ContainsTerrain(x, z);
				}
				float num = x - Center.x;
				float num2 = z - Center.z;
				return num * num + num2 * num2 <= VisualRadiusSquared;
			}

			public bool HasSameState(OverrideZone other)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: 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_005d: Unknown result type (might be due to invalid IL or missing references)
				if (Approximately(Center, other.Center) && Mathf.Approximately(Radius, other.Radius) && Biome == other.Biome && SuppressSpawns == other.SuppressSpawns && UseTerrainSource == other.UseTerrainSource && Approximately(TerrainSourceCenter, other.TerrainSourceCenter) && Mathf.Approximately(TerrainPatchHalfSize, other.TerrainPatchHalfSize) && Mathf.Approximately(TerrainEdgeFalloffWidth, other.TerrainEdgeFalloffWidth) && Mathf.Approximately(TerrainEdgeFloorHeight, other.TerrainEdgeFloorHeight))
				{
					return SuppressVegetationDrops == other.SuppressVegetationDrops;
				}
				return false;
			}

			private float DistanceFromCenter(float x, float z)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				float num = x - Center.x;
				float num2 = z - Center.z;
				return Mathf.Sqrt(num * num + num2 * num2);
			}

			private static bool Approximately(Vector3 left, Vector3 right)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				if (Mathf.Approximately(left.x, right.x) && Mathf.Approximately(left.y, right.y))
				{
					return Mathf.Approximately(left.z, right.z);
				}
				return false;
			}
		}

		private readonly struct TerrainSample
		{
			public Vector2 Source { get; }

			public float SourceWeight { get; }

			public float FloorHeight { get; }

			public TerrainSample(Vector2 source, float sourceWeight, float floorHeight)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Source = source;
				SourceWeight = sourceWeight;
				FloorHeight = floorHeight;
			}

			public float ApplyHeight(float sourceHeight)
			{
				return Mathf.Lerp(FloorHeight, sourceHeight, SourceWeight);
			}
		}

		[HarmonyPatch(typeof(WorldGenerator), "GetBiome", new Type[]
		{
			typeof(float),
			typeof(float),
			typeof(float),
			typeof(bool)
		})]
		private static class WorldGeneratorGetBiomePatch
		{
			private static bool Prefix(float wx, float wy, ref Biome __result)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Expected I4, but got Unknown
				if (!TryGetBiome(wx, wy, out var biome))
				{
					return true;
				}
				__result = (Biome)(int)biome;
				return false;
			}
		}

		[HarmonyPatch(typeof(Heightmap), "GetBiome", new Type[]
		{
			typeof(Vector3),
			typeof(float),
			typeof(bool)
		})]
		private static class HeightmapGetBiomePatch
		{
			private static bool Prefix(Vector3 point, ref Biome __result)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Expected I4, but got Unknown
				if (!TryGetBiome(point.x, point.z, out var biome))
				{
					return true;
				}
				__result = (Biome)(int)biome;
				return false;
			}
		}

		[HarmonyPatch(typeof(WorldGenerator), "GetBiomeHeight")]
		private static class WorldGeneratorGetBiomeHeightPatch
		{
			private static bool Prefix(ref Biome biome, float wx, float wy, ref Color mask, bool preGeneration, ref float __result)
			{
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: 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_0044: Expected I4, but got Unknown
				//IL_0051: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				if (!TryGetTerrainSample(wx, wy, out var sample) || WorldGenerator.instance == null)
				{
					return true;
				}
				_samplingSourceTerrain = true;
				try
				{
					biome = (Biome)(int)WorldGenerator.instance.GetBiome(sample.Source.x, sample.Source.y, 0.02f, false);
					__result = sample.ApplyHeight(WorldGenerator.instance.GetBiomeHeight(biome, sample.Source.x, sample.Source.y, ref mask, preGeneration));
				}
				finally
				{
					_samplingSourceTerrain = false;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(WorldGenerator), "GetHeight", new Type[]
		{
			typeof(float),
			typeof(float)
		})]
		private static class WorldGeneratorGetHeightPatch
		{
			private static bool Prefix(float wx, float wy, ref float __result)
			{
				//IL_0024: 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)
				if (!TryGetTerrainSample(wx, wy, out var sample) || WorldGenerator.instance == null)
				{
					return true;
				}
				_samplingSourceTerrain = true;
				try
				{
					__result = sample.ApplyHeight(WorldGenerator.instance.GetHeight(sample.Source.x, sample.Source.y));
				}
				finally
				{
					_samplingSourceTerrain = false;
				}
				return false;
			}
		}

		[HarmonyPatch]
		private static class WorldGeneratorGetHeightWithMaskPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(WorldGenerator), "GetHeight", new Type[3]
				{
					typeof(float),
					typeof(float),
					typeof(Color).MakeByRefType()
				}, (Type[])null);
			}

			private static bool Prefix(float wx, float wy, ref Color mask, ref float __result)
			{
				//IL_0024: 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)
				if (!TryGetTerrainSample(wx, wy, out var sample) || WorldGenerator.instance == null)
				{
					return true;
				}
				_samplingSourceTerrain = true;
				try
				{
					__result = sample.ApplyHeight(WorldGenerator.instance.GetHeight(sample.Source.x, sample.Source.y, ref mask));
				}
				finally
				{
					_samplingSourceTerrain = false;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Heightmap), "GetBiomeColor", new Type[]
		{
			typeof(float),
			typeof(float)
		})]
		private static class HeightmapGetBiomeColorPatch
		{
			private static bool Prefix(Heightmap __instance, float ix, float iy, ref Color __result)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: 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_0067: 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)
				//IL_0071: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)__instance == (Object)null)
				{
					return true;
				}
				float num = (float)__instance.m_width * __instance.m_scale * 0.5f;
				Vector3 position = ((Component)__instance).transform.position;
				float x = position.x + (ix - 0.5f) * num * 2f;
				float z = position.z + (iy - 0.5f) * num * 2f;
				if (!TryGetVisualBiome(x, z, out var biome))
				{
					return true;
				}
				__result = Color32.op_Implicit(Heightmap.GetBiomeColor(biome));
				return false;
			}
		}

		[HarmonyPatch(typeof(WorldGenerator), "IsAshlands")]
		private static class WorldGeneratorIsAshlandsPatch
		{
			private static bool Prefix(float x, float y, ref bool __result)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Invalid comparison between Unknown and I4
				if (!TryGetBiome(x, y, out var biome))
				{
					return true;
				}
				__result = (int)biome == 32;
				return false;
			}
		}

		[HarmonyPatch(typeof(WorldGenerator), "IsDeepnorth")]
		private static class WorldGeneratorIsDeepnorthPatch
		{
			private static bool Prefix(float x, float y, ref bool __result)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Invalid comparison between Unknown and I4
				if (!TryGetBiome(x, y, out var biome))
				{
					return true;
				}
				__result = (int)biome == 64;
				return false;
			}
		}

		[HarmonyPatch(typeof(SpawnSystem), "IsSpawnPointGood")]
		private static class SpawnSystemIsSpawnPointGoodPatch
		{
			private static bool Prefix(ref Vector3 spawnPoint, ref bool __result)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				if (!ContainsSpawnBlockedZone(spawnPoint))
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		private const int ProtocolVersion = 5;

		private const string ZdoVegetationMarker = "valheimCreative.vegetation";

		private const string ZdoVegetationSlotId = "valheimCreative.vegetationSlot";

		private const float VisualBiomeMargin = 16f;

		private static readonly Dictionary<string, OverrideZone> Zones = new Dictionary<string, OverrideZone>();

		private static readonly FieldInfo? HeightmapBuildDataField = AccessTools.Field(typeof(Heightmap), "m_buildData");

		[ThreadStatic]
		private static bool _samplingSourceTerrain;

		public static void OnOverride(long sender, ZPackage pkg)
		{
			//IL_0079: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				int num = pkg.ReadInt();
				if (num < 1 || num > 5)
				{
					PraetorisClientPlugin.Log.LogWarning((object)$"Ignoring creative biome override version {num}; expected 1-{5}.");
					return;
				}
				int num2 = pkg.ReadInt();
				if (num2 <= 0)
				{
					ClearAll();
					return;
				}
				for (int i = 0; i < num2; i++)
				{
					string text = pkg.ReadString();
					bool num3 = pkg.ReadBool();
					Vector3 center = pkg.ReadVector3();
					float num4 = pkg.ReadSingle();
					Biome val = (Biome)pkg.ReadInt();
					bool suppressSpawns = ((num2 == 1 && pkg.GetPos() < pkg.Size()) ? pkg.ReadBool() : (!text.StartsWith("siege_", StringComparison.OrdinalIgnoreCase)));
					bool useTerrainSource = false;
					Vector3 terrainSourceCenter = Vector3.zero;
					if (num >= 2 && pkg.GetPos() < pkg.Size())
					{
						useTerrainSource = pkg.ReadBool();
						terrainSourceCenter = pkg.ReadVector3();
					}
					float terrainPatchHalfSize = CalculateTerrainPatchHalfSize(num4);
					if (num >= 3 && pkg.GetPos() < pkg.Size())
					{
						float num5 = pkg.ReadSingle();
						if (num5 > 0f)
						{
							terrainPatchHalfSize = num5;
						}
					}
					float terrainEdgeFalloffWidth = 0f;
					float terrainEdgeFloorHeight = 0f;
					if (num >= 4 && pkg.GetPos() < pkg.Size())
					{
						terrainEdgeFalloffWidth = Mathf.Max(0f, pkg.ReadSingle());
					}
					if (num >= 4 && pkg.GetPos() < pkg.Size())
					{
						terrainEdgeFloorHeight = pkg.ReadSingle();
					}
					bool suppressVegetationDrops = !text.StartsWith("siege_", StringComparison.OrdinalIgnoreCase);
					if (num >= 5 && pkg.GetPos() < pkg.Size())
					{
						suppressVegetationDrops = pkg.ReadBool();
					}
					if (!num3 || (int)val == 0 || num4 <= 0f)
					{
						Remove(text);
					}
					else
					{
						Set(text, center, num4, val, suppressSpawns, useTerrainSource, terrainSourceCenter, terrainPatchHalfSize, terrainEdgeFalloffWidth, terrainEdgeFloorHeight, suppressVegetationDrops);
					}
				}
			}
			catch (Exception arg)
			{
				PraetorisClientPlugin.Log.LogWarning((object)$"Failed to apply creative biome override: {arg}");
			}
		}

		public static bool TryGetBiome(float x, float z, out Biome biome)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected I4, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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: Expected I4, but got Unknown
			if (_samplingSourceTerrain)
			{
				biome = (Biome)0;
				return false;
			}
			foreach (OverrideZone value in Zones.Values)
			{
				if (!value.ContainsTerrain(x, z))
				{
					continue;
				}
				if (value.UseTerrainSource && WorldGenerator.instance != null)
				{
					Vector2 val = value.MapToSource(x, z);
					_samplingSourceTerrain = true;
					try
					{
						biome = (Biome)(int)WorldGenerator.instance.GetBiome(val.x, val.y, 0.02f, false);
					}
					finally
					{
						_samplingSourceTerrain = false;
					}
					return true;
				}
				if (!value.UseTerrainSource)
				{
					biome = (Biome)(int)value.Biome;
					return true;
				}
			}
			biome = (Biome)0;
			return false;
		}

		private static bool TryGetVisualBiome(float x, float z, out Biome biome)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected I4, but got Unknown
			foreach (OverrideZone value in Zones.Values)
			{
				if (value.ContainsVisual(x, z))
				{
					biome = (Biome)(int)value.Biome;
					return true;
				}
			}
			biome = (Biome)0;
			return false;
		}

		private static bool TryGetTerrainSample(float x, float z, out TerrainSample sample)
		{
			sample = default(TerrainSample);
			if (_samplingSourceTerrain)
			{
				return false;
			}
			foreach (OverrideZone value in Zones.Values)
			{
				if (value.UseTerrainSource && value.ContainsTerrain(x, z))
				{
					sample = value.CreateTerrainSample(x, z);
					return true;
				}
			}
			return false;
		}

		public static bool ContainsSpawnBlockedZone(Vector3 point)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			foreach (OverrideZone value in Zones.Values)
			{
				if (value.SuppressSpawns && value.Contains(point.x, point.z))
				{
					return true;
				}
			}
			return false;
		}

		public static bool ShouldSuppressVegetationDrops(Component component)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			return ShouldSuppressVegetationDrops(component, component.transform.position);
		}

		public static bool ShouldSuppressVegetationDrops(Component component, Vector3 point)
		{
			//IL_0034: 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)
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			foreach (OverrideZone value in Zones.Values)
			{
				if (value.SuppressVegetationDrops && value.Contains(point.x, point.z))
				{
					ZNetView val = component.GetComponent<ZNetView>() ?? component.GetComponentInParent<ZNetView>();
					ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null);
					if (val2 != null && IsMarkedCreativeVegetation(val2))
					{
						return true;
					}
					return (Object)(object)component.GetComponent<TreeBase>() != (Object)null || (Object)(object)component.GetComponent<TreeLog>() != (Object)null || (Object)(object)component.GetComponent<Pickable>() != (Object)null || (Object)(object)component.GetComponent<MineRock>() != (Object)null || (Object)(object)component.GetComponent<MineRock5>() != (Object)null || (Object)(object)component.GetComponent<DropOnDestroyed>() != (Object)null;
				}
			}
			return false;
		}

		public static bool ContainsTerrainOverride(Vector3 point)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			foreach (OverrideZone value in Zones.Values)
			{
				if (value.UseTerrainSource && value.ContainsTerrain(point.x, point.z))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsMarkedCreativeVegetation(ZDO zdo)
		{
			if (!zdo.GetBool("valheimCreative.vegetation", false))
			{
				return !string.IsNullOrWhiteSpace(zdo.GetString("valheimCreative.vegetationSlot", ""));
			}
			return true;
		}

		private static void Set(string zoneId, Vector3 center, float radius, Biome biome, bool suppressSpawns, bool useTerrainSource, Vector3 terrainSourceCenter, float terrainPatchHalfSize, float terrainEdgeFalloffWidth, float terrainEdgeFloorHeight, bool suppressVegetationDrops)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			zoneId = NormalizeZoneId(zoneId);
			OverrideZone value;
			bool flag = Zones.TryGetValue(zoneId, out value);
			OverrideZone overrideZone = new OverrideZone(center, radius, biome, suppressSpawns, useTerrainSource, terrainSourceCenter, terrainPatchHalfSize, terrainEdgeFalloffWidth, terrainEdgeFloorHeight, suppressVegetationDrops);
			if (!flag || !value.HasSameState(overrideZone))
			{
				Zones[zoneId] = overrideZone;
				if (flag)
				{
					RefreshTerrain(value);
				}
				RefreshTerrain(overrideZone);
				string text = (useTerrainSource ? $", terrainSource={terrainSourceCenter.x:0.##},{terrainSourceCenter.z:0.##}" : string.Empty);
				PraetorisClientPlugin.Log.LogInfo((object)$"Creative biome override {zoneId}: {biome} at {center.x:0.##},{center.z:0.##} radius {radius:0.##}, suppressSpawns={suppressSpawns}, suppressVegetationDrops={suppressVegetationDrops}{text}.");
			}
		}

		private static void Remove(string zoneId)
		{
			zoneId = NormalizeZoneId(zoneId);
			if (Zones.TryGetValue(zoneId, out OverrideZone value))
			{
				Zones.Remove(zoneId);
				RefreshTerrain(value);
				PraetorisClientPlugin.Log.LogInfo((object)("Removed creative biome override " + zoneId + "."));
			}
		}

		private static void ClearAll()
		{
			if (Zones.Count == 0)
			{
				return;
			}
			List<OverrideZone> list = new List<OverrideZone>(Zones.Values);
			Zones.Clear();
			foreach (OverrideZone item in list)
			{
				RefreshTerrain(item);
			}
			PraetorisClientPlugin.Log.LogInfo((object)"Cleared creative biome overrides.");
		}

		private static void RefreshTerrain(OverrideZone zone)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			Heightmap[] array = Object.FindObjectsByType<Heightmap>((FindObjectsSortMode)0);
			foreach (Heightmap val in array)
			{
				if (!((Object)(object)val == (Object)null) && Intersects(val, zone))
				{
					HeightmapBuildDataField?.SetValue(val, null);
					val.Poke(false);
				}
			}
			if ((Object)(object)ClutterSystem.instance != (Object)null)
			{
				ClutterSystem.instance.ResetGrass(zone.Center, zone.GrassResetRadius);
			}
		}

		private static bool Intersects(Heightmap heightmap, OverrideZone zone)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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_0055: 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)
			Vector3 position = ((Component)heightmap).transform.position;
			if (zone.UseTerrainSource)
			{
				return zone.IntersectsTerrain(heightmap);
			}
			float num = (float)heightmap.m_width * heightmap.m_scale * 0.5f;
			float num2 = Math.Max(Math.Abs(position.x - zone.Center.x) - num, 0f);
			float num3 = Math.Max(Math.Abs(position.z - zone.Center.z) - num, 0f);
			return num2 * num2 + num3 * num3 <= zone.RadiusSquared;
		}

		private static float CalculateTerrainPatchHalfSize(float radius)
		{
			float num = Mathf.Max(1f, radius);
			float num2 = Mathf.Ceil(Mathf.Max(0f, num - 32f) / 64f);
			return 32f + num2 * 64f;
		}

		private static string NormalizeZoneId(string zoneId)
		{
			if (!string.IsNullOrWhiteSpace(zoneId))
			{
				return zoneId.Trim();
			}
			return "creative";
		}
	}
	internal static class CreativeCommandZoneState
	{
		private const int ProtocolVersion = 1;

		private const string DeniedMessage = "Creative commands can only be used inside your creative zone.";

		private static bool _active;

		private static Vector3 _center;

		private static float _radius;

		private static long _ownerPlayerId;

		private static long _playerId;

		private static string _slotId = string.Empty;

		private static bool _guardEnabled;

		private static string _protectedCommandPrefixes = string.Empty;

		internal static void OnState(long sender, ZPackage package)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: 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)
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				int num = package.ReadInt();
				if (num != 1)
				{
					PraetorisClientPlugin.Log.LogWarning((object)$"Ignoring creative command zone state protocol {num}; expected {1}.");
					return;
				}
				bool flag = package.ReadBool();
				Vector3 val = package.ReadVector3();
				float num2 = package.ReadSingle();
				long num3 = package.ReadLong();
				long num4 = package.ReadLong();
				string text = package.ReadString();
				bool flag2 = package.GetPos() < package.Size() && package.ReadBool();
				string text2 = ((package.GetPos() < package.Size()) ? package.ReadString() : string.Empty);
				if (!flag || !(num2 > 0f) || num4 == 0L || !_active || !Approximately(_center, val) || !Mathf.Approximately(_radius, num2) || _ownerPlayerId != num3 || _playerId != num4 || !string.Equals(_slotId, text ?? string.Empty, StringComparison.Ordinal) || _guardEnabled != flag2 || !string.Equals(_protectedCommandPrefixes, text2 ?? string.Empty, StringComparison.Ordinal))
				{
					_guardEnabled = flag2;
					_protectedCommandPrefixes = text2 ?? string.Empty;
					if (!flag || num2 <= 0f || num4 == 0L)
					{
						Clear();
						return;
					}
					_active = true;
					_center = val;
					_radius = num2;
					_ownerPlayerId = num3;
					_playerId = num4;
					_slotId = text ?? string.Empty;
					PraetorisClientPlugin.Log.LogInfo((object)$"Creative command zone active: {_slotId} at {_center.x:0.##},{_center.z:0.##} radius {_radius:0.##}.");
				}
			}
			catch (Exception arg)
			{
				Clear();
				PraetorisClientPlugin.Log.LogWarning((object)$"Failed to apply creative command zone state: {arg}");
			}
		}

		internal static void Clear()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_active = false;
			_center = Vector3.zero;
			_radius = 0f;
			_ownerPlayerId = 0L;
			_playerId = 0L;
			_slotId = string.Empty;
		}

		internal static bool CanRunCommand(ConsoleEventArgs args)
		{
			if (args == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || !IsProtectedCommand(args.FullLine))
			{
				return true;
			}
			if (IsLocalPlayerInsideActiveZone())
			{
				return true;
			}
			object obj = ((object)args.Context) ?? ((object)Console.instance);
			if (obj != null)
			{
				((Terminal)obj).AddString("Creative commands can only be used inside your creative zone.");
			}
			return false;
		}

		private static bool IsProtectedCommand(string rawCommand)
		{
			if (!_guardEnabled)
			{
				return false;
			}
			string text = NormalizeCommand(rawCommand);
			if (text.Length == 0)
			{
				return false;
			}
			foreach (string protectedCommandPrefix in GetProtectedCommandPrefixes())
			{
				if (text.StartsWith(protectedCommandPrefix, StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsLocalPlayerInsideActiveZone()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!_active || (Object)(object)localPlayer == (Object)null || _radius <= 0f)
			{
				return false;
			}
			if (_playerId != 0L && localPlayer.GetPlayerID() != _playerId)
			{
				return false;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			float num = position.x - _center.x;
			float num2 = position.z - _center.z;
			return num * num + num2 * num2 <= _radius * _radius;
		}

		private static IEnumerable<string> GetProtectedCommandPrefixes()
		{
			return from value in _protectedCommandPrefixes.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select value.Trim().ToLowerInvariant() into value
				where value.Length > 0
				select value;
		}

		private static string NormalizeCommand(string rawCommand)
		{
			if (string.IsNullOrWhiteSpace(rawCommand))
			{
				return string.Empty;
			}
			string[] array = rawCommand.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length != 0)
			{
				return array[0].ToLowerInvariant();
			}
			return string.Empty;
		}

		private static bool Approximately(Vector3 left, Vector3 right)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (Mathf.Approximately(left.x, right.x) && Mathf.Approximately(left.y, right.y))
			{
				return Mathf.Approximately(left.z, right.z);
			}
			return false;
		}
	}
	internal static class CreativeInventoryRpc
	{
		private sealed class CreativeInventorySnapshot
		{
			public bool Available { get; set; }

			public string Error { get; set; } = string.Empty;

			public long PlayerId { get; set; }

			public string PlayerName { get; set; } = string.Empty;

			public int PlayerInventoryCount { get; set; }

			public bool ExtraSlotsLoaded { get; set; }

			public bool ExtraSlotsAvailable { get; set; }

			public int ExtraSlotsCount { get; set; }

			public int TotalUniqueCount { get; set; }

			public List<CreativeInventoryItem> Items { get; } = new List<CreativeInventoryItem>();

			public static CreativeInventorySnapshot Unavailable(string error)
			{
				return new CreativeInventorySnapshot
				{
					Available = false,
					Error = error
				};
			}
		}

		private sealed class CreativeInventoryItem
		{
			public string Source { get; set; } = string.Empty;

			public string PrefabName { get; set; } = string.Empty;

			public string SharedName { get; set; } = string.Empty;

			public int Stack { get; set; }

			public int Quality { get; set; }

			public bool Equipped { get; set; }

			public int GridX { get; set; }

			public int GridY { get; set; }

			public static CreativeInventoryItem FromItem(string source, ItemData item)
			{
				return new CreativeInventoryItem
				{
					Source = source,
					PrefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : string.Empty),
					SharedName = (item.m_shared?.m_name ?? string.Empty),
					Stack = item.m_stack,
					Quality = item.m_quality,
					Equipped = item.m_equipped,
					GridX = item.m_gridPos.x,
					GridY = item.m_gridPos.y
				};
			}
		}

		private sealed class ExtraSlotsReadResult
		{
			public bool ModLoaded { get; private set; }

			public bool Available { get; private set; }

			public string Error { get; private set; } = string.Empty;

			public List<ItemData> Items { get; } = new List<ItemData>();

			public static ExtraSlotsReadResult NotLoaded()
			{
				return new ExtraSlotsReadResult
				{
					ModLoaded = false,
					Available = true
				};
			}

			public static ExtraSlotsReadResult Loaded(IEnumerable<ItemData> items)
			{
				ExtraSlotsReadResult extraSlotsReadResult = new ExtraSlotsReadResult();
				extraSlotsReadResult.ModLoaded = true;
				extraSlotsReadResult.Available = true;
				extraSlotsReadResult.Items.AddRange(items);
				return extraSlotsReadResult;
			}

			public static ExtraSlotsReadResult Failed(string error)
			{
				return new ExtraSlotsReadResult
				{
					ModLoaded = true,
					Available = false,
					Error = error
				};
			}
		}

		private const int ProtocolVersion = 1;

		private const string ExtraSlotsApiTypeName = "ExtraSlots.API";

		public static void OnRequest(long sender, ZPackage pkg)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			string text = string.Empty;
			ZDOID val = ZDOID.None;
			bool flag = true;
			try
			{
				int num = pkg.ReadInt();
				if (num != 1)
				{
					SendUnavailable(sender, text, val, $"Unsupported creative inventory protocol version {num}.");
					return;
				}
				text = pkg.ReadString();
				val = pkg.ReadZDOID();
				flag = pkg.ReadBool();
				CreativeInventorySnapshot snapshot = BuildSnapshot(val, flag);
				SendResponse(sender, text, val, snapshot);
			}
			catch (Exception arg)
			{
				PraetorisClientPlugin.Log.LogWarning((object)$"Failed to answer creative inventory request {text}: {arg}");
				SendUnavailable(sender, text, val, "Failed to read client inventory.");
			}
		}

		private static CreativeInventorySnapshot BuildSnapshot(ZDOID expectedCharacterId, bool includeItems)
		{
			//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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)((Character)localPlayer).m_nview == (Object)null || !((Character)localPlayer).m_nview.IsValid())
			{
				return CreativeInventorySnapshot.Unavailable("Local player is not available.");
			}
			ZDO zDO = ((Character)localPlayer).m_nview.GetZDO();
			if (zDO == null)
			{
				return CreativeInventorySnapshot.Unavailable("Local player character is not available.");
			}
			if (!((ZDOID)(ref expectedCharacterId)).IsNone() && zDO.m_uid != expectedCharacterId)
			{
				return CreativeInventorySnapshot.Unavailable("Local player character did not match the requested character.");
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (inventory == null)
			{
				return CreativeInventorySnapshot.Unavailable("Local player inventory is not available.");
			}
			List<ItemData> list = (from item in inventory.GetAllItems()
				where item != null
				select item).ToList();
			ExtraSlotsReadResult extraSlotsReadResult = ReadExtraSlotsItems();
			if (!extraSlotsReadResult.Available && extraSlotsReadResult.ModLoaded)
			{
				return CreativeInventorySnapshot.Unavailable(extraSlotsReadResult.Error);
			}
			List<ItemData> list2 = new List<ItemData>();
			AddUnique(list2, list);
			AddUnique(list2, extraSlotsReadResult.Items);
			CreativeInventorySnapshot creativeInventorySnapshot = new CreativeInventorySnapshot
			{
				Available = true,
				Error = string.Empty,
				PlayerId = localPlayer.GetPlayerID(),
				PlayerName = localPlayer.GetPlayerName(),
				PlayerInventoryCount = list.Count,
				ExtraSlotsAvailable = extraSlotsReadResult.Available,
				ExtraSlotsLoaded = extraSlotsReadResult.ModLoaded,
				ExtraSlotsCount = extraSlotsReadResult.Items.Count,
				TotalUniqueCount = list2.Count
			};
			if (includeItems)
			{
				creativeInventorySnapshot.Items.AddRange(list.Select((ItemData item) => CreativeInventoryItem.FromItem("player", item)));
				creativeInventorySnapshot.Items.AddRange(extraSlotsReadResult.Items.Select((ItemData item) => CreativeInventoryItem.FromItem("extraSlots", item)));
			}
			return creativeInventorySnapshot;
		}

		private static ExtraSlotsReadResult ReadExtraSlotsItems()
		{
			Type type = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
				select assembly.GetType("ExtraSlots.API", throwOnError: false)).FirstOrDefault((Type type2) => type2 != null);
			if (type == null)
			{
				return ExtraSlotsReadResult.NotLoaded();
			}
			MethodInfo method = type.GetMethod("GetAllExtraSlotsItems", BindingFlags.Static | BindingFlags.Public);
			if (method == null)
			{
				return ExtraSlotsReadResult.Failed("ExtraSlots API did not expose GetAllExtraSlotsItems.");
			}
			try
			{
				if (!(method.Invoke(null, Array.Empty<object>()) is IEnumerable enumerable))
				{
					return ExtraSlotsReadResult.Failed("ExtraSlots API returned no item list.");
				}
				List<ItemData> list = new List<ItemData>();
				foreach (object item in enumerable)
				{
					ItemData val = (ItemData)((item is ItemData) ? item : null);
					if (val != null)
					{
						list.Add(val);
					}
				}
				return ExtraSlotsReadResult.Loaded(list);
			}
			catch (Exception ex)
			{
				return ExtraSlotsReadResult.Failed("ExtraSlots API read failed: " + ex.Message);
			}
		}

		private static void AddUnique(List<ItemData> target, IEnumerable<ItemData> items)
		{
			foreach (ItemData item in items)
			{
				if (!target.Contains(item))
				{
					target.Add(item);
				}
			}
		}

		private static void SendUnavailable(long target, string requestId, ZDOID characterId, string error)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			SendResponse(target, requestId, characterId, CreativeInventorySnapshot.Unavailable(error));
		}

		private static void SendResponse(long target, string requestId, ZDOID characterId, CreativeInventorySnapshot snapshot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(requestId);
			val.Write(characterId);
			val.Write(snapshot.Available);
			val.Write(snapshot.Error);
			val.Write(snapshot.PlayerId);
			val.Write(snapshot.PlayerName);
			val.Write(snapshot.PlayerInventoryCount);
			val.Write(snapshot.ExtraSlotsLoaded);
			val.Write(snapshot.ExtraSlotsAvailable);
			val.Write(snapshot.ExtraSlotsCount);
			val.Write(snapshot.TotalUniqueCount);
			val.Write(snapshot.Items.Count);
			foreach (CreativeInventoryItem item in snapshot.Items)
			{
				val.Write(item.Source);
				val.Write(item.PrefabName);
				val.Write(item.SharedName);
				val.Write(item.Stack);
				val.Write(item.Quality);
				val.Write(item.Equipped);
				val.Write(item.GridX);
				val.Write(item.GridY);
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "DiscordTools_CreativeInventoryResponse", new object[1] { val });
		}
	}
	internal static class CreativeVegetationDropPatches
	{
		[HarmonyPatch]
		private static class TreeBaseRpcDamagePatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(TreeBase), "RPC_Damage", (Type[])null, (Type[])null);
			}

			private static void Prefix(TreeBase __instance, ref DropPatchState? __state)
			{
				__state = ReplaceDropsIfSuppressed((Component)(object)__instance, __instance.m_dropWhenDestroyed);
				if (__state != null)
				{
					__instance.m_dropWhenDestroyed = EmptyDropTable;
				}
			}

			private static void Postfix(TreeBase __instance, DropPatchState? __state)
			{
				if (__state != null)
				{
					__instance.m_dropWhenDestroyed = __state.DropTable;
				}
			}
		}

		[HarmonyPatch]
		private static class TreeBaseSpawnLogPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(TreeBase), "SpawnLog", (Type[])null, (Type[])null);
			}

			private static bool Prefix(TreeBase __instance)
			{
				return !CreativeBiomeOverride.ShouldSuppressVegetationDrops((Component)(object)__instance);
			}
		}

		[HarmonyPatch]
		private static class TreeLogDestroyPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(TreeLog), "Destroy", (Type[])null, (Type[])null);
			}

			private static void Prefix(TreeLog __instance, ref TreeLogPatchState? __state)
			{
				if (!CreativeBiomeOverride.ShouldSuppressVegetationDrops((Component)(object)__instance))
				{
					__state = null;
					return;
				}
				__state = new TreeLogPatchState(__instance.m_dropWhenDestroyed, __instance.m_subLogPrefab);
				__instance.m_dropWhenDestroyed = EmptyDropTable;
				__instance.m_subLogPrefab = null;
			}

			private static void Postfix(TreeLog __instance, TreeLogPatchState? __state)
			{
				if (__state != null)
				{
					__instance.m_dropWhenDestroyed = __state.DropTable;
					__instance.m_subLogPrefab = __state.SubLogPrefab;
				}
			}
		}

		[HarmonyPatch]
		private static class PickableRpcPickPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(Pickable), "RPC_Pick", (Type[])null, (Type[])null);
			}

			private static bool Prefix(Pickable __instance)
			{
				if (!CreativeBiomeOverride.ShouldSuppressVegetationDrops((Component)(object)__instance))
				{
					return true;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsOwner())
				{
					return true;
				}
				__instance.SetPicked(true);
				return false;
			}
		}

		[HarmonyPatch]
		private static class DropOnDestroyedPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(DropOnDestroyed), "OnDestroyed", (Type[])null, (Type[])null);
			}

			private static bool Prefix(DropOnDestroyed __instance)
			{
				return !CreativeBiomeOverride.ShouldSuppressVegetationDrops((Component)(object)__instance);
			}
		}

		[HarmonyPatch]
		private static class MineRockRpcHitPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(MineRock), "RPC_Hit", (Type[])null, (Type[])null);
			}

			private static void Prefix(MineRock __instance, HitData hit, ref DropPatchState? __state)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				__state = ReplaceDropsIfSuppressed((Component)(object)__instance, __instance.m_dropItems, hit.m_point);
				if (__state != null)
				{
					__instance.m_dropItems = EmptyDropTable;
				}
			}

			private static void Postfix(MineRock __instance, DropPatchState? __state)
			{
				if (__state != null)
				{
					__instance.m_dropItems = __state.DropTable;
				}
			}
		}

		[HarmonyPatch]
		private static class MineRock5DamageAreaPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(MineRock5), "DamageArea", (Type[])null, (Type[])null);
			}

			private static void Prefix(MineRock5 __instance, HitData hit, ref DropPatchState? __state)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				__state = ReplaceDropsIfSuppressed((Component)(object)__instance, __instance.m_dropItems, hit.m_point);
				if (__state != null)
				{
					__instance.m_dropItems = EmptyDropTable;
				}
			}

			private static void Postfix(MineRock5 __instance, DropPatchState? __state)
			{
				if (__state != null)
				{
					__instance.m_dropItems = __state.DropTable;
				}
			}
		}

		private sealed class DropPatchState
		{
			internal DropTable DropTable { get; }

			internal DropPatchState(DropTable dropTable)
			{
				DropTable = dropTable;
			}
		}

		private sealed class TreeLogPatchState
		{
			internal DropTable DropTable { get; }

			internal GameObject SubLogPrefab { get; }

			internal TreeLogPatchState(DropTable dropTable, GameObject subLogPrefab)
			{
				DropTable = dropTable;
				SubLogPrefab = subLogPrefab;
			}
		}

		private static readonly DropTable EmptyDropTable = new DropTable
		{
			m_drops = new List<DropData>(),
			m_dropMin = 0,
			m_dropMax = 0,
			m_dropChance = 0f
		};

		private static DropPatchState? ReplaceDropsIfSuppressed(Component component, DropTable dropTable)
		{
			if (!CreativeBiomeOverride.ShouldSuppressVegetationDrops(component))
			{
				return null;
			}
			return new DropPatchState(dropTable);
		}

		private static DropPatchState? ReplaceDropsIfSuppressed(Component component, DropTable dropTable, Vector3 point)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (!CreativeBiomeOverride.ShouldSuppressVegetationDrops(component, point))
			{
				return null;
			}
			return new DropPatchState(dropTable);
		}
	}
	internal static class LinkCommandHandler
	{
		private static readonly Regex CodePattern = new Regex("^[A-Za-z0-9_-]{4,64}$", RegexOptions.Compiled);

		public static bool TryHandle(Chat chat)
		{
			string text = (((Object)(object)((Terminal)chat).m_input != (Object)null) ? ((TMP_InputField)((Terminal)chat).m_input).text : "");
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = PraetorisClientPlugin.LinkCommand.Value.Trim();
			if (string.IsNullOrWhiteSpace(text2))
			{
				text2 = "!link";
			}
			string text3 = text.Trim();
			if (!text3.Equals(text2, StringComparison.OrdinalIgnoreCase) && !text3.StartsWith(text2 + " ", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string text4 = ((text3.Length > text2.Length) ? text3.Substring(text2.Length).Trim() : "");
			if (!CodePattern.IsMatch(text4))
			{
				((Terminal)chat).AddString("Usage: " + text2 + " CODE");
				ClearInput(chat);
				return true;
			}
			if (!LinkRpc.TrySendRequest(text4, out string message))
			{
				((Terminal)chat).AddString(message);
				ClearInput(chat);
				return true;
			}
			((Terminal)chat).AddString("Sending Discord link code to the server.");
			ClearInput(chat);
			return true;
		}

		private static void ClearInput(Chat chat)
		{
			if (!((Object)(object)((Terminal)chat).m_input == (Object)null))
			{
				((TMP_InputField)((Terminal)chat).m_input).text = "";
				((Component)((Terminal)chat).m_input).gameObject.SetActive(false);
			}
		}
	}
	internal static class LinkRpc
	{
		public static bool TrySendRequest(string code, out string message)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			message = "";
			if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null || ZNet.instance.IsServer() || (int)ZNet.GetConnectionStatus() != 2)
			{
				message = "You must be connected to a server before linking Discord.";
				return false;
			}
			string text = Guid.NewGuid().ToString("N");
			ZPackage val = new ZPackage();
			val.Write(text);
			val.Write(code);
			ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "DiscordTools_LinkRequest", new object[1] { val });
			return true;
		}

		public static void OnRequest(long sender, ZPackage pkg)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			string requestId = pkg.ReadString();
			string code = pkg.ReadString();
			ZNetPeer val = PlayerResolver.FindPeerBySender(sender);
			if (val == null)
			{
				SendResult(sender, requestId, success: false, "The server could not identify your Valheim connection.");
				return;
			}
			PraetorisClientPlugin instance = PraetorisClientPlugin.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				SendResult(sender, requestId, success: false, "PraetorisClient is not ready on the server.");
				return;
			}
			LinkRequest link = new LinkRequest
			{
				Sender = sender,
				RequestId = requestId,
				Code = code,
				PlayerId = PlayerResolver.StablePlayerId(val),
				PlayerName = (val.m_playerName ?? ""),
				Endpoint = PlayerResolver.SafeEndPoint(val),
				PlatformDisplayName = PlayerResolver.PlatformDisplayName(val),
				ReceivedAtUtc = DateTime.UtcNow
			};
			PraetorisClientPlugin.Log.LogInfo((object)("Received Discord link code from " + PlayerResolver.DescribePeer(val) + "."));
			((MonoBehaviour)instance).StartCoroutine(BotApiClient.PostLinkRoutine(link, SendResult));
		}

		public static void OnResult(long sender, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer())
			{
				string text = pkg.ReadString();
				bool flag = pkg.ReadBool();
				string text2 = pkg.ReadString();
				PraetorisClientPlugin.Log.LogInfo((object)("Discord link result " + text + ": " + text2));
				Chat instance = Chat.instance;
				if (instance != null)
				{
					((Terminal)instance).AddString(flag ? text2 : ("Discord link failed: " + text2));
				}
			}
		}

		private static void SendResult(long target, string requestId, bool success, string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(requestId);
			val.Write(success);
			val.Write(message);
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "DiscordTools_LinkResult", new object[1] { val });
		}
	}
	internal sealed class LinkRequest
	{
		public long Sender;

		public string RequestId = "";

		public string Code = "";

		public string PlayerId = "";

		public string PlayerName = "";

		public string Endpoint = "";

		public string PlatformDisplayName = "";

		public DateTime ReceivedAtUtc;
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class ZNetAwakePatch
	{
		private static void Postfix()
		{
			CreativeCommandZoneState.Clear();
			PraetorisClientRpc.Register();
		}
	}
	[HarmonyPatch(typeof(Chat), "SendInput")]
	internal static class ChatSendInputPatch
	{
		private static bool Prefix(Chat __instance)
		{
			return !LinkCommandHandler.TryHandle(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	internal static class CharacterApplyDamageTelemetryPatch
	{
		private static void Prefix(Character __instance, HitData hit, ref DamageObservationState __state)
		{
			__state = ValheimEventsTelemetry.CaptureDamageBefore(__instance, hit);
		}

		private static void Postfix(Character __instance, HitData hit, DamageModifier mod, DamageObservationState __state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			ValheimEventsTelemetry.LogDamageApplied(__instance, hit, mod, __state);
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerDeathTelemetryPatch
	{
		private static void Prefix(Player __instance, ref DeathObservationState __state)
		{
			__state = ValheimEventsTelemetry.CaptureDeathBefore(__instance);
		}

		private static void Postfix(Player __instance, DeathObservationState __state)
		{
			ValheimEventsTelemetry.LogPlayerDied(__instance, __state);
		}
	}
	[HarmonyPatch(typeof(Minimap), "Explore", new Type[]
	{
		typeof(int),
		typeof(int)
	})]
	internal static class MinimapExploreTelemetryPatch
	{
		private static void Postfix(Minimap __instance, int x, int y, bool __result)
		{
			if (__result)
			{
				ValheimEventsTelemetry.RecordExploredCell(__instance, x, y);
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Update")]
	internal static class GameUpdateTelemetryPatch
	{
		private static void Postfix()
		{
			ValheimEventsTelemetry.Update();
			RpcTraceTelemetry.Update();
		}
	}
	[HarmonyPatch(typeof(Game), "Logout")]
	internal static class GameLogoutRpcTracePatch
	{
		private static bool Prefix(Game __instance, bool save, bool changeToStartScene)
		{
			return RpcTraceTelemetry.ShouldAllowLogout(__instance, save, changeToStartScene);
		}
	}
	[HarmonyPatch(typeof(Game), "OnApplicationQuit")]
	internal static class GameApplicationQuitRpcTracePatch
	{
		private static void Prefix()
		{
			RpcTraceTelemetry.OnApplicationQuitFallback();
		}
	}
	[HarmonyPatch(typeof(Menu), "QuitGame")]
	internal static class MenuQuitGameRpcTracePatch
	{
		private static bool Prefix()
		{
			return RpcTraceTelemetry.ShouldAllowMenuQuit();
		}
	}
	[HarmonyPatch(typeof(Menu), "OnQuitYes")]
	internal static class MenuQuitYesRpcTracePatch
	{
		private static bool Prefix()
		{
			return RpcTraceTelemetry.ShouldAllowMenuQuit();
		}
	}
	[HarmonyPatch(typeof(ConsoleCommand), "RunAction")]
	internal static class ConsoleCommandRunActionCreativeZoneGuardPatch
	{
		private static bool Prefix(ConsoleEventArgs args)
		{
			return CreativeCommandZoneState.CanRunCommand(args);
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	internal static class SkillsRaiseSkillCreativeZonePatch
	{
		private static bool Prefix(Skills __instance)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)((Character)localPlayer).GetSkills()))
			{
				return !CreativeCommandZoneState.IsLocalPlayerInsideActiveZone();
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "EdgeOfWorldKill")]
	internal static class PlayerEdgeOfWorldKillCreativePatch
	{
		private static bool Prefix(Player __instance)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null))
			{
				return !CreativeBiomeOverride.ContainsTerrainOverride(((Component)__instance).transform.position);
			}
			return true;
		}
	}
	internal static class PlayerResolver
	{
		public static List<ZNetPeer> FindPeers(string query)
		{
			List<ZNetPeer> list = new List<ZNetPeer>();
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return list;
			}
			string text = Normalize(query);
			foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers())
			{
				if (connectedPeer.IsReady())
				{
					string value = SafeHostName(connectedPeer);
					string value2 = StablePlayerId(connectedPeer);
					if (Normalize(connectedPeer.m_playerName) == text || Normalize(value) == text || Normalize(value2) == text || (DigitsOnly(value) == DigitsOnly(query) && DigitsOnly(query).Length > 0))
					{
						list.Add(connectedPeer);
					}
				}
			}
			if (list.Count > 0)
			{
				return list;
			}
			foreach (ZNetPeer connectedPeer2 in ZNet.instance.GetConnectedPeers())
			{
				if (connectedPeer2.IsReady() && Normalize(connectedPeer2.m_playerName).Contains(text))
				{
					list.Add(connectedPeer2);
				}
			}
			return list;
		}

		public static ZNetPeer? FindPeerBySender(long sender)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return null;
			}
			return ((IEnumerable<ZNetPeer>)ZNet.instance.GetConnectedPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer peer) => peer.m_uid == sender));
		}

		public static string DescribePeer(ZNetPeer peer)
		{
			return peer.m_playerName + " (" + StablePlayerId(peer) + ")";
		}

		public static string StablePlayerId(ZNetPeer peer)
		{
			string text = SafeHostName(peer);
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return peer.m_uid.ToString(CultureInfo.InvariantCulture);
		}

		public static string SafeHostName(ZNetPeer peer)
		{
			try
			{
				ISocket socket = peer.m_socket;
				return ((socket != null) ? socket.GetHostName() : null) ?? "";
			}
			catch
			{
				return "";
			}
		}

		public static string SafeEndPoint(ZNetPeer peer)
		{
			try
			{
				ISocket socket = peer.m_socket;
				return ((socket != null) ? socket.GetEndPointString() : null) ?? "";
			}
			catch
			{
				return "";
			}
		}

		public static string PlatformDisplayName(ZNetPeer peer)
		{
			try
			{
				string value;
				return (peer.m_serverSyncedPlayerData != null && peer.m_serverSyncedPlayerData.TryGetValue("platformDisplayName", out value)) ? value : "";
			}
			catch
			{
				return "";
			}
		}

		private static string Normalize(string value)
		{
			return (value ?? "").Trim().ToLowerInvariant();
		}

		private static string DigitsOnly(string value)
		{
			return new string((value ?? "").Where(char.IsDigit).ToArray());
		}
	}
	[BepInPlugin("warpalicious.PraetorisClient", "PraetorisClient", "0.1.31")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PraetorisClientPlugin : BaseUnityPlugin
	{
		private const string ModName = "PraetorisClient";

		private const string ModVersion = "0.1.31";

		private const string Author = "warpalicious";

		private const string ModGUID = "warpalicious.PraetorisClient";

		private const string LinkApiUrlEnv = "PRAETORISCLIENT_LINK_API_URL";

		private const string BotApiKeyEnv = "PRAETORISCLIENT_BOT_API_KEY";

		private readonly Harmony _harmony = new Harmony("warpalicious.PraetorisClient");

		private DateTime _lastReloadTime;

		private FileSystemWatcher? _configWatcher;

		private const long ReloadDelayTicks = 10000000L;

		public static readonly ManualLogSource Log = Logger.CreateLogSource("PraetorisClient");

		internal static ConfigEntry<string> LinkApiUrl = null;

		internal static ConfigEntry<string> BotApiKey = null;

		internal static ConfigEntry<string> LinkCommand = null;

		internal static ConfigEntry<bool> ValheimEventsTelemetryEnabled = null;

		internal static ConfigEntry<bool> CombatTelemetryEnabled = null;

		internal static ConfigEntry<bool> ExplorationTelemetryEnabled = null;

		internal static ConfigEntry<float> ExplorationFlushSeconds = null;

		internal static ConfigEntry<bool> RpcTraceEnabled = null;

		internal static ConfigEntry<bool> RpcTraceCaptureSendReceive = null;

		internal static ConfigEntry<string> RpcTraceNameDenyList = null;

		internal static ConfigEntry<bool> RpcTraceHttpUploadPreferred = null;

		internal static ConfigEntry<bool> ZdoTraceEnabled = null;

		internal static ConfigEntry<string> ZdoTracePrefabFilter = null;

		internal static ConfigEntry<string> ZdoTraceZdoIdFilter = null;

		internal static ConfigEntry<float> ZdoTraceSampleRate = null;

		internal static ConfigEntry<int> ZdoTraceMaxEventsPerSecond = null;

		internal static string TraceModGuid => "warpalicious.PraetorisClient";

		internal static string TraceModName => "PraetorisClient";

		internal static string TraceModVersion => "0.1.31";

		public static PraetorisClientPlugin? Instance { get; private set; }

		internal static string GetLinkApiUrl()
		{
			string environmentVariable = Environment.GetEnvironmentVariable("PRAETORISCLIENT_LINK_API_URL");
			if (!string.IsNullOrWhiteSpace(environmentVariable))
			{
				return environmentVariable.Trim();
			}
			return LinkApiUrl.Value;
		}

		internal static string GetBotApiKey()
		{
			string environmentVariable = Environment.GetEnvironmentVariable("PRAETORISCLIENT_BOT_API_KEY");
			if (!string.IsNullOrWhiteSpace(environmentVariable))
			{
				return environmentVariable.Trim();
			}
			return BotApiKey.Value;
		}

		public void Awake()
		{
			Instance = this;
			BindConfig();
			SynchronizationManager.OnConfigurationSynchronized += OnConfigurationSynchronized;
			SiegePortalTestCommand.Register();
			RpcTraceTelemetry.Initialize();
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			SetupWatcher();
		}

		private void OnDestroy()
		{
			SynchronizationManager.OnConfigurationSynchronized -= OnConfigurationSynchronized;
			try
			{
				_configWatcher?.Dispose();
				_configWatcher = null;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Failed to dispose configuration watcher: " + ex.Message));
			}
			try
			{
				RpcTraceTelemetry.Shutdown();
			}
			catch (Exception ex2)
			{
				Log.LogWarning((object)("Failed to shut down RPC trace telemetry: " + ex2.Message));
			}
			try
			{
				((BaseUnityPlugin)this).Config.Save();
			}
			catch (Exception ex3)
			{
				Log.LogWarning((object)("Failed to save configuration during shutdown: " + ex3.Message));
			}
			try
			{
				_harmony.UnpatchSelf();
			}
			catch (Exception ex4)
			{
				Log.LogWarning((object)("Failed to unpatch PraetorisClient during shutdown: " + ex4.Message));
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void BindConfig()
		{
			LinkApiUrl = ((BaseUnityPlugin)this).Config.Bind<string>("BotApi", "LinkApiUrl", "", "Compatible bot Valheim link endpoint. Prefer the PRAETORISCLIENT_LINK_API_URL environment variable on dedicated servers.");
			BotApiKey = ((BaseUnityPlugin)this).Config.Bind<string>("BotApi", "ApiKey", "", "API key sent to the bot in the X-API-Key header. Prefer the PRAETORISCLIENT_BOT_API_KEY environment variable on dedicated servers.");
			LinkCommand = ((BaseUnityPlugin)this).Config.Bind<string>("Linking", "LinkCommand", "!link", "In-game chat command consumed before it is sent as chat.");
			ValheimEventsTelemetryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("ValheimEvents", "Enabled", true, SyncedDescription("Sends client-observed telemetry to the server-side ValheimEvents mod."));
			CombatTelemetryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("ValheimEvents", "CombatTelemetry", true, SyncedDescription("Sends client-observed combat and death telemetry."));
			ExplorationTelemetryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("ValheimEvents", "ExplorationTelemetry", true, SyncedDescription("Sends client-observed minimap exploration telemetry."));
			ExplorationFlushSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("ValheimEvents", "ExplorationFlushSeconds", 2f, SyncedDescription("How long newly explored minimap cells are batched before sending."));
			RpcTraceEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("RpcTrace", "Enabled", true, SyncedDescription("Sends client-observed routed RPC trace rows to the server-side ValheimTracer receiver."));
			RpcTraceCaptureSendReceive = ((BaseUnityPlugin)this).Config.Bind<bool>("RpcTrace", "CaptureSendReceive", true, SyncedDescription("Captures raw routed RPC send and receive points in addition to handled RPC points."));
			RpcTraceNameDenyList = ((BaseUnityPlugin)this).Config.Bind<string>("RpcTrace", "RpcNameDenyList", "", SyncedDescription("Comma-separated routed RPC names to exclude from client trace capture."));
			RpcTraceHttpUploadPreferred = ((BaseUnityPlugin)this).Config.Bind<bool>("RpcTrace", "HttpUploadPreferred", true, SyncedDescription("Uses ValheimTracer-issued HTTP upload tokens for trace batches when the server supports it."));
			ZdoTraceEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("ZdoTrace", "Enabled", true, "Enables ZDOData package and selected ZDO revision tracing.");
			ZdoTracePrefabFilter = ((BaseUnityPlugin)this).Config.Bind<string>("ZdoTrace", "PrefabFilter", "", "Comma-separated prefab names or prefab hashes to trace. Empty means no prefab filter.");
			ZdoTraceZdoIdFilter = ((BaseUnityPlugin)this).Config.Bind<string>("ZdoTrace", "ZdoIdFilter", "", "Comma-separated ZDO ids to trace in user:id format. Empty means no ZDO id filter.");
			ZdoTraceSampleRate = ((BaseUnityPlugin)this).Config.Bind<float>("ZdoTrace", "SampleRate", 1f, "Deterministic sample rate for ZDO revisions not matched by filters. 0 disables sampling, 1 captures all revisions.");
			ZdoTraceMaxEventsPerSecond = ((BaseUnityPlugin)this).Config.Bind<int>("ZdoTrace", "MaxEventsPerSecond", 0, "Maximum non-forced ZDO trace events per second. Set to 0 for no limit.");
		}

		private static ConfigDescription SyncedDescription(string description)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { val });
		}

		private static void OnConfigurationSynchronized(object sender, ConfigurationSynchronizationEventArgs args)
		{
			if (args.UpdatedPluginGUIDs != null && args.UpdatedPluginGUIDs.Contains("warpalicious.PraetorisClient"))
			{
				string text = (args.InitialSynchronization ? "initial" : "updated");
				Log.LogInfo((object)("Jotunn synchronized PraetorisClient configuration (" + text + ")."));
			}
		}

		private void SetupWatcher()
		{
			try
			{
				_lastReloadTime = DateTime.Now;
				_configWatcher?.Dispose();
				_configWatcher = new FileSystemWatcher(Paths.ConfigPath, "warpalicious.PraetorisClient.cfg");
				_configWatcher.Changed += ReadConfigValues;
				_configWatcher.Created += ReadConfigValues;
				_configWatcher.Renamed += ReadConfigValues;
				_configWatcher.IncludeSubdirectories = true;
				_configWatcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Failed to start configuration watcher: " + ex.Message));
			}
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime now = DateTime.Now;
			long num = now.Ticks - _lastReloadTime.Ticks;
			if (File.Exists(Path.Combine(Paths.ConfigPath, "warpalicious.PraetorisClient.cfg")) && num >= 10000000)
			{
				try
				{
					Log.LogInfo((object)"Reloading configuration.");
					((BaseUnityPlugin)this).Config.Reload();
				}
				catch (Exception ex)
				{
					Log.LogError((object)("Failed to reload configuration: " + ex.Message));
				}
				_lastReloadTime = now;
			}
		}
	}
	internal static class PraetorisClientRpc
	{
		private static ZRoutedRpc? _registeredRpc;

		public static void Register()
		{
			if (ZRoutedRpc.instance != null && _registeredRpc != ZRoutedRpc.instance)
			{
				_registeredRpc = ZRoutedRpc.instance;
				ZRoutedRpc.instance.Register<ZPackage>("DiscordTools_LinkRequest", (Action<long, ZPackage>)LinkRpc.OnRequest);
				ZRoutedRpc.instance.Register<ZPackage>("DiscordTools_LinkResult", (Action<long, ZPackage>)LinkRpc.OnResult);
				ZRoutedRpc.instance.Register<ZPackage>("DiscordTools_CreativeInventoryRequest", (Action<long, ZPackage>)CreativeInventoryRpc.OnRequest);
				ZRoutedRpc.instance.Register<ZPackage>("DiscordTools_CreativeBiomeOverride", (Action<long, ZPackage>)CreativeBiomeOverride.OnOverride);
				ZRoutedRpc.instance.Register<ZPackage>("PraetorisClient_CreativeCommandZoneState", (Action<long, ZPackage>)CreativeCommandZoneState.OnState);
				ZRoutedRpc.instance.Register<ZPackage>("PraetorisClient_RpcTraceClockResponse", (Action<long, ZPackage>)RpcTraceTelemetry.OnClockResponse);
				ZRoutedRpc.instance.Register<ZPackage>("PraetorisClient_RpcTraceUploadTokenResponse", (Action<long, ZPackage>)RpcTraceUploadTokenClient.OnTokenResponse);
				PraetorisClientPlugin.Log.LogInfo((object)"Registered PraetorisClient RPC handlers.");
			}
		}
	}
	internal static class RpcNames
	{
		public const string LinkRequest = "DiscordTools_LinkRequest";

		public const string LinkResult = "DiscordTools_LinkResult";

		public const string CreativeInventoryRequest = "DiscordTools_CreativeInventoryRequest";

		public const string CreativeInventoryResponse = "DiscordTools_CreativeInventoryResponse";

		public const string CreativeBiomeOverride = "DiscordTools_CreativeBiomeOverride";

		public const string CreativeCommandZoneState = "PraetorisClient_CreativeCommandZoneState";

		public const string SiegePortalEnter = "DiscordTools_SiegePortalEnter";

		public const string ValheimEventsTelemetry = "PraetorisClient_ValheimEventsTelemetry";

		public const string RpcTraceClockRequest = "PraetorisClient_RpcTraceClockRequest";

		public const string RpcTraceClockResponse = "PraetorisClient_RpcTraceClockResponse";

		public const string RpcTraceUploadTokenRequest = "PraetorisClient_RpcTraceUploadTokenRequest";

		public const string RpcTraceUploadTokenResponse = "PraetorisClient_RpcTraceUploadTokenResponse";
	}
	internal static class RpcTraceFlushCoordinator
	{
		private const float UploadUnavailableLogIntervalSeconds = 30f;

		private static bool _allowQuit;

		private static float _nextUploadUnavailableLogTime;

		internal static void Initialize()
		{
			Application.wantsToQuit -= OnWantsToQuit;
			Application.wantsToQuit += OnWantsToQuit;
			_allowQuit = false;
			_nextUploadUnavailableLogTime = 0f;
		}

		internal static void Shutdown()
		{
			Application.wantsToQuit -= OnWantsToQuit;
		}

		internal static void RequestFlush(string reason)
		{
			if (RpcTraceTelemetry.IsTracingEnabled())
			{
				if (RpcTraceHttpUploadCoordinator.CanAcceptFlushRequest())
				{
					RpcTraceHttpUploadCoordinator.RequestFlush(reason);
				}
				else
				{
					LogHttpUnavailable(reason);
				}
			}
		}

		internal static void Update()
		{
			if (RpcTraceTelemetry.IsTracingEnabled() && !RpcTraceHttpUploadCoordinator.IsActive() && !RpcTraceHttpUploadCoordinator.CanAcceptFlushRequest() && RpcTraceLocalStore.HasPendingFiles())
			{
				LogHttpUnavailable("background");
			}
		}

		internal static bool ShouldAllowLogout(Game game, bool save, bool changeToStartScene)
		{
			if (!RpcTraceTelemetry.IsTracingEnabled())
			{
				return true;
			}
			if (RpcTraceHttpUploadCoordinator.CanAcceptFlushRequest())
			{
				RpcTraceHttpUploadCoordinator.RequestFlush("logout");
			}
			else if (RpcTraceLocalStore.HasPendingFiles())
			{
				LogHttpUnavailable("logout");
			}
			RpcTraceTelemetry.SuppressCaptureUntilDisconnected();
			return true;
		}

		internal static bool ShouldAllowMenuQuit()
		{
			if (!RpcTraceTelemetry.IsTracingEnabled())
			{
				return true;
			}
			if (RpcTraceHttpUploadCoordinator.CanAcceptFlushRequest())
			{
				RpcTraceHttpUploadCoordinator.RequestFlush("quit");
			}
			else if (RpcTraceLocalStore.HasPendingFiles())
			{
				LogHttpUnavailable("quit");
			}
			RpcTraceTelemetry.DisableCaptureForShutdown();
			return true;
		}

		private static bool OnWantsToQuit()
		{
			if (_allowQuit)
			{
				return true;
			}
			if (!RpcTraceTelemetry.IsTracingEnabled())
			{
				return true;
			}
			if (RpcTraceHttpUploadCoordinator.CanAcceptFlushRequest())
			{
				RpcTraceHttpUploadCoordinator.RequestFlush("quit");
			}
			else if (RpcTraceLocalStore.HasPendingFiles())
			{
				LogHttpUnavailable("quit");
			}
			RpcTraceTelemetry.DisableCaptureForShutdown();
			_allowQuit = true;
			return true;
		}

		private static void LogHttpUnavailable(string reason)
		{
			if (!(Time.realtimeSinceStartup < _nextUploadUnavailableLogTime))
			{
				_nextUploadUnavailableLogTime = Time.realtimeSinceStartup + 30f;
				PraetorisClientPlugin.Log.LogWarning((object)("RPC trace HTTP upload is unavailable during " + (string.IsNullOrWhiteSpace(reason) ? "flush" : reason) + "; keeping local trace files for later HTTP retry."));
			}
		}
	}
	internal static class RpcTraceHttpUploadContract
	{
		internal const string ContentType = "application/gzip";

		internal const string UserAgentSuffix = "ValheimTracerHttpUpload";

		internal static Dictionary<string, string> BuildHeaders(string token, string batchId, string runtimeId, string fileId, int batchIndex, bool finalBatch, string flushReason, string modVersion)
		{
			return new Dictionary<string, string>
			{
				{
					"Authorization",
					"Bearer " + token
				},
				{ "Content-Type", "application/gzip" },
				{ "X-Trace-Batch-Id", batchId },
				{ "X-Trace-Runtime-Id", runtimeId },
				{ "X-Trace-File-Id", fileId },
				{
					"X-Trace-Batch-Index",
					batchIndex.ToString()
				},
				{
					"X-Trace-Final-Batch",
					finalBatch ? "true" : "false"
				},
				{
					"X-Trace-Flush-Reason",
					flushReason ?? ""
				},
				{
					"User-Agent",
					BuildUserAgent(modVersion)
				}
			};
		}

		internal static string BuildUserAgent(string modVersion)
		{
			string text = (string.IsNullOrWhiteSpace(modVersion) ? "unknown" : modVersion);
			return "PraetorisClient/" + text + " ValheimTracerHttpUpload";
		}
	}
	internal static class RpcTraceHttpUploadCoordinator
	{
		private sealed class PreparedUploadBatch
		{
			internal byte[] Body { get; }

			internal int ConsumedRows { get; }

			internal bool FinalBatch { get; }

			internal bool EndOfFile { get; }

			internal bool SkippedOversizedRow { get; }

			internal double PreparationMilliseconds { get; }

			private PreparedUploadBatch(byte[] body, int consumedRows, bool finalBatch, bool endOfFile, bool skippedOversizedRow, double preparationMilliseconds)
			{
				Body = body;
				ConsumedRows = consumedRows;
				FinalBatch = finalBatch;
				EndOfFile = endOfFile;
				SkippedOversizedRow = skippedOversizedRow;
				PreparationMilliseconds = preparationMilliseconds;
			}

			internal static PreparedUploadBatch Ready(byte[] body, int consumedRows, bool finalBatch, double preparationMilliseconds)
			{
				return new PreparedUploadBatch(body, consumedRows, finalBatch, endOfFile: false, skippedOversizedRow: false, preparationMilliseconds);
			}

			internal static PreparedUploadBatch End()
			{
				return new PreparedUploadBatch(Array.Empty<byte>(), 0, finalBatch: true, endOfFile: true, skippedOversizedRow: false, 0.0);
			}

			internal static PreparedUploadBatch SkipOversizedRow()
			{
				return new PreparedUploadBatch(Array.Empty<byte>(), 0, finalBatch: false, endOfFile: false, skippedOversizedRow: true, 0.0);
			}
		}

		private sealed class UploadResult
		{
			internal bool Success { get; }

			internal long ResponseCode { get; }

			internal string Message { get; }

			internal UploadResult(bool success, long responseCode, string message)
			{
				Success = success;
				ResponseCode = responseCode;
				Message = message ?? "";
			}
		}

		private const int MaxHttpRowsPerBatch = 1000;

		private const int FirstRetryRowsPerBatch = 250;

		private const int MinHttpRowsPerBatch = 100;

		private const float InitialFailureRetrySeconds = 15f;

		private const float MaxFailureRetrySeconds = 300f;

		private const float FailureLogIntervalSeconds = 60f;

		private const float UploadFrameSummaryIntervalSeconds = 30f;

		private const float UploadFrameWarningThresholdMs = 150f;

		private const float WorldReadyUploadDelaySeconds = 20f;

		private const int HttpUploadTimeoutMilliseconds = 30000;

		private static readonly object Sync = new object();

		private static readonly Queue<string> PendingFiles = new Queue<string>();

		private static bool _flushRequested;

		private static string _flushReason = "background";

		private static bool _uploading;

		private static float _nextUploadTime;

		private static int _consecutiveFailures;

		private static int _maxRowsPerUploadBatch = 1000;

		private static float _nextFailureLogTime;

		private static float _worldReadyUploadTime;

		private static float _uploadFrameWindowActiveUntil;

		private static float _nextUploadFrameSummaryTime;

		private static int _uploadFrameSamples;

		private static int _longUploadFrames;

		private static float _maxUploadFrameMs;

		internal static void Initialize()
		{
			lock (Sync)
			{
				PendingFiles.Clear();
				_flushRequested = false;
				_flushReason = "background";
				_uploading = false;
				_nextUploadTime = 0f;
				_consecutiveFailures = 0;
				_maxRowsPerUploadBatch = 1000;
				_nextFailureLogTime = 0f;
				_worldReadyUploadTime = 0f;
				ResetUploadFrameStatsLocked();
			}
		}

		internal static void Shutdown()
		{
			lock (Sync)
			{
				PendingFiles.Clear();
				_uploading = false;
				_flushRequested = false;
				_consecutiveFailures = 0;
				_maxRowsPerUploadBatch = 1000;
				_nextFailureLogTime = 0f;
				_worldReadyUploadTime = 0f;
				ResetUploadFrameStatsLocked();
			}
		}

		internal static bool IsActive()
		{
			if (!HasUploadConfiguration())
			{
				_worldReadyUploadTime = 0f;
				return false;
			}
			return CanUpload();
		}

		internal static bool CanAcceptFlushRequest()
		{
			if (!HasUploadConfiguration())
			{
				_worldReadyUploadTime = 0f;
				return false;
			}
			return HasConnectedClient();
		}

		internal static void RequestFlush(string reason)
		{
			lock (Sync)
			{
				_flushRequested = true;
				_flushReason = (string.IsNullOrWhiteSpace(reason) ? "manual" : reason);
				_nextUploadTime = 0f;
			}
		}

		internal static void Update()
		{
			RecordUploadFrame();
			if (!IsActive())
			{
				return;
			}
			lock (Sync)
			{
				if (_uploading || (!_flushRequested && Time.realtimeSinceStartup < _nextUploadTime))
				{
					return;
				}
			}
			PrepareFilesIfNeeded();
			StartNextUploadIfReady();
		}

		private static void PrepareFilesIfNeeded()
		{
			lock (Sync)
			{
				if (PendingFiles.Count > 0 || _uploading)
				{
					return;
				}
				foreach (string flushableFile in RpcTraceLocalStore.GetFlushableFiles())
				{
					if (new FileInfo(flushableFile).Length == 0L)
					{
						RpcTraceLocalStore.DeleteFile(flushableFile);
					}
					else
					{
						PendingFiles.Enqueue(flushableFile);
					}
				}
				_flushRequested = false;
				_nextUploadTime = Time.realtimeSinceStartup + RpcTraceUploadTokenClient.FlushIntervalSeconds;
			}
		}

		private static void StartNextUploadIfReady()
		{
			string path;
			string flushReason;
			lock (Sync)
			{
				if (_uploading || PendingFiles.Count == 0)
				{
					return;
				}
				path = PendingFiles.Dequeue();
				flushReason = _flushReason;
				_uploading = true;
				_flushRequested = false;
				_uploadFrameWindowActiveUntil = Time.realtimeSinceStartup + 30f;
			}
			if ((Object)(object)PraetorisClientPlugin.Instance != (Object)null)
			{
				((MonoBehaviour)PraetorisClientPlugin.Instance).StartCoroutine(UploadFile(path, flushReason));
			}
			else
			{
				RequeueUpload(path);
			}
		}

		private static IEnumerator UploadFile(string path, string flushReason)
		{
			string fileId = RpcTraceLocalStore.BuildFileId(path);
			int startLine = 0;
			int batchIndex = 0;
			while (IsActive() && File.Exists(path))
			{
				int maxRows = GetMaxRowsPerUploadBatch();
				Task<PreparedUploadBatch> prepareTask = Task.Run(() => PrepareUploadBatch(path, startLine, maxRows));
				while (!prepareTask.IsCompleted)
				{
					yield return null;
				}
				if (prepareTask.IsFaulted)
				{
					PraetorisClientPlugin.Log.LogWarning((object)("Failed to prepare HTTP RPC trace batch for " + fileId + ": " + (prepareTask.Exception?.GetBaseException().Message ?? "unknown error")));
					RequeueUpload(path);
					yield break;
				}
				PreparedUploadBatch batch = prepareTask.Result;
				if (batch.EndOfFile)
				{
					RpcTraceLocalStore.DeleteFile(path);
					CompleteUpload(success: true);
					yield break;
				}
				if (batch.SkippedOversizedRow)
				{
					PraetorisClientPlugin.Log.LogWarning((object)("Skipping oversized HTTP RPC trace row in " + fileId + "."));
					int num = startLine;
					startLine = num + 1;
					continue;
				}
				string batchId = fileId + "-" + batchIndex.ToString("D6");
				PraetorisClientPlugin.Log.LogInfo((object)("Prepared HTTP RPC trace batch " + batchId + ": rows=" + batch.ConsumedRows + ", gzipBytes=" + batch.Body.Length + ", final=" + batch.FinalBatch + ", prepareMs=" + batch.PreparationMilliseconds.ToString("F1", CultureInfo.InvariantCulture) + " off main thread."));
				Dictionary<string, string> headers = RpcTraceHttpUploadContract.BuildHeaders(RpcTraceUploadTokenClient.Token, batchId, RpcTraceTelemetry.RuntimeId, fileId, batchIndex, batch.FinalBatch, flushReason, PraetorisClientPlugin.TraceModVersion);
				Task<UploadResult> uploadTask = Task.Run(() => SendHttpUpload(RpcTraceUploadTokenClient.EndpointUrl, headers, batch.Body));
				while (!uploadTask.IsCompleted)
				{
					yield return null;
				}
				if (uploadTask.IsFaulted)
				{
					string message = uploadTask.Exception?.GetBaseException().Message ?? "unknown error";
					RegisterUploadFailure(batchId, 0L, message);
					RequeueUpload(path);
					yield break;
				}
				UploadResult result = uploadTask.Result;
				if (!result.Success)
				{
					if (!RpcTraceUploadTokenClient.ShouldRetryUpload(result.ResponseCode, result.Message))
					{
						CompleteUpload(success: false);
						yield break;
					}
					RegisterUploadFailure(batchId, result.ResponseCode, result.Message);
					RequeueUpload(path);
					yield break;
				}
				RegisterUploadSuccess();
				startLine += batch.ConsumedRows;
				batchIndex++;
				if (!batch.FinalBatch)
				{
					continue;
				}
				PraetorisClientPlugin.Log.LogInfo((object)("Uploaded RPC trace file " + fileId + " over HTTP; deleting local copy."));
				RpcTraceLocalStore.DeleteFile(path);
				CompleteUpload(success: true);
				yield break;
			}
			RequeueUpload(path);
		}

		private static UploadResult SendHttpUpload(string endpointUrl, Dictionary<string, string> headers, byte[] body)
		{
			if (!TryNormalizeEndpointUrl(endpointUrl, out string normalizedEndpointUrl, out string error))
			{
				return new UploadResult(success: false, 0L, error);
			}
			try
			{
				Uri uri = new Uri(normalizedEndpointUrl);
				int port = ((uri.Port > 0) ? uri.Port : (string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) ? 443 : 80));
				using TcpClient tcpClient = new TcpClient();
				tcpClient.SendTimeout = 30000;
				tcpClient.ReceiveTimeout = 30000;
				ConnectWithTimeout(tcpClient, uri.Host, port);
				using Stream stream = CreateRequestStream(tcpClient, uri);
				WriteHttpRequest(stream, uri, port, headers, body);
				return ReadHttpResponse(stream);
			}
			catch (IOException ex)
			{
				return new UploadResult(success: false, 0L, ex.Message);
			}
			catch (SocketException ex2)
			{
				return new UploadResult(success: false, 0L, ex2.Message);
			}
			catch (AuthenticationException ex3)
			{
				return new UploadResult(success: false, 0L, ex3.Message);
			}
		}

		private static void ConnectWithTimeout(TcpClient client, string host, int port)
		{
			IAsyncResult asyncResult = client.BeginConnect(host, port, null, null);
			try
			{
				if (!asyncResult.AsyncWaitHandle.WaitOne(30000))
				{
					throw new IOException("Request timeout");
				}
				client.EndConnect(asyncResult);
			}
			finally
			{
				asyncResult.AsyncWaitHandle.Close();
			}
		}

		private static Stream CreateRequestStream(TcpClient client, Uri uri)
		{
			NetworkStream stream = client.GetStream();
			if (!string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
			{
				return stream;
			}
			SslStream sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
			sslStream.AuthenticateAsClient(uri.Host);
			return sslStream;
		}

		private static void WriteHttpRequest(Stream stream, Uri uri, int port, Dictionary<string, string> headers, byte[] body)
		{
			StringBuilder stringBuilder = new StringBuilder();
			string value = (string.IsNullOrWhiteSpace(uri.PathAndQuery) ? "/" : uri.PathAndQuery);
			stringBuilder.Append("POST ").Append(value).Append(" HTTP/1.1\r\n");
			stringBuilder.Append("Host: ").Append(BuildHostHeader(uri, port)).Append("\r\n");
			stringBuilder.Append("Connection: close\r\n");
			stringBuilder.Append("Content-Length: ").Append(body.Length.ToString(CultureInfo.InvariantCulture)).Append("\r\n");
			foreach (KeyValuePair<string, string> header in headers)
			{
				if (!string.Equals(header.Key, "Host", StringComparison.OrdinalIgnoreCase) && !string.Equals(header.Key, "Connection", StringComparison.OrdinalIgnoreCase) && !string.Equals(header.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
				{
					stringBuilder.Append(header.Key).Append(": ").Append(header.Value ?? "")
						.Append("\r\n");
				}
			}
			stringBuilder.Append("\r\n");
			byte[] bytes = Encoding.ASCII.GetBytes(stringBuilder.ToString());
			stream.Write(bytes, 0, bytes.Length);
			stream.Write(body, 0, body.Length);
			stream.Flush();
		}

		private static string BuildHostHeader(Uri uri, int port)
		{
			if ((!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) || port != 80) && (!string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) || port != 443))
			{
				return uri.Host + ":" + port.ToString(CultureInfo.InvariantCulture);
			}
			return uri.Host;
		}

		private static UploadResult ReadHttpResponse(Stream stream)
		{
			using MemoryStream memoryStream = new MemoryStream();
			byte[] array = new byte[8192];
			int count;
			while ((count = stream.Read(array, 0, array.Length)) > 0)
			{
				memoryStream.Write(array, 0, count);
			}
			string text = Encoding.UTF8.GetString(memoryStream.ToArray());
			int num = text.IndexOf("\r\n\r\n", StringComparison.Ordinal);
			string obj = ((num >= 0) ? text.Substring(0, num) : text);
			string message = ((num >= 0) ? text.Substring(num + 4) : "");
			string[] array2 = obj.Split(new string[1] { "\r\n" }, StringSplitOptions.None)[0].Split(new char[1] { ' ' });
			if (array2.Length < 2 || !long.TryParse(array2[1], out var result))
			{
				return new UploadResult(success: false, 0L, "Invalid HTTP response");
			}
			return new UploadResult(result >= 200 && result < 300, result, message);
		}

		private static bool TryNormalizeEndpointUrl(string endpointUrl, out string normalizedEndpointUrl, out string error)
		{
			normalizedEndpointUrl = (endpointUrl ?? "").Trim();
			error = "";
			if (string.IsNullOrWhiteSpace(normalizedEndpointUrl))
			{
				error = "Empty upload endpoint URL";
				return false;
			}
			if (normalizedEndpointUrl.StartsWith("//", StringComparison.Ordinal))
			{
				normalizedEndpointUrl = "http:" + normalizedEndpointUrl;
			}
			else if (!normalizedEndpointUrl.Contains("://"))
			{
				normalizedEndpointUrl = "http://" + normalizedEndpointUrl;
			}
			if (!Uri.TryCreate(normalizedEndpointUrl, UriKind.Absolute, out Uri result))
			{
				error = "Invalid upload endpoint URL";
				return false;
			}
			if (!string.Equals(result.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && !string.Equals(result.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
			{
				error = "Unsupported upload endpoint scheme";
				return false;
			}
			normalizedEndpointUrl = result.AbsoluteUri;
			return true;
		}

		private static PreparedUploadBatch PrepareUploadBatch(string path, int startLine, int maxRows)
		{
			bool reachedEnd;
			List<string> list = RpcTraceLocalStore.ReadBatch(path, startLine, maxRows, out reachedEnd);
			if (list.Count == 0 && reachedEnd)
			{
				return PreparedUploadBatch.End();
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			int num = Math.Max(4096, RpcTraceUploadTokenClient.MaxBatchBytes);
			while (list.Count > 0)
			{
				byte[] array = GzipRows(list);
				if (array.Length <= num)
				{
					stopwatch.Stop();
					return PreparedUploadBatch.Ready(array, list.Count, reachedEnd, stopwatch.Elapsed.TotalMilliseconds);
				}
				if (list.Count == 1)
				{
					return PreparedUploadBatch.SkipOversizedRow();
				}
				list.RemoveAt(list.Count - 1);
			}
			return PreparedUploadBatch.SkipOversizedRow();
		}

		private static byte[] GzipRows(IReadOnlyList<string> rows)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (GZipStream stream = new GZipStream(memoryStream, CompressionLevel.Fastest, leaveOpen: true))
			{
				using StreamWriter streamWriter = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				foreach (string row in rows)
				{
					streamWriter.WriteLine(row);
				}
			}
			return memoryStream.ToArray();
		}

		private static void RequeueUpload(string path)
		{
			lock (Sync)
			{
				if (File.Exists(path))
				{
					PendingFiles.Enqueue(path);
				}
				_uploading = false;
				_flushRequested = false;
				_nextUploadTime = Time.realtimeSinceStartup + GetFailureRetryDelaySeconds();
				_uploadFrameWindowActiveUntil = Time.realtimeSinceStartup + 2f;
			}
		}

		private static void CompleteUpload(bool success)
		{
			lock (Sync)
			{
				_uploading = false;
				if (success)
				{
					_consecutiveFailures = 0;
				}
				if (success && PendingFiles.Count > 0)
				{
					_nextUploadTime = 0f;
				}
				else
				{
					_nextUploadTime = Time.realtimeSinceStartup + (success ? RpcTraceUploadTokenClient.FlushIntervalSeconds : GetFailureRetryDelaySeconds());
				}
				_uploadFrameWindowActiveUntil = Time.realtimeSinceStartup + 2f;
			}
		}

		private static void RegisterUploadFailure(string batchId, long responseCode, string message)
		{
			int maxRowsPerUploadBatch;
			float failureRetryDelaySeconds;
			bool flag;
			lock (Sync)
			{
				_consecutiveFailures++;
				AdjustBatchSizeAfterFailure(responseCode, message);
				maxRowsPerUploadBatch = _maxRowsPerUploadBatch;
				failureRetryDelaySeconds = GetFailureRetryDelaySeconds();
				flag = Time.realtimeSinceStartup >= _nextFailureLogTime;
				if (flag)
				{
					_nextFailureLogTime = Time.realtimeSinceStartup + 60f;
				}
			}
			if (flag)
			{
				PraetorisClientPlugin.Log.LogWarning((object)("HTTP RPC trace upload failed for " + batchId + ": HTTP " + responseCode + " " + message + ". Retrying in " + Math.Ceiling(failureRetryDelaySeconds) + "s with maxRows=" + maxRowsPerUploadBatch + "."));
			}
		}

		private static void RegisterUploadSuccess()
		{
			lock (Sync)
			{
				_consecutiveFailures = 0;
				_nextFailureLogTime = 0f;
			}
		}

		private static int GetMaxRowsPerUploadBatch()
		{
			lock (Sync)
			{
				return Math.Max(100, _maxRowsPerUploadBatch);
			}
		}

		private static void AdjustBatchSizeAfterFailure(long responseCode, string message)
		{
			if (IsReceiverTimeoutFailure(responseCode, message))
			{
				if (_maxRowsPerUploadBatch > 250)
				{
					_maxRowsPerUploadBatch = 250;
				}
				else
				{
					_maxRowsPerUploadBatch = 100;
				}
			}
		}

		private static bool IsReceiverTimeoutFailure(long responseCode, string message)
		{
			if (responseCode == 504 || responseCode == 408)
			{
				return true;