Decompiled source of ServerLoadingScreen v1.0.8

plugins/ServerLoadingScreen.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ServerLoadingScreen")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.8.0")]
[assembly: AssemblyInformationalVersion("1.0.8+80462296ddc44a65758684e183b1490e287a298e")]
[assembly: AssemblyProduct("ServerLoadingScreen")]
[assembly: AssemblyTitle("ServerLoadingScreen")]
[assembly: AssemblyVersion("1.0.8.0")]
[module: RefSafetyRules(11)]
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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ServerLoadingScreen
{
	internal static class AssetNames
	{
		public static void Validate(string name, bool logo)
		{
			Limits.Require(name.Length > 0 && name.Length <= 128 && name != "." && name != ".." && !name.Any((char c) => char.IsControl(c) || "<>:\"/\\|?*".IndexOf(c) >= 0));
			string text = Path.GetExtension(name).ToLowerInvariant();
			Limits.Require(text == ".png" || (!logo && (text == ".jpg" || text == ".jpeg")));
		}
	}
	internal static class Limits
	{
		public const int Background = 8388608;

		public const int Logo = 2097152;

		public const int Chunk = 16384;

		public const int Packet = 16512;

		public const long CacheBytes = 134217728L;

		public static void Require(bool ok)
		{
			if (!ok)
			{
				throw new InvalidDataException("Invalid loading-screen data.");
			}
		}

		public static string Hash(byte[] bytes)
		{
			using SHA256 sHA = SHA256.Create();
			return string.Concat(from b in sHA.ComputeHash(bytes)
				select b.ToString("x2"));
		}

		public static bool IsHash(string value)
		{
			if (value.Length == 64)
			{
				return value.All((char c) => (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'));
			}
			return false;
		}
	}
	internal sealed class Asset
	{
		public int Length;

		public int Width;

		public int Height;

		public string Hash = "";

		public void Validate(bool logo)
		{
			Limits.Require(Length >= 0 && Length <= (logo ? 2097152 : 8388608));
			if (Length == 0)
			{
				Limits.Require(Width == 0 && Height == 0 && Hash.Length == 0);
				return;
			}
			Limits.Require(Limits.IsHash(Hash));
			ImageGuard.Dimensions(Width, Height, logo);
		}

		public static Asset From(byte[] bytes, bool logo)
		{
			if (bytes.Length == 0)
			{
				return new Asset();
			}
			Tuple<int, int> tuple = ImageGuard.Check(bytes, logo);
			return new Asset
			{
				Length = bytes.Length,
				Width = tuple.Item1,
				Height = tuple.Item2,
				Hash = Limits.Hash(bytes)
			};
		}

		public void Check(byte[] bytes, bool logo)
		{
			Limits.Require(bytes.Length == Length && Limits.Hash(bytes) == Hash);
			Tuple<int, int> tuple = ImageGuard.Check(bytes, logo);
			Limits.Require(tuple.Item1 == Width && tuple.Item2 == Height);
		}
	}
	internal enum LoadingAnimation : byte
	{
		Hammer,
		Campfire,
		Off
	}
	internal sealed class Manifest
	{
		public Guid ServerId;

		public string Welcome = "";

		public float LogoWidth = 0.3f;

		public float LogoX = 0.5f;

		public float LogoY = 0.65f;

		public float MinimumDisplaySeconds = 5f;

		public LoadingAnimation Animation;

		public Asset Background = new Asset();

		public Asset Logo = new Asset();

		public Asset At(int slot)
		{
			Limits.Require(slot == 0 || slot == 1);
			if (slot != 0)
			{
				return Logo;
			}
			return Background;
		}

		public void Validate()
		{
			Limits.Require(ServerId != Guid.Empty && Welcome.Length <= 200 && !Welcome.Any(char.IsControl));
			Limits.Require(LogoWidth >= 0.05f && LogoWidth <= 0.8f && LogoX >= 0f && LogoX <= 1f && LogoY >= 0f && LogoY <= 1f);
			Limits.Require(MinimumDisplaySeconds >= 5f && MinimumDisplaySeconds <= 60f);
			Limits.Require(Animation == LoadingAnimation.Hammer || Animation == LoadingAnimation.Campfire || Animation == LoadingAnimation.Off);
			Background.Validate(logo: false);
			Logo.Validate(logo: true);
			Limits.Require(Background.Length != 0 || Logo.Length == 0);
		}
	}
	internal static class Wire
	{
		public const byte Hello = 1;

		public const byte Description = 2;

		public const byte Request = 3;

		public const byte Block = 4;

		public const byte Complete = 5;

		public static byte[] Pack(byte kind, Guid nonce, Action<BinaryWriter>? body = null)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true);
			binaryWriter.Write(1397510961);
			binaryWriter.Write((byte)4);
			binaryWriter.Write(kind);
			binaryWriter.Write(nonce.ToByteArray());
			body?.Invoke(binaryWriter);
			Limits.Require(memoryStream.Length <= 16512);
			return memoryStream.ToArray();
		}

		public static BinaryReader Open(byte[] bytes, out byte kind, out Guid nonce)
		{
			Limits.Require(bytes.Length >= 22 && bytes.Length <= 16512);
			BinaryReader binaryReader = new BinaryReader(new MemoryStream(bytes, writable: false), Encoding.UTF8);
			try
			{
				Limits.Require(binaryReader.ReadInt32() == 1397510961 && binaryReader.ReadByte() == 4);
				kind = binaryReader.ReadByte();
				nonce = new Guid(Exact(binaryReader, 16));
				Limits.Require(kind >= 1 && kind <= 5 && nonce != Guid.Empty);
				return binaryReader;
			}
			catch
			{
				binaryReader.Dispose();
				throw;
			}
		}

		public static byte[] Exact(BinaryReader r, int count)
		{
			Limits.Require(count >= 0 && count <= 16512 && count <= r.BaseStream.Length - r.BaseStream.Position);
			return r.ReadBytes(count);
		}

		public static void End(BinaryReader r)
		{
			Limits.Require(r.BaseStream.Position == r.BaseStream.Length);
		}

		public static void Text(BinaryWriter w, string text)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(text);
			w.Write((ushort)bytes.Length);
			w.Write(bytes);
		}

		public static string Text(BinaryReader r, int max)
		{
			int num = r.ReadUInt16();
			Limits.Require(num <= max);
			return new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Exact(r, num));
		}

		public static void WriteManifest(BinaryWriter w, Manifest m)
		{
			m.Validate();
			w.Write(m.ServerId.ToByteArray());
			Text(w, m.Welcome);
			w.Write(m.LogoWidth);
			w.Write(m.LogoX);
			w.Write(m.LogoY);
			w.Write(m.MinimumDisplaySeconds);
			w.Write((byte)m.Animation);
			Asset[] array = new Asset[2] { m.Background, m.Logo };
			foreach (Asset asset in array)
			{
				w.Write(asset.Length);
				w.Write(asset.Width);
				w.Write(asset.Height);
				Text(w, asset.Hash);
			}
		}

		public static Manifest ReadManifest(BinaryReader r)
		{
			Manifest manifest = new Manifest
			{
				ServerId = new Guid(Exact(r, 16)),
				Welcome = Text(r, 800),
				LogoWidth = r.ReadSingle(),
				LogoX = r.ReadSingle(),
				LogoY = r.ReadSingle(),
				MinimumDisplaySeconds = r.ReadSingle(),
				Animation = (LoadingAnimation)r.ReadByte()
			};
			Asset[] array = new Asset[2] { manifest.Background, manifest.Logo };
			foreach (Asset obj in array)
			{
				obj.Length = r.ReadInt32();
				obj.Width = r.ReadInt32();
				obj.Height = r.ReadInt32();
				obj.Hash = Text(r, 64);
			}
			End(r);
			manifest.Validate();
			return manifest;
		}
	}
	internal static class ImageGuard
	{
		private static uint U32(byte[] b, int p)
		{
			return (uint)((b[p] << 24) | (b[p + 1] << 16) | (b[p + 2] << 8) | b[p + 3]);
		}

		private static int U16(byte[] b, int p)
		{
			return (b[p] << 8) | b[p + 1];
		}

		public static void Dimensions(int w, int h, bool logo)
		{
			int num = (logo ? 2048 : 4096);
			Limits.Require(w > 0 && h > 0 && w <= num && h <= num && (long)w * (long)h <= (logo ? 4194304 : 8388608));
		}

		public static Tuple<int, int> Check(byte[] b, bool logo)
		{
			Limits.Require(b.Length >= 24 && b.Length <= (logo ? 2097152 : 8388608));
			int num = 0;
			int num2 = 0;
			if (b.Take(8).SequenceEqual(new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }))
			{
				bool flag = false;
				bool flag2 = false;
				bool flag3 = false;
				int num3 = 8;
				while (num3 < b.Length)
				{
					Limits.Require(b.Length - num3 >= 12);
					uint num4 = U32(b, num3);
					Limits.Require(num4 <= b.Length - num3 - 12);
					int num5 = (int)num4;
					string text = Encoding.ASCII.GetString(b, num3 + 4, 4);
					Limits.Require(flag3 || text == "IHDR");
					uint num6 = uint.MaxValue;
					for (int i = num3 + 4; i < num3 + 8 + num5; i++)
					{
						num6 ^= b[i];
						for (int j = 0; j < 8; j++)
						{
							num6 = (num6 >> 1) ^ (uint)(((num6 & 1) == 1) ? (-306674912) : 0);
						}
					}
					Limits.Require((num6 ^ 0xFFFFFFFFu) == U32(b, num3 + 8 + num5));
					if (text == "IHDR")
					{
						Limits.Require(!flag3 && num5 == 13);
						checked
						{
							num = (int)U32(b, num3 + 8);
							num2 = (int)U32(b, num3 + 12);
							Dimensions(num, num2, logo);
							flag3 = true;
						}
						Limits.Require(b[num3 + 18] == 0 && b[num3 + 19] == 0 && b[num3 + 20] <= 1);
					}
					Limits.Require(text != "acTL" && text != "fcTL" && text != "fdAT");
					if (text == "IDAT")
					{
						flag = true;
					}
					num3 += num5 + 12;
					if (text == "IEND")
					{
						Limits.Require(num5 == 0 && num3 == b.Length);
						flag2 = true;
						break;
					}
				}
				Limits.Require(flag3 && flag && flag2);
			}
			else
			{
				Limits.Require(!logo && b[0] == byte.MaxValue && b[1] == 216 && b[^2] == byte.MaxValue && b[^1] == 217);
				bool flag4 = false;
				int num8;
				for (int k = 2; k < b.Length - 2; k += num8)
				{
					Limits.Require(b[k++] == byte.MaxValue);
					for (; k < b.Length && b[k] == byte.MaxValue; k++)
					{
					}
					Limits.Require(k + 2 < b.Length);
					int num7 = b[k++];
					num8 = U16(b, k);
					Limits.Require(num8 >= 2 && num8 <= b.Length - k);
					if (num7 == 192 || num7 == 194)
					{
						Limits.Require(num == 0 && num8 >= 8 && b[k + 2] == 8);
						num2 = U16(b, k + 3);
						num = U16(b, k + 5);
						Dimensions(num, num2, logo: false);
					}
					if (num7 == 218)
					{
						flag4 = true;
						break;
					}
				}
				Limits.Require(flag4 && num > 0);
			}
			return Tuple.Create(num, num2);
		}
	}
	internal sealed class Transfer
	{
		public readonly Asset Asset;

		public readonly byte[] Bytes;

		public int Offset { get; private set; }

		public bool Complete => Offset == Bytes.Length;

		public Transfer(Asset asset)
		{
			asset.Validate(logo: false);
			Limits.Require(asset.Length > 0);
			Asset = asset;
			Bytes = new byte[asset.Length];
		}

		public void Append(int offset, byte[] block)
		{
			Limits.Require(offset == Offset && block.Length == Math.Min(16384, Bytes.Length - Offset) && block.Length != 0);
			Buffer.BlockCopy(block, 0, Bytes, Offset, block.Length);
			Offset += block.Length;
		}
	}
	internal sealed class SendBudget
	{
		public const int BytesPerSecond = 4194304;

		public const int Burst = 262144;

		public const int QueueLimit = 524288;

		public int RateBytesPerSecond = 4194304;

		private double available = 262144.0;

		private double last;

		public bool TrySpend(double now, int bytes)
		{
			Limits.Require(bytes > 0 && bytes <= 16384);
			available = Math.Min(262144.0, available + Math.Max(0.0, now - last) * (double)RateBytesPerSecond);
			last = now;
			if (available < (double)bytes)
			{
				return false;
			}
			available -= bytes;
			return true;
		}
	}
	internal sealed class JoinGate
	{
		public const double DiscoveryTimeout = 10.0;

		public const double MaximumWait = 120.0;

		private readonly double started;

		private readonly double displaySeconds;

		private double visibleSince = -1.0;

		private bool presented;

		public bool Presented => presented;

		public bool Released { get; private set; }

		public JoinGate(double started, double displaySeconds = 5.0)
		{
			Limits.Require(displaySeconds >= 5.0 && displaySeconds <= 60.0);
			this.started = started;
			this.displaySeconds = displaySeconds;
		}

		public void Observe(bool fullyVisible, double now)
		{
			if (!fullyVisible)
			{
				visibleSince = -1.0;
			}
			else if (visibleSince < 0.0)
			{
				visibleSince = now;
			}
			else if (now - visibleSince >= displaySeconds)
			{
				presented = true;
			}
		}

		public bool Hold(double now, bool hasManifest, bool failed)
		{
			if (Released)
			{
				return false;
			}
			if (failed || now - started >= 120.0 + displaySeconds || (!hasManifest && now - started >= 10.0) || presented)
			{
				Released = true;
			}
			return !Released;
		}
	}
	internal sealed class DiskCache
	{
		private readonly string root;

		private static readonly object Gate = new object();

		public DiskCache(string root)
		{
			this.root = root;
		}

		private string PathFor(Asset a)
		{
			Limits.Require(Limits.IsHash(a.Hash));
			return Path.Combine(root, a.Hash + ".img");
		}

		public byte[]? Read(Asset a, bool logo)
		{
			lock (Gate)
			{
				try
				{
					a.Validate(logo);
					string text = PathFor(a);
					if (!Directory.Exists(root) || (File.GetAttributes(root) & FileAttributes.ReparsePoint) != FileAttributes.None)
					{
						return null;
					}
					foreach (string item in new string[1] { text }.Concat(Directory.EnumerateFiles(root, "*.img").Take(64)).Distinct())
					{
						try
						{
							FileInfo fileInfo = new FileInfo(item);
							if (!fileInfo.Exists || fileInfo.Length != a.Length || (fileInfo.Attributes & FileAttributes.ReparsePoint) != FileAttributes.None)
							{
								continue;
							}
							byte[] array = File.ReadAllBytes(item);
							a.Check(array, logo);
							try
							{
								if (item != text && !File.Exists(text))
								{
									File.Move(item, text);
								}
								File.SetLastWriteTimeUtc(text, DateTime.UtcNow);
							}
							catch (IOException)
							{
							}
							catch (UnauthorizedAccessException)
							{
							}
							return array;
						}
						catch (IOException)
						{
						}
						catch (UnauthorizedAccessException)
						{
						}
					}
					return null;
				}
				catch
				{
					return null;
				}
			}
		}

		public void Store(Asset a, byte[] bytes, bool logo)
		{
			a.Check(bytes, logo);
			lock (Gate)
			{
				Directory.CreateDirectory(root);
				Limits.Require((File.GetAttributes(root) & FileAttributes.ReparsePoint) == 0);
				string text = PathFor(a);
				string text2 = Path.Combine(root, Guid.NewGuid().ToString("N") + ".tmp");
				try
				{
					List<FileInfo> list = (from f in new DirectoryInfo(root).GetFiles()
						orderby f.LastWriteTimeUtc
						select f).ToList();
					long num = list.Sum((FileInfo f) => f.Length);
					int num2 = list.Count;
					foreach (FileInfo item in list)
					{
						if (num + bytes.Length <= 134217728 && num2 < 64)
						{
							break;
						}
						long length = item.Length;
						item.Delete();
						num -= length;
						num2--;
					}
					File.WriteAllBytes(text2, bytes);
					if (File.Exists(text))
					{
						File.Replace(text2, text, null);
					}
					else
					{
						File.Move(text2, text);
					}
				}
				finally
				{
					if (File.Exists(text2))
					{
						File.Delete(text2);
					}
				}
			}
		}
	}
	internal sealed class LoadingAnimationGraphic : MaskableGraphic
	{
		private LoadingAnimation style;

		private float clock;

		public void Animate(LoadingAnimation value, float time)
		{
			style = value;
			clock = time % 60f;
			((Graphic)this).SetVerticesDirty();
		}

		private static void Triangle(VertexHelper mesh, Vector2 a, Vector2 b, Vector2 c, Color color)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_0025: 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_002b: 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_0032: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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)
			int currentVertCount = mesh.currentVertCount;
			mesh.AddVert(Vector2.op_Implicit(a), Color32.op_Implicit(color), Vector4.op_Implicit(Vector2.zero));
			mesh.AddVert(Vector2.op_Implicit(b), Color32.op_Implicit(color), Vector4.op_Implicit(Vector2.zero));
			mesh.AddVert(Vector2.op_Implicit(c), Color32.op_Implicit(color), Vector4.op_Implicit(Vector2.zero));
			mesh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 2);
		}

		private static void Line(VertexHelper mesh, Vector2 a, Vector2 b, float width, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_0023: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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)
			Vector2 val = b - a;
			Vector2 normalized = ((Vector2)(ref val)).normalized;
			Vector2 val2 = new Vector2(0f - normalized.y, normalized.x) * width * 0.5f;
			Triangle(mesh, a - val2, a + val2, b + val2, color);
			Triangle(mesh, a - val2, b + val2, b - val2, color);
		}

		protected override void OnPopulateMesh(VertexHelper mesh)
		{
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_0396: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0410: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0508: Unknown result type (might be due to invalid IL or missing references)
			//IL_0521: Unknown result type (might be due to invalid IL or missing references)
			//IL_0539: Unknown result type (might be due to invalid IL or missing references)
			//IL_054b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0563: Unknown result type (might be due to invalid IL or missing references)
			//IL_0577: Unknown result type (might be due to invalid IL or missing references)
			//IL_058f: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_061c: Unknown result type (might be due to invalid IL or missing references)
			//IL_061e: Unknown result type (might be due to invalid IL or missing references)
			//IL_062a: Unknown result type (might be due to invalid IL or missing references)
			//IL_062f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0650: Unknown result type (might be due to invalid IL or missing references)
			mesh.Clear();
			if (style == LoadingAnimation.Off)
			{
				return;
			}
			if (style == LoadingAnimation.Hammer)
			{
				Color color = default(Color);
				((Color)(ref color))..ctor(0.55f, 0.62f, 0.67f);
				Color color2 = default(Color);
				((Color)(ref color2))..ctor(1f, 0.76f, 0.3f);
				Line(mesh, new Vector2(-23f, -7f), new Vector2(23f, -7f), 9f, new Color(0.06f, 0.07f, 0.08f));
				Line(mesh, new Vector2(-21f, -6f), new Vector2(21f, -6f), 5f, color);
				Triangle(mesh, new Vector2(-18f, -8f), new Vector2(18f, -8f), new Vector2(0f, -19f), color);
				Line(mesh, new Vector2(0f, -13f), new Vector2(0f, -24f), 10f, color);
				Line(mesh, new Vector2(-15f, -25f), new Vector2(15f, -25f), 5f, color);
				float num = clock % 1f;
				float num2 = 0.5f - 0.5f * Mathf.Cos(num * MathF.PI * 2f);
				float num3 = (15f + 70f * num2) * (MathF.PI / 180f);
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(18f, 5f);
				Vector2 val2 = val + new Vector2(0f - Mathf.Cos(num3), Mathf.Sin(num3)) * 29f;
				Line(mesh, val, val2, 7f, new Color(0.08f, 0.04f, 0.02f));
				Line(mesh, val, val2, 4f, new Color(0.68f, 0.38f, 0.15f));
				Vector2 val3 = new Vector2(Mathf.Sin(num3), Mathf.Cos(num3)) * 9f;
				Line(mesh, val2 - val3, val2 + val3, 12f, new Color(0.07f, 0.06f, 0.04f));
				Line(mesh, val2 - val3, val2 + val3, 8f, color2);
				if (num < 0.23f)
				{
					Vector2 val4 = default(Vector2);
					for (int i = 0; i < 5; i++)
					{
						float num4 = (0.15f + (float)i * 0.22f) * MathF.PI;
						((Vector2)(ref val4))..ctor(Mathf.Cos(num4), Mathf.Sin(num4));
						Vector2 val5 = new Vector2(-10f, 0f) + val4 * (5f + num * 90f);
						Line(mesh, val5, val5 + val4 * 4f, 2f, new Color(1f, 0.8f, 0.32f, 1f - num / 0.23f));
					}
				}
			}
			else
			{
				Line(mesh, new Vector2(-22f, -22f), new Vector2(22f, -12f), 9f, new Color(0.08f, 0.04f, 0.02f, 0.95f));
				Line(mesh, new Vector2(-22f, -12f), new Vector2(22f, -22f), 9f, new Color(0.08f, 0.04f, 0.02f, 0.95f));
				Line(mesh, new Vector2(-20f, -21f), new Vector2(20f, -13f), 4f, new Color(0.45f, 0.24f, 0.1f));
				Line(mesh, new Vector2(-20f, -13f), new Vector2(20f, -21f), 4f, new Color(0.62f, 0.34f, 0.13f));
				Vector2 val6 = default(Vector2);
				Vector2 val7 = default(Vector2);
				Vector2 val8 = default(Vector2);
				for (int j = 0; j < 3; j++)
				{
					float num5 = (j - 1) * 10;
					float num6 = Mathf.Sin(clock * 5f + (float)(j * 2)) * 4f;
					float num7 = (float)(13 + ((j == 1) ? 12 : 0)) + Mathf.Sin(clock * 7f + (float)j) * 3f;
					((Vector2)(ref val6))..ctor(num5 - 11f, -14f);
					((Vector2)(ref val7))..ctor(num5 + 10f, -14f);
					Triangle(mesh, val6 + new Vector2(-2f, -2f), val7 + new Vector2(2f, -2f), new Vector2(num5 + num6, num7 + 3f), new Color(0.15f, 0.04f, 0f, 0.85f));
					Triangle(mesh, val6, val7, new Vector2(num5 + num6, num7), new Color(1f, 0.3f, 0.035f, 0.95f));
					Triangle(mesh, new Vector2(num5 - 6f, -13f), new Vector2(num5 + 6f, -13f), new Vector2(num5 + num6 * 0.5f, num7 * 0.55f), new Color(1f, 0.76f, 0.16f));
					Triangle(mesh, new Vector2(num5 - 3f, -12f), new Vector2(num5 + 3f, -12f), new Vector2(num5, 1f), new Color(1f, 0.95f, 0.58f));
					float num8 = (clock * 0.55f + (float)j / 3f) % 1f;
					((Vector2)(ref val8))..ctor(Mathf.Sin(clock * 2f + (float)(j * 2)) * 10f, 4f + num8 * 28f);
					Line(mesh, val8, val8 + new Vector2(1f, 3f), 2f, new Color(1f, 0.65f, 0.16f, 1f - num8));
				}
			}
		}
	}
	internal sealed class LoadingView
	{
		private GameObject? root;

		private Texture2D? background;

		private Texture2D? logo;

		private RawImage? image;

		private RectTransform? logoRect;

		private Hud? owner;

		private CanvasGroup? fade;

		private RectTransform? content;

		private RectTransform? spinner;

		private LoadingAnimationGraphic? animation;

		public void Status(Hud hud, LoadingAnimation style)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			Cover(hud);
			if (style == LoadingAnimation.Off)
			{
				if ((Object)(object)spinner != (Object)null)
				{
					((Component)spinner).gameObject.SetActive(false);
				}
				return;
			}
			if ((Object)(object)spinner == (Object)null)
			{
				GameObject val = new GameObject("LoadingAnimation", new Type[2]
				{
					typeof(RectTransform),
					typeof(LoadingAnimationGraphic)
				});
				spinner = (RectTransform)val.transform;
				((Transform)spinner).SetParent(root.transform, false);
				RectTransform? obj = spinner;
				RectTransform? obj2 = spinner;
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(0.94f, 0.075f);
				obj2.anchorMax = val2;
				obj.anchorMin = val2;
				spinner.anchoredPosition = Vector2.zero;
				spinner.sizeDelta = new Vector2(100f, 70f);
				animation = val.GetComponent<LoadingAnimationGraphic>();
				((Graphic)animation).raycastTarget = false;
			}
			((Component)spinner).gameObject.SetActive(true);
			animation.Animate(style, Time.realtimeSinceStartup);
			((Transform)spinner).SetAsLastSibling();
		}

		public void Cover(Hud hud)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_0060: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)owner != (Object)(object)hud || (Object)(object)root == (Object)null)
			{
				Clear();
				root = new GameObject("ServerLoadingScreen", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				RectTransform val = (RectTransform)root.transform;
				((Transform)val).SetParent(((Component)hud.m_loadingScreen).transform, false);
				val.anchorMin = Vector2.zero;
				val.anchorMax = Vector2.one;
				val.offsetMin = Vector2.zero;
				val.offsetMax = Vector2.zero;
				Image component = root.GetComponent<Image>();
				((Graphic)component).color = Color.black;
				((Graphic)component).raycastTarget = false;
				owner = hud;
			}
			root.transform.SetAsLastSibling();
			if ((Object)(object)fade != (Object)null)
			{
				fade.alpha = 0f;
			}
		}

		public bool Show(Hud hud, Manifest m, byte[][] bytes, float opacity)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Expected O, but got Unknown
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0489: Unknown result type (might be due to invalid IL or missing references)
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			Cover(hud);
			if ((Object)(object)background == (Object)null)
			{
				background = Decode(bytes[0], m.Background);
				GameObject val = new GameObject("ServerContent", new Type[1] { typeof(RectTransform) });
				content = (RectTransform)val.transform;
				((Transform)content).SetParent(root.transform, false);
				content.anchorMin = Vector2.zero;
				content.anchorMax = Vector2.one;
				content.offsetMin = Vector2.zero;
				content.offsetMax = Vector2.zero;
				image = val.AddComponent<RawImage>();
				image.texture = (Texture)(object)background;
				((Graphic)image).raycastTarget = false;
				fade = val.AddComponent<CanvasGroup>();
				fade.alpha = opacity;
				fade.blocksRaycasts = false;
				fade.interactable = false;
				if (m.Welcome.Length > 0)
				{
					GameObject val2 = new GameObject("Welcome", new Type[2]
					{
						typeof(RectTransform),
						typeof(TextMeshProUGUI)
					});
					RectTransform val3 = (RectTransform)val2.transform;
					((Transform)val3).SetParent((Transform)(object)content, false);
					val3.anchorMin = new Vector2(0.1f, 0.16f);
					val3.anchorMax = new Vector2(0.9f, 0.26f);
					val3.offsetMin = Vector2.zero;
					val3.offsetMax = Vector2.zero;
					TextMeshProUGUI component = val2.GetComponent<TextMeshProUGUI>();
					((TMP_Text)component).font = hud.m_loadingTip.font;
					((TMP_Text)component).text = m.Welcome;
					((TMP_Text)component).richText = false;
					((Graphic)component).raycastTarget = false;
					((TMP_Text)component).alignment = (TextAlignmentOptions)514;
					((TMP_Text)component).enableAutoSizing = true;
					((TMP_Text)component).fontSizeMin = 12f;
					((TMP_Text)component).fontSizeMax = 32f;
					((Graphic)component).color = Color.white;
				}
				owner = hud;
			}
			if ((Object)(object)logo == (Object)null && m.Logo.Length > 0 && bytes[1] != null)
			{
				logo = Decode(bytes[1], m.Logo);
				GameObject val4 = new GameObject("ServerLogo", new Type[2]
				{
					typeof(RectTransform),
					typeof(RawImage)
				});
				logoRect = (RectTransform)val4.transform;
				((Transform)logoRect).SetParent((Transform)(object)content, false);
				RawImage component2 = val4.GetComponent<RawImage>();
				component2.texture = (Texture)(object)logo;
				((Graphic)component2).raycastTarget = false;
			}
			fade.alpha = opacity;
			if ((Object)(object)spinner != (Object)null)
			{
				((Transform)spinner).SetAsLastSibling();
			}
			Rect rect = ((RectTransform)root.transform).rect;
			if (((Rect)(ref rect)).width <= 0f || ((Rect)(ref rect)).height <= 0f)
			{
				return false;
			}
			float num = ((Rect)(ref rect)).width / ((Rect)(ref rect)).height;
			float num2 = (float)((Texture)background).width / (float)((Texture)background).height;
			float num3 = Mathf.Min(1f, num / num2);
			float num4 = Mathf.Min(1f, num2 / num);
			image.uvRect = new Rect((1f - num3) / 2f, (1f - num4) / 2f, num3, num4);
			if ((Object)(object)logoRect != (Object)null && (Object)(object)logo != (Object)null)
			{
				float num5 = ((Rect)(ref rect)).width * m.LogoWidth;
				float num6 = num5 * (float)((Texture)logo).height / (float)((Texture)logo).width;
				float num7 = Mathf.Min(1f, ((Rect)(ref rect)).height * 0.8f / num6);
				num5 *= num7;
				num6 *= num7;
				float num8 = Mathf.Clamp(m.LogoX, num5 / ((Rect)(ref rect)).width / 2f, 1f - num5 / ((Rect)(ref rect)).width / 2f);
				float num9 = Mathf.Clamp(m.LogoY, num6 / ((Rect)(ref rect)).height / 2f, 1f - num6 / ((Rect)(ref rect)).height / 2f);
				RectTransform? obj = logoRect;
				RectTransform? obj2 = logoRect;
				Vector2 val5 = default(Vector2);
				((Vector2)(ref val5))..ctor(num8, num9);
				obj2.anchorMax = val5;
				obj.anchorMin = val5;
				logoRect.anchoredPosition = Vector2.zero;
				logoRect.sizeDelta = new Vector2(num5, num6);
			}
			return root.activeInHierarchy;
		}

		private static Texture2D Decode(byte[] bytes, Asset a)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			try
			{
				Limits.Require(ImageConversion.LoadImage(val, bytes, true));
				Limits.Require(((Texture)val).width == a.Width && ((Texture)val).height == a.Height);
				((Texture)val).wrapMode = (TextureWrapMode)1;
				return val;
			}
			catch
			{
				Object.Destroy((Object)(object)val);
				throw;
			}
		}

		public void Clear()
		{
			if ((Object)(object)root != (Object)null)
			{
				root.SetActive(false);
				Object.Destroy((Object)(object)root);
			}
			if ((Object)(object)background != (Object)null)
			{
				Object.Destroy((Object)(object)background);
			}
			if ((Object)(object)logo != (Object)null)
			{
				Object.Destroy((Object)(object)logo);
			}
			root = null;
			background = null;
			logo = null;
			image = null;
			logoRect = null;
			owner = null;
			fade = null;
			content = null;
			spinner = null;
			animation = null;
		}
	}
	[BepInPlugin("local.serverloadingscreen", "Server Loading Screen", "1.0.8")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class Snapshot
		{
			public Manifest Manifest;

			public byte[][] Bytes;
		}

		private sealed class ServerSession
		{
			public ZNetPeer Peer;

			public Guid Nonce;

			public Task<Snapshot>? Loading;

			public Snapshot? Snapshot;

			public bool Asked;

			public bool SentManifest;

			public bool Failed;

			public int Slot = -1;

			public readonly int[] Offsets = new int[2];

			public float Started;

			public float Last;

			public SteamUploadBoost? Boost;
		}

		private sealed class ClientSession
		{
			public ZNetPeer Peer;

			public Guid Nonce = Guid.NewGuid();

			public Manifest? Manifest;

			public Task<byte[]?>? Work;

			public readonly byte[][] Bytes = new byte[2][];

			public bool ReadingCache;

			public Transfer? Transfer;

			public int Slot;

			public bool Failed;

			public bool Finished;

			public bool Join = true;

			public string CacheError = "";

			public bool DisplayLogged;

			public JoinGate Gate;

			public float Started;

			public float Last;
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class NewConnection
		{
			private static void Postfix(ZNetPeer peer)
			{
				if (!((Object)(object)self == (Object)null))
				{
					peer.m_rpc.Register<ZPackage>("SLS_V4", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage pkg)
					{
						self?.Receive(peer, rpc, pkg);
					});
					if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
					{
						self.servers[peer.m_rpc] = new ServerSession
						{
							Peer = peer
						};
					}
				}
			}
		}

		[HarmonyPatch(typeof(Hud), "UpdateBlackScreen")]
		private static class LoadingScreen
		{
			private static void Postfix(Hud __instance)
			{
				if ((Object)(object)self == (Object)null)
				{
					return;
				}
				try
				{
					self.Draw(__instance);
				}
				catch
				{
					self.FailClient("Loading UI unavailable; keeping the game's loading screen.");
				}
			}
		}

		[HarmonyPatch(typeof(Game), "UpdateRespawn")]
		private static class InitialSpawn
		{
			private static bool Prefix(Game __instance, bool ___m_firstSpawn)
			{
				if ((Object)(object)self == (Object)null || !___m_firstSpawn || !__instance.WaitingForRespawn() || (Object)(object)Player.m_localPlayer != (Object)null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
				{
					return true;
				}
				try
				{
					return !self.HoldInitialSpawn();
				}
				catch
				{
					self.FailClient("Initial loading wait failed; continuing normal spawn.");
					return true;
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnDestroy")]
		private static class NetworkDestroyed
		{
			private static void Prefix()
			{
				if ((Object)(object)self != (Object)null)
				{
					self.ResetClient();
					self.ClearServerSessions();
				}
			}
		}

		public const string Id = "local.serverloadingscreen";

		private const string RpcName = "SLS_V4";

		private static Plugin? self;

		private Harmony? harmony;

		private ConfigEntry<string> serverId;

		private ConfigEntry<string> welcome;

		private ConfigEntry<string> backgroundFile;

		private ConfigEntry<string> logoFile;

		private ConfigEntry<float> logoWidth;

		private ConfigEntry<float> logoX;

		private ConfigEntry<float> logoY;

		private ConfigEntry<float> minimumDisplaySeconds;

		private ConfigEntry<int> transferRateKiB;

		private ConfigEntry<LoadingAnimation> loadingAnimation;

		private readonly Dictionary<ZRpc, ServerSession> servers = new Dictionary<ZRpc, ServerSession>();

		private ClientSession? client;

		private readonly LoadingView view = new LoadingView();

		private DiskCache cache;

		private string assetFolder = "";

		private readonly SendBudget sendBudget = new SendBudget();

		private int firstSender;

		private void Awake()
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Expected O, but got Unknown
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Expected O, but got Unknown
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Expected O, but got Unknown
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Expected O, but got Unknown
			self = this;
			serverId = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "ServerId", Guid.NewGuid().ToString("N"), "Automatische öffentliche Cache-Kennung. Beibehalten; hier kein Passwort eintragen.");
			if (string.IsNullOrWhiteSpace(serverId.Value))
			{
				serverId.Value = Guid.NewGuid().ToString("N");
				((BaseUnityPlugin)this).Config.Save();
			}
			welcome = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "Welcome", "", "Nur Server: Begrüßung, maximal 200 Zeichen ohne Zeilenumbrüche. Clients übernehmen den Serverwert.");
			backgroundFile = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "BackgroundFile", "", "Nur Server: Dateiname im Ordner BepInEx/config/ServerLoadingScreen. Leer = background.png, background.jpg oder background.jpeg automatisch wählen. Keine Pfade oder URLs.");
			logoFile = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "LogoFile", "logo.png", "Nur Server: PNG-Dateiname im selben Bildordner. Leer = kein Logo. Clients laden die Bilder automatisch.");
			logoWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "LogoWidth", 0.3f, new ConfigDescription("Nur Server: Logobreite als Anteil der Bildschirmbreite.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 0.8f), Array.Empty<object>()));
			logoX = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "LogoX", 0.5f, new ConfigDescription("Nur Server: Logo-Mittelpunkt horizontal, 0 = links, 1 = rechts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			logoY = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "LogoY", 0.65f, new ConfigDescription("Nur Server: Logo-Mittelpunkt vertikal, 0 = unten, 1 = oben.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			minimumDisplaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "MinimumDisplaySeconds", 5f, new ConfigDescription("Nur Server: mindestens 5 bis 60 Sekunden vollständig anzeigen. Bei längerem Weltladen bleibt das Bild bis zum Spieleinstieg sichtbar. Clients übernehmen diesen Wert automatisch.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>()));
			transferRateKiB = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "TransferRateKiB", 4096, new ConfigDescription("Nur Server: Bildtransfer-Budget insgesamt und temporäres Steam-Sendelimit pro anfragender Verbindung in KiB/s, bis 8192. 0 = Steam unverändert, Modbudget 1024. Bestehende Werte bleiben erhalten. Nach Abschluss Steam-Limit zurücksetzen. Keine garantierte Geschwindigkeit.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 8192), Array.Empty<object>()));
			loadingAnimation = ((BaseUnityPlugin)this).Config.Bind<LoadingAnimation>("Layout", "LoadingAnimation", LoadingAnimation.Hammer, "Nur Server: Off = aus, Hammer = Schmiedehammer, Campfire = Lagerfeuer. Nur der Server entscheidet.");
			assetFolder = Path.Combine(Paths.ConfigPath, "ServerLoadingScreen");
			cache = new DiskCache(Path.Combine(Paths.CachePath, "ServerLoadingScreen-v1"));
			Directory.CreateDirectory(assetFolder);
			harmony = new Harmony("local.serverloadingscreen");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Ready: version 1.0.8, protocol v4. Full loading-screen replacement; server-configured minimum display time.");
		}

		private bool HoldInitialSpawn()
		{
			if (client == null)
			{
				Update();
			}
			ClientSession clientSession = client;
			if (clientSession == null || !clientSession.Join || !Current(clientSession))
			{
				return false;
			}
			bool released = clientSession.Gate.Released;
			bool num = clientSession.Gate.Hold(Time.realtimeSinceStartup, clientSession.Manifest != null, clientSession.Failed);
			if (!num && !released)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)(clientSession.Gate.Presented ? "Loading screen shown; releasing initial spawn." : "Loading wait ended without a complete display; continuing normal spawn."));
				if (!clientSession.Gate.Presented)
				{
					clientSession.Join = false;
					view.Clear();
				}
			}
			return num;
		}

		private bool Current(ClientSession c)
		{
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && ZNet.instance.GetServerPeer() == c.Peer && c.Peer.IsReady())
			{
				return c.Peer.m_rpc.IsConnected();
			}
			return false;
		}

		private void Update()
		{
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					ResetClient();
					ClearServerSessions();
					return;
				}
				if (ZNet.instance.IsServer())
				{
					TickServer();
					return;
				}
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer == null || !serverPeer.IsReady() || !serverPeer.m_rpc.IsConnected())
				{
					if (client != null)
					{
						ResetClient();
					}
					return;
				}
				if (client == null || serverPeer != client.Peer)
				{
					ResetClient();
					client = new ClientSession
					{
						Peer = serverPeer,
						Started = Time.realtimeSinceStartup,
						Last = Time.realtimeSinceStartup,
						Gate = new JoinGate(Time.realtimeSinceStartup)
					};
					Send(serverPeer.m_rpc, Wire.Pack(1, client.Nonce));
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Requesting loading-screen manifest from confirmed server.");
				}
				TickClient(client);
			}
			catch
			{
				if (client != null)
				{
					FailClient("Loading-screen processing failed; using default.");
				}
			}
		}

		private void Receive(ZNetPeer peer, ZRpc rpc, ZPackage pkg)
		{
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null || !peer.IsReady() || !rpc.IsConnected())
				{
					return;
				}
				if (pkg.Size() > 16512 || pkg.Size() < 22)
				{
					throw new InvalidDataException();
				}
				byte kind;
				Guid nonce;
				using BinaryReader binaryReader = Wire.Open(pkg.GetArray(), out kind, out nonce);
				if (ZNet.instance.IsServer())
				{
					if (!servers.TryGetValue(rpc, out ServerSession value) || value.Failed)
					{
						return;
					}
					switch (kind)
					{
					case 1:
					{
						Wire.End(binaryReader);
						if (value.Asked)
						{
							break;
						}
						value.Asked = true;
						if (servers.Values.Count((ServerSession x) => x.Loading != null || x.Snapshot != null) >= 4)
						{
							value.Failed = true;
							break;
						}
						value.Nonce = nonce;
						value.Started = (value.Last = Time.realtimeSinceStartup);
						((BaseUnityPlugin)this).Config.Reload();
						Manifest manifest = new Manifest
						{
							ServerId = Guid.Parse(serverId.Value),
							Welcome = welcome.Value,
							LogoWidth = logoWidth.Value,
							LogoX = logoX.Value,
							LogoY = logoY.Value,
							MinimumDisplaySeconds = minimumDisplaySeconds.Value,
							Animation = loadingAnimation.Value
						};
						string backgroundName = backgroundFile.Value;
						string logoName = logoFile.Value;
						value.Loading = Task.Run(() => ReadSnapshot(assetFolder, manifest, backgroundName, logoName));
						break;
					}
					case 3:
						if (value.SentManifest && nonce == value.Nonce && value.Snapshot != null)
						{
							int num = binaryReader.ReadByte();
							int num2 = binaryReader.ReadInt32();
							Wire.End(binaryReader);
							Limits.Require(num <= 1 && num2 == value.Offsets[num] && num2 < value.Snapshot.Manifest.At(num).Length && value.Slot == -1);
							value.Slot = num;
							value.Last = Time.realtimeSinceStartup;
							if (value.Boost == null)
							{
								value.Boost = SteamUploadBoost.TryCreate(peer.m_socket, transferRateKiB.Value, delegate(string message)
								{
									((BaseUnityPlugin)this).Logger.LogInfo((object)message);
								});
							}
							break;
						}
						goto default;
					default:
						if (kind == 5 && value.SentManifest && nonce == value.Nonce)
						{
							Wire.End(binaryReader);
							StopServerSession(value);
						}
						break;
					}
					return;
				}
				ClientSession c = client;
				if (c == null || c.Failed || c.Finished || c.Peer != peer || !Current(c) || nonce != c.Nonce)
				{
					return;
				}
				if (kind == 2 && c.Manifest == null)
				{
					c.Manifest = Wire.ReadManifest(binaryReader);
					c.Last = Time.realtimeSinceStartup;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Received loading-screen manifest: background " + c.Manifest.Background.Length + " bytes, logo " + c.Manifest.Logo.Length + " bytes."));
					if (c.Manifest.Background.Length == 0)
					{
						FailClient("Server has no valid loading-screen assets.");
						return;
					}
					c.Gate = new JoinGate(c.Started, c.Manifest.MinimumDisplaySeconds);
					NextAsset(c);
				}
				else
				{
					if (kind != 4 || c.Transfer == null)
					{
						return;
					}
					int slot = binaryReader.ReadByte();
					int offset = binaryReader.ReadInt32();
					int num3 = binaryReader.ReadInt32();
					Limits.Require(slot == c.Slot && num3 > 0 && num3 <= 16384);
					byte[] block = Wire.Exact(binaryReader, num3);
					Wire.End(binaryReader);
					c.Transfer.Append(offset, block);
					c.Last = Time.realtimeSinceStartup;
					if (!c.Transfer.Complete)
					{
						return;
					}
					byte[] data = c.Transfer.Bytes;
					c.Transfer = null;
					Asset asset = c.Manifest.At(slot);
					c.ReadingCache = false;
					c.Work = Task.Run(delegate
					{
						asset.Check(data, slot == 1);
						try
						{
							cache.Store(asset, data, slot == 1);
						}
						catch (IOException ex)
						{
							c.CacheError = ex.GetType().Name;
						}
						catch (UnauthorizedAccessException ex2)
						{
							c.CacheError = ex2.GetType().Name;
						}
						return data;
					});
				}
			}
			catch
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					if (servers.TryGetValue(rpc, out ServerSession value2))
					{
						StopServerSession(value2);
					}
				}
				else if (client != null && client.Peer == peer)
				{
					FailClient("Invalid loading-screen response; using default.");
				}
			}
		}

		private static Snapshot ReadSnapshot(string folder, Manifest m, string backgroundName, string logoName)
		{
			try
			{
				if (backgroundName.Length > 0)
				{
					AssetNames.Validate(backgroundName, logo: false);
				}
				if (logoName.Length > 0)
				{
					AssetNames.Validate(logoName, logo: true);
				}
				byte[] array = Read((backgroundName.Length == 0) ? "background.png" : backgroundName, 8388608);
				if (backgroundName.Length == 0 && array.Length == 0)
				{
					array = Read("background.jpg", 8388608);
				}
				if (backgroundName.Length == 0 && array.Length == 0)
				{
					array = Read("background.jpeg", 8388608);
				}
				byte[] array2 = ((logoName.Length == 0) ? Array.Empty<byte>() : Read(logoName, 2097152));
				m.Background = Asset.From(array, logo: false);
				m.Logo = Asset.From(array2, logo: true);
				m.Validate();
				Snapshot snapshot = new Snapshot();
				snapshot.Manifest = m;
				snapshot.Bytes = new byte[2][] { array, array2 };
				return snapshot;
			}
			catch
			{
				Manifest manifest = new Manifest
				{
					ServerId = m.ServerId
				};
				manifest.Validate();
				Snapshot snapshot2 = new Snapshot();
				snapshot2.Manifest = manifest;
				snapshot2.Bytes = new byte[2][]
				{
					Array.Empty<byte>(),
					Array.Empty<byte>()
				};
				return snapshot2;
			}
			byte[] Read(string name, int max)
			{
				string text = Path.Combine(folder, name);
				if (!File.Exists(text))
				{
					return Array.Empty<byte>();
				}
				FileInfo fileInfo = new FileInfo(text);
				Limits.Require(fileInfo.Length > 0 && fileInfo.Length <= max);
				using FileStream fileStream = File.Open(text, FileMode.Open, FileAccess.Read, FileShare.Read);
				Limits.Require(fileStream.Length == fileInfo.Length);
				byte[] array3 = new byte[(int)fileStream.Length];
				int num;
				for (int i = 0; i < array3.Length; i += num)
				{
					num = fileStream.Read(array3, i, array3.Length - i);
					Limits.Require(num > 0);
				}
				return array3;
			}
		}

		private void TickServer()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			sendBudget.RateBytesPerSecond = ((transferRateKiB.Value <= 0) ? 1024 : Math.Min(8192, transferRateKiB.Value)) * 1024;
			KeyValuePair<ZRpc, ServerSession>[] array = servers.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<ZRpc, ServerSession> keyValuePair = array[(i + firstSender) % array.Length];
				ServerSession s = keyValuePair.Value;
				if (!s.Peer.m_rpc.IsConnected())
				{
					StopServerSession(s);
					servers.Remove(keyValuePair.Key);
				}
				else
				{
					if (s.Failed || !s.Asked)
					{
						continue;
					}
					if (realtimeSinceStartup - s.Started > 180f || realtimeSinceStartup - s.Last > 45f)
					{
						StopServerSession(s);
						continue;
					}
					if (s.Loading != null && s.Loading.IsCompleted)
					{
						if (s.Loading.IsFaulted || s.Loading.IsCanceled)
						{
							s.Failed = true;
							s.Loading = null;
							continue;
						}
						s.Snapshot = s.Loading.Result;
						s.Loading = null;
					}
					if (s.Snapshot == null || s.Peer.m_socket.GetSendQueueSize() > 507904)
					{
						continue;
					}
					if (!s.SentManifest)
					{
						Send(keyValuePair.Key, Wire.Pack(2, s.Nonce, delegate(BinaryWriter w)
						{
							Wire.WriteManifest(w, s.Snapshot.Manifest);
						}));
						s.SentManifest = true;
						((BaseUnityPlugin)this).Logger.LogInfo((object)((s.Snapshot.Manifest.Background.Length == 0) ? "Sent disabled manifest (missing/invalid assets)." : "Sent loading-screen manifest."));
						if (s.Snapshot.Manifest.Background.Length == 0)
						{
							s.Snapshot = null;
							s.Failed = true;
							continue;
						}
					}
					for (int num = 0; num < 16; num++)
					{
						if (s.Slot < 0)
						{
							break;
						}
						int slot = s.Slot;
						int offset = s.Offsets[slot];
						byte[] data = s.Snapshot.Bytes[slot];
						int length = Math.Min(16384, data.Length - offset);
						if (s.Peer.m_socket.GetSendQueueSize() > 524288 - length || !sendBudget.TrySpend(realtimeSinceStartup, length))
						{
							break;
						}
						Send(keyValuePair.Key, Wire.Pack(4, s.Nonce, delegate(BinaryWriter w)
						{
							w.Write((byte)slot);
							w.Write(offset);
							w.Write(length);
							w.Write(data, offset, length);
						}));
						s.Offsets[slot] += length;
						s.Last = realtimeSinceStartup;
						if (s.Offsets[slot] == data.Length)
						{
							s.Slot = -1;
						}
					}
				}
			}
			firstSender = ((array.Length != 0) ? ((firstSender + 1) % array.Length) : 0);
		}

		private void TickClient(ClientSession c)
		{
			if (c.Failed || c.Finished)
			{
				return;
			}
			if (Time.realtimeSinceStartup - c.Last > 45f || Time.realtimeSinceStartup - c.Started > 180f)
			{
				FailClient("Loading-screen transfer timed out; using default.");
			}
			else
			{
				if (c.Work == null || !c.Work.IsCompleted)
				{
					return;
				}
				if (c.Work.IsFaulted || c.Work.IsCanceled)
				{
					FailClient("Image verification/cache operation failed; using default.");
					return;
				}
				byte[] result = c.Work.Result;
				c.Work = null;
				if (c.ReadingCache && result == null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Downloading loading-screen asset " + c.Slot + " (streaming)."));
					c.Transfer = new Transfer(c.Manifest.At(c.Slot));
					Request(c);
					return;
				}
				c.Bytes[c.Slot] = result;
				c.Last = Time.realtimeSinceStartup;
				((BaseUnityPlugin)this).Logger.LogInfo((object)((c.ReadingCache ? "Cache ready" : "Download verified") + " (asset " + c.Slot + ")."));
				if (c.CacheError.Length > 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Cache write failed (" + c.CacheError + "); current image remains usable."));
					c.CacheError = "";
				}
				c.Slot++;
				NextAsset(c);
			}
		}

		private void NextAsset(ClientSession c)
		{
			if (c.Slot == 2)
			{
				Send(c.Peer.m_rpc, Wire.Pack(5, c.Nonce));
				c.Finished = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Loading-screen assets ready after " + (Time.realtimeSinceStartup - c.Started).ToString("F1") + " seconds."));
				if (!c.Join)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Loading already ended; verified cached images will be reused on the next join.");
				}
			}
			else
			{
				int slot = c.Slot;
				Asset asset = c.Manifest.At(slot);
				c.ReadingCache = true;
				c.Work = Task.Run(() => (asset.Length != 0) ? cache.Read(asset, slot == 1) : Array.Empty<byte>());
			}
		}

		private static void Request(ClientSession c)
		{
			Send(c.Peer.m_rpc, Wire.Pack(3, c.Nonce, delegate(BinaryWriter w)
			{
				w.Write((byte)c.Slot);
				w.Write(c.Transfer.Offset);
			}));
		}

		private static void Send(ZRpc rpc, byte[] bytes)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			rpc.Invoke("SLS_V4", new object[1] { (object)new ZPackage(bytes) });
		}

		private void Draw(Hud hud)
		{
			ClientSession clientSession = client;
			if (clientSession == null)
			{
				if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && (Object)(object)Player.m_localPlayer == (Object)null && (Object)(object)Game.instance != (Object)null && Game.instance.WaitingForRespawn() && (Object)(object)hud.m_loadingScreen != (Object)null)
				{
					view.Cover(hud);
					view.Status(hud, LoadingAnimation.Off);
				}
				else
				{
					view.Clear();
				}
				return;
			}
			if (clientSession.Failed || !Current(clientSession) || !clientSession.Join)
			{
				view.Clear();
				return;
			}
			if ((Object)(object)hud.m_loadingScreen != (Object)null)
			{
				view.Status(hud, clientSession.Manifest?.Animation ?? LoadingAnimation.Off);
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)Game.instance != (Object)null && !Game.instance.WaitingForRespawn())
			{
				if ((Object)(object)hud.m_loadingScreen != (Object)null && ((Component)hud.m_loadingScreen).gameObject.activeInHierarchy && hud.m_loadingScreen.alpha > 0f)
				{
					if (clientSession.Bytes[0] != null)
					{
						view.Show(hud, clientSession.Manifest, clientSession.Bytes, 1f);
					}
					else
					{
						view.Cover(hud);
					}
				}
				else
				{
					clientSession.Join = false;
					view.Clear();
				}
				return;
			}
			if ((Object)(object)hud.m_loadingScreen != (Object)null)
			{
				view.Cover(hud);
			}
			if (clientSession.Bytes[0] == null || (Object)(object)hud.m_loadingScreen == (Object)null || !((Component)hud.m_loadingScreen).gameObject.activeInHierarchy)
			{
				clientSession.Gate.Observe(fullyVisible: false, Time.realtimeSinceStartup);
				return;
			}
			bool flag = view.Show(hud, clientSession.Manifest, clientSession.Bytes, 1f) && hud.m_loadingScreen.alpha >= 0.99f;
			clientSession.Gate.Observe(flag && clientSession.Finished, Time.realtimeSinceStartup);
			if (flag && clientSession.Finished && !clientSession.DisplayLogged)
			{
				clientSession.DisplayLogged = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Server loading screen visible; minimum " + clientSession.Manifest.MinimumDisplaySeconds + " seconds, then retained until loading ends."));
			}
		}

		private void FailClient(string reason)
		{
			if (client != null)
			{
				client.Failed = true;
				client.Transfer = null;
				client.Work = null;
				Array.Clear(client.Bytes, 0, client.Bytes.Length);
			}
			view.Clear();
			((BaseUnityPlugin)this).Logger.LogWarning((object)reason);
		}

		private void ResetClient()
		{
			client = null;
			view.Clear();
		}

		private static void StopServerSession(ServerSession session)
		{
			session.Boost?.Dispose();
			session.Boost = null;
			session.Failed = true;
			session.Loading = null;
			session.Snapshot = null;
		}

		private void ClearServerSessions()
		{
			foreach (ServerSession value in servers.Values)
			{
				StopServerSession(value);
			}
			servers.Clear();
		}

		private void OnDestroy()
		{
			ResetClient();
			ClearServerSessions();
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			self = null;
		}
	}
	internal sealed class SteamUploadBoost : IDisposable
	{
		private static readonly FieldInfo? Connection = typeof(ZSteamSocket).GetField("m_con", BindingFlags.Instance | BindingFlags.NonPublic);

		private const ESteamNetworkingConfigValue Setting = (ESteamNetworkingConfigValue)11;

		private const ESteamNetworkingConfigScope Scope = (ESteamNetworkingConfigScope)4;

		private const ESteamNetworkingConfigDataType Int32Type = (ESteamNetworkingConfigDataType)1;

		private readonly IntPtr handle;

		private readonly int original;

		private readonly int applied;

		private readonly bool inherited;

		private bool disposed;

		private SteamUploadBoost(IntPtr handle, int original, int applied, bool inherited)
		{
			this.handle = handle;
			this.original = original;
			this.applied = applied;
			this.inherited = inherited;
		}

		private static bool Read(IntPtr handle, out int value, out bool inherited)
		{
			//IL_0013: 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: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Invalid comparison between Unknown and I4
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			IntPtr intPtr = Marshal.AllocHGlobal(4);
			try
			{
				ulong num = 4uL;
				ESteamNetworkingConfigDataType val = default(ESteamNetworkingConfigDataType);
				ESteamNetworkingGetConfigValueResult configValue = SteamGameServerNetworkingUtils.GetConfigValue((ESteamNetworkingConfigValue)11, (ESteamNetworkingConfigScope)4, handle, ref val, intPtr, ref num);
				inherited = (int)configValue == 2;
				value = 0;
				if ((!inherited && (int)configValue != 1) || (int)val != 1 || num != 4)
				{
					return false;
				}
				value = Marshal.ReadInt32(intPtr);
				return true;
			}
			finally
			{
				Marshal.FreeHGlobal(intPtr);
			}
		}

		private static bool Set(IntPtr handle, int? value)
		{
			if (!value.HasValue)
			{
				return SteamGameServerNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)11, (ESteamNetworkingConfigScope)4, handle, (ESteamNetworkingConfigDataType)1, IntPtr.Zero);
			}
			IntPtr intPtr = Marshal.AllocHGlobal(4);
			try
			{
				Marshal.WriteInt32(intPtr, value.Value);
				return SteamGameServerNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)11, (ESteamNetworkingConfigScope)4, handle, (ESteamNetworkingConfigDataType)1, intPtr);
			}
			finally
			{
				Marshal.FreeHGlobal(intPtr);
			}
		}

		public static SteamUploadBoost? TryCreate(object socket, int rateKiB, Action<string> log)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (rateKiB <= 0 || !(socket is ZSteamSocket) || Connection == null)
			{
				return null;
			}
			try
			{
				HSteamNetConnection val = (HSteamNetConnection)Connection.GetValue(socket);
				IntPtr intPtr = new IntPtr(val.m_HSteamNetConnection);
				int num = Math.Min(rateKiB, 8192) * 1024;
				if (!Read(intPtr, out var value, out var flag) || value <= 0 || value >= num)
				{
					return null;
				}
				SteamUploadBoost result = new SteamUploadBoost(intPtr, value, num, flag);
				if (!Set(intPtr, num))
				{
					return null;
				}
				log("Temporary Steam upload limit: " + value + " -> " + num + " bytes/s for this image transfer.");
				return result;
			}
			catch
			{
				log("Steam upload limit unchanged; continuing with the existing connection settings.");
				return null;
			}
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			disposed = true;
			try
			{
				if (Read(handle, out var value, out var flag) && !flag && value == applied)
				{
					Set(handle, inherited ? ((int?)null) : new int?(original));
				}
			}
			catch
			{
			}
		}
	}
}