Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ServerLoadingScreen v1.0.8
plugins/ServerLoadingScreen.dll
Decompiled 3 weeks agousing 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 { } } } }