Decompiled source of ForgeCore v0.1.0

ForgeCore.Once.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.Json;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ForgeCore.Once")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+66ec6b43e1a1603bac84a4b9a226674300b2fc7d")]
[assembly: AssemblyProduct("ForgeCore.Once")]
[assembly: AssemblyTitle("ForgeCore.Once")]
[assembly: AssemblyVersion("0.1.0.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 ForgeCore
{
	public static class Fnv
	{
		public const uint Offset32 = 2166136261u;

		public const uint Prime32 = 16777619u;

		public const ulong Offset64 = 14695981039346656037uL;

		public const ulong Prime64 = 1099511628211uL;

		public static uint Mix32(uint hash, byte value)
		{
			return (hash ^ value) * 16777619;
		}

		public static ulong Mix64(ulong hash, byte value)
		{
			return (hash ^ value) * 1099511628211L;
		}

		public static uint Hash32(ReadOnlySpan<byte> bytes)
		{
			uint num = 2166136261u;
			ReadOnlySpan<byte> readOnlySpan = bytes;
			for (int i = 0; i < readOnlySpan.Length; i++)
			{
				byte value = readOnlySpan[i];
				num = Mix32(num, value);
			}
			return num;
		}

		public static ulong Hash64(ReadOnlySpan<byte> bytes)
		{
			ulong num = 14695981039346656037uL;
			ReadOnlySpan<byte> readOnlySpan = bytes;
			for (int i = 0; i < readOnlySpan.Length; i++)
			{
				byte value = readOnlySpan[i];
				num = Mix64(num, value);
			}
			return num;
		}

		public static uint Hash32(string text)
		{
			return Hash32(Encoding.UTF8.GetBytes(text));
		}

		public static ulong Hash64(string text)
		{
			return Hash64(Encoding.UTF8.GetBytes(text));
		}
	}
	public sealed class OnceReporter
	{
		private readonly Action<string>? _sink;

		private readonly Dictionary<string, long> _counts = new Dictionary<string, long>(StringComparer.Ordinal);

		private readonly object _gate = new object();

		public OnceReporter(Action<string> sink)
		{
			_sink = sink ?? throw new ArgumentNullException("sink");
		}

		public OnceReporter()
		{
		}

		public bool Report(string key, string message)
		{
			long count = Tick(key);
			if (!Reports(count))
			{
				return false;
			}
			Write(count, message);
			return true;
		}

		public bool Report<TState>(string key, TState state, Func<TState, string> message)
		{
			long count = Tick(key);
			if (!Reports(count))
			{
				return false;
			}
			Write(count, message(state));
			return true;
		}

		public bool ShouldReport(string key)
		{
			return Reports(Tick(key));
		}

		private long Tick(string key)
		{
			lock (_gate)
			{
				_counts.TryGetValue(key, out var value);
				return _counts[key] = value + 1;
			}
		}

		private void Write(long count, string message)
		{
			if (_sink == null)
			{
				throw new InvalidOperationException("This OnceReporter has no output target; use ShouldReport.");
			}
			_sink((count == 1) ? message : (message + " (" + count.ToString(CultureInfo.InvariantCulture) + " times)"));
		}

		public long Count(string key)
		{
			lock (_gate)
			{
				long value;
				return _counts.TryGetValue(key, out value) ? value : 0;
			}
		}

		public void Reset()
		{
			lock (_gate)
			{
				_counts.Clear();
			}
		}

		private static bool Reports(long count)
		{
			if (count <= 0)
			{
				return false;
			}
			while (count % 10 == 0L)
			{
				count /= 10;
			}
			return count == 1;
		}
	}
	public static class PosixPath
	{
		public static string ToPosix(string value)
		{
			return value.Replace('\\', '/');
		}
	}
	public class StrictJsonException : Exception
	{
		public string Field { get; }

		public StrictJsonException(string field, string message)
			: base(message)
		{
			Field = field;
		}
	}
	public static class StrictJson
	{
		public static void OnlyKnown(JsonElement value, string prefix, string[] known, string[]? alsoAllowed = null, string? owner = null)
		{
			if (value.ValueKind != JsonValueKind.Object)
			{
				throw new StrictJsonException(prefix.TrimEnd('.'), "must be an object.");
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (JsonProperty item in value.EnumerateObject())
			{
				if (Array.IndexOf(known, item.Name) < 0 && (alsoAllowed == null || Array.IndexOf(alsoAllowed, item.Name) < 0))
				{
					throw new StrictJsonException(prefix + item.Name, "unknown field" + ((owner == null) ? "" : (" for " + owner)) + " (known: " + string.Join(", ", known) + ").");
				}
				if (!hashSet.Add(item.Name))
				{
					throw new StrictJsonException(prefix + item.Name, "duplicate field.");
				}
			}
		}

		public static void UniqueMembers(JsonElement value, string prefix = "")
		{
			if (value.ValueKind == JsonValueKind.Object)
			{
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				{
					foreach (JsonProperty item in value.EnumerateObject())
					{
						if (!hashSet.Add(item.Name))
						{
							throw new StrictJsonException(prefix + item.Name, "duplicate field.");
						}
						UniqueMembers(item.Value, prefix + item.Name + ".");
					}
					return;
				}
			}
			if (value.ValueKind != JsonValueKind.Array)
			{
				return;
			}
			foreach (JsonElement item2 in value.EnumerateArray())
			{
				UniqueMembers(item2, prefix);
			}
		}

		public static JsonElement Object(JsonElement value, string prefix, string field)
		{
			if (!value.TryGetProperty(field, out var value2) || value2.ValueKind != JsonValueKind.Object)
			{
				throw new StrictJsonException(prefix + field, "is required and must be an object.");
			}
			return value2;
		}

		public static bool Finite(JsonElement value, out double number)
		{
			number = 0.0;
			if (value.ValueKind == JsonValueKind.Number && value.TryGetDouble(out number))
			{
				return double.IsFinite(number);
			}
			return false;
		}

		public static double Number(JsonElement value, string prefix, string field, double? fallback, double min, double max)
		{
			if (!value.TryGetProperty(field, out var value2))
			{
				return fallback ?? throw new StrictJsonException(prefix + field, "is required.");
			}
			if (!Finite(value2, out var number))
			{
				throw new StrictJsonException(prefix + field, "must be a finite number.");
			}
			return InRange(number, prefix + field, min, max);
		}

		public static double Integer(JsonElement value, string prefix, string field, double? fallback, double min, double max)
		{
			if (!value.TryGetProperty(field, out var value2))
			{
				return fallback ?? throw new StrictJsonException(prefix + field, "is required.");
			}
			if (!Finite(value2, out var number))
			{
				throw new StrictJsonException(prefix + field, "must be a finite number.");
			}
			if (Math.Floor(number) != number)
			{
				throw new StrictJsonException(prefix + field, "must be a whole number.");
			}
			return InRange(number, prefix + field, min, max);
		}

		public static bool Bool(JsonElement value, string prefix, string field, bool? fallback)
		{
			if (!value.TryGetProperty(field, out var value2))
			{
				return fallback ?? throw new StrictJsonException(prefix + field, "is required.");
			}
			JsonValueKind valueKind = value2.ValueKind;
			if (valueKind - 5 <= JsonValueKind.Object)
			{
				return value2.GetBoolean();
			}
			throw new StrictJsonException(prefix + field, "must be true or false.");
		}

		public static T Enum<T>(JsonElement value, string prefix, string field) where T : struct, Enum
		{
			string text = string.Join(", ", System.Enum.GetNames(typeof(T)));
			if (!value.TryGetProperty(field, out var value2) || value2.ValueKind != JsonValueKind.String)
			{
				throw new StrictJsonException(prefix + field, "is required: one of " + text + ".");
			}
			string text2 = value2.GetString();
			if (!System.Enum.TryParse<T>(text2, ignoreCase: false, out var result) || !System.Enum.IsDefined(typeof(T), result) || text2 != result.ToString())
			{
				throw new StrictJsonException(prefix + field, "'" + text2 + "' is not one of " + text + ".");
			}
			return result;
		}

		public static (uint? Id, string? Name) Reference(JsonElement value, string prefix, string field)
		{
			if (!value.TryGetProperty(field, out var value2))
			{
				throw new StrictJsonException(prefix + field, "is required (a persistentID number or a block name).");
			}
			if (value2.ValueKind == JsonValueKind.Number && value2.TryGetUInt32(out var value3))
			{
				return (Id: value3, Name: null);
			}
			if (value2.ValueKind == JsonValueKind.String && !string.IsNullOrEmpty(value2.GetString()))
			{
				return (Id: null, Name: value2.GetString());
			}
			throw new StrictJsonException(prefix + field, "must be a persistentID number or a block name.");
		}

		private static double InRange(double number, string field, double min, double max)
		{
			if (!(number >= min) || !(number <= max))
			{
				throw new StrictJsonException(field, number.ToString(CultureInfo.InvariantCulture) + " is outside " + min.ToString(CultureInfo.InvariantCulture) + ".." + max.ToString(CultureInfo.InvariantCulture) + ".");
			}
			return number;
		}

		public static string Canonical(JsonElement value)
		{
			StringBuilder stringBuilder = new StringBuilder();
			Write(stringBuilder, value);
			return stringBuilder.ToString();
		}

		private static void Write(StringBuilder text, JsonElement value)
		{
			switch (value.ValueKind)
			{
			case JsonValueKind.Object:
			{
				List<JsonProperty> list = new List<JsonProperty>();
				foreach (JsonProperty item in value.EnumerateObject())
				{
					list.Add(item);
				}
				list.Sort((JsonProperty a, JsonProperty b) => string.CompareOrdinal(a.Name, b.Name));
				text.Append('{');
				for (int num2 = 0; num2 < list.Count; num2++)
				{
					if (num2 > 0)
					{
						text.Append(',');
					}
					text.Append(JsonSerializer.Serialize(list[num2].Name)).Append(':');
					Write(text, list[num2].Value);
				}
				text.Append('}');
				break;
			}
			case JsonValueKind.Array:
			{
				text.Append('[');
				int num = 0;
				foreach (JsonElement item2 in value.EnumerateArray())
				{
					if (num++ > 0)
					{
						text.Append(',');
					}
					Write(text, item2);
				}
				text.Append(']');
				break;
			}
			case JsonValueKind.Number:
				text.Append(CanonicalNumber(value.GetRawText()));
				break;
			case JsonValueKind.String:
				text.Append(JsonSerializer.Serialize(value.GetString()));
				break;
			default:
				text.Append(value.GetRawText());
				break;
			}
		}

		private static string CanonicalNumber(string raw)
		{
			int num = raw.IndexOfAny(new char[2] { 'e', 'E' });
			string text = ((num < 0) ? raw : raw.Substring(0, num));
			BigInteger bigInteger = ((num < 0) ? BigInteger.Zero : BigInteger.Parse(raw.Substring(num + 1), NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture));
			int num2 = text.IndexOf('.');
			if (num2 >= 0)
			{
				bigInteger -= (BigInteger)(text.Length - num2 - 1);
			}
			bool flag = text[0] == '-';
			string text2 = text.Replace(".", "").TrimStart('-', '0');
			if (text2.Length == 0)
			{
				return "0";
			}
			string text3 = text2.TrimEnd('0');
			bigInteger += (BigInteger)(text2.Length - text3.Length);
			return (flag ? "-" : "") + text3 + (bigInteger.IsZero ? "" : ("e" + bigInteger.ToString(CultureInfo.InvariantCulture)));
		}
	}
}

ForgeCore.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using AIGraph;
using Agents;
using AssetShards;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using ChainedPuzzles;
using Enemies;
using FirstPersonItem;
using GTFO.API;
using GameData;
using Gear;
using Globals;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections;
using Il2CppSystem.Collections.Generic;
using LevelGeneration;
using Localization;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ForgeCore")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+66ec6b43e1a1603bac84a4b9a226674300b2fc7d")]
[assembly: AssemblyProduct("ForgeCore")]
[assembly: AssemblyTitle("ForgeCore")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsUnmanagedAttribute : Attribute
	{
	}
	[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 ForgeCore
{
	internal sealed class CameraLeaseBook
	{
		internal sealed class Entry
		{
			internal readonly string Owner;

			internal float Lift;

			internal float NearClip = float.NaN;

			internal Entry(string owner)
			{
				Owner = owner;
			}
		}

		private readonly List<Entry> _held = new List<Entry>();

		internal int Count => _held.Count;

		internal Entry? Owner
		{
			get
			{
				if (_held.Count != 0)
				{
					List<Entry> held = _held;
					return held[held.Count - 1];
				}
				return null;
			}
		}

		internal float Lift => Owner?.Lift ?? 0f;

		internal float NearClip => Owner?.NearClip ?? float.NaN;

		internal Entry Take(string owner)
		{
			if (string.IsNullOrWhiteSpace(owner))
			{
				throw new ArgumentException("Name the mod (its plugin GUID).", "owner");
			}
			Entry entry = new Entry(owner);
			_held.Add(entry);
			return entry;
		}

		internal bool Holds(Entry entry)
		{
			return _held.Contains(entry);
		}

		internal bool Owns(Entry entry)
		{
			if (_held.Count != 0)
			{
				List<Entry> held = _held;
				return held[held.Count - 1] == entry;
			}
			return false;
		}

		internal bool Release(Entry entry)
		{
			return _held.Remove(entry);
		}

		internal int ReleaseAll()
		{
			int count = _held.Count;
			_held.Clear();
			return count;
		}
	}
	public readonly record struct ChunkInfo(ushort Type, uint StartMs, uint EndMs, long Offset, int RawLength);
	public sealed class ChunkLogFormatException : Exception
	{
		public ChunkLogFormatException(string message)
			: base(message)
		{
		}
	}
	public static class ChunkLog
	{
		internal const uint FileMagic = 1196180294u;

		internal const uint ChunkMagic = 1263421507u;

		internal const uint IndexMagic = 1481196358u;

		internal const ushort ContainerVersion = 1;

		internal const int StartBytes = 12;

		internal const int ChunkHeaderBytes = 28;

		internal const int IndexEntryBytes = 24;

		internal const int TrailerBytes = 20;

		private static readonly uint[] Table = MakeTable();

		public static ChunkWriter Create(Stream stream, uint formatVersion)
		{
			return new ChunkWriter(stream, formatVersion);
		}

		public static ChunkReader Open(Stream stream, uint expectedFormatVersion)
		{
			return new ChunkReader(stream, expectedFormatVersion);
		}

		internal static uint Crc32(ReadOnlySpan<byte> data, uint crc = 0u)
		{
			crc = ~crc;
			ReadOnlySpan<byte> readOnlySpan = data;
			for (int i = 0; i < readOnlySpan.Length; i++)
			{
				byte b = readOnlySpan[i];
				crc = Table[(crc ^ b) & 0xFF] ^ (crc >> 8);
			}
			return ~crc;
		}

		private static uint[] MakeTable()
		{
			uint[] array = new uint[256];
			for (uint num = 0u; num < 256; num++)
			{
				uint num2 = num;
				for (int i = 0; i < 8; i++)
				{
					num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1));
				}
				array[num] = num2;
			}
			return array;
		}

		internal static void PutU32(Span<byte> s, int at, uint v)
		{
			for (int i = 0; i < 4; i++)
			{
				s[at + i] = (byte)(v >> 8 * i);
			}
		}

		internal static void PutU64(Span<byte> s, int at, ulong v)
		{
			for (int i = 0; i < 8; i++)
			{
				s[at + i] = (byte)(v >> 8 * i);
			}
		}

		internal static void PutU16(Span<byte> s, int at, ushort v)
		{
			s[at] = (byte)v;
			s[at + 1] = (byte)(v >> 8);
		}

		internal static uint GetU32(ReadOnlySpan<byte> s, int at)
		{
			uint num = 0u;
			for (int i = 0; i < 4; i++)
			{
				num |= (uint)(s[at + i] << 8 * i);
			}
			return num;
		}

		internal static ulong GetU64(ReadOnlySpan<byte> s, int at)
		{
			ulong num = 0uL;
			for (int i = 0; i < 8; i++)
			{
				num |= (ulong)s[at + i] << 8 * i;
			}
			return num;
		}

		internal static ushort GetU16(ReadOnlySpan<byte> s, int at)
		{
			return (ushort)(s[at] | (s[at + 1] << 8));
		}
	}
	public sealed class ChunkWriter
	{
		private readonly Stream _stream;

		private readonly List<ChunkInfo> _index = new List<ChunkInfo>();

		private readonly MemoryStream _scratch = new MemoryStream();

		private readonly byte[] _header = new byte[28];

		private long _end;

		private bool _finished;

		public long Length => _end;

		internal ChunkWriter(Stream stream, uint formatVersion)
		{
			_stream = stream ?? throw new ArgumentNullException("stream");
			Span<byte> span = stackalloc byte[12];
			ChunkLog.PutU32(span, 0, 1196180294u);
			ChunkLog.PutU16(span, 4, 1);
			ChunkLog.PutU16(span, 6, 0);
			ChunkLog.PutU32(span, 8, formatVersion);
			_stream.Write(span);
			_stream.Flush();
			_end = 12L;
		}

		public void Append(ushort type, uint startMs, uint endMs, ReadOnlySpan<byte> data)
		{
			if (_finished)
			{
				throw new InvalidOperationException("The log is finished.");
			}
			if (endMs < startMs)
			{
				throw new ArgumentException("A chunk's end is not before its start.");
			}
			_scratch.SetLength(0L);
			using (DeflateStream deflateStream = new DeflateStream(_scratch, CompressionLevel.Fastest, leaveOpen: true))
			{
				deflateStream.Write(data);
			}
			bool flag = _scratch.Length < data.Length;
			ReadOnlySpan<byte> readOnlySpan = (flag ? new ReadOnlySpan<byte>(_scratch.GetBuffer(), 0, (int)_scratch.Length) : data);
			ChunkLog.PutU32(_header, 0, 1263421507u);
			ChunkLog.PutU16(_header, 4, type);
			ChunkLog.PutU16(_header, 6, flag ? ((ushort)1) : ((ushort)0));
			ChunkLog.PutU32(_header, 8, startMs);
			ChunkLog.PutU32(_header, 12, endMs);
			ChunkLog.PutU32(_header, 16, (uint)data.Length);
			ChunkLog.PutU32(_header, 20, (uint)readOnlySpan.Length);
			ChunkLog.PutU32(_header, 24, ChunkLog.Crc32(readOnlySpan, ChunkLog.Crc32(_header.AsSpan(0, 24))));
			_stream.Write(_header);
			_stream.Write(readOnlySpan);
			_stream.Flush();
			_index.Add(new ChunkInfo(type, startMs, endMs, _end, data.Length));
			_end += 28 + readOnlySpan.Length;
		}

		public void Finish()
		{
			if (!_finished)
			{
				long end = _end;
				byte[] array = new byte[_index.Count * 24];
				for (int i = 0; i < _index.Count; i++)
				{
					ChunkInfo chunkInfo = _index[i];
					int num = i * 24;
					ChunkLog.PutU64(array, num, (ulong)chunkInfo.Offset);
					ChunkLog.PutU16(array, num + 8, chunkInfo.Type);
					ChunkLog.PutU16(array, num + 10, 0);
					ChunkLog.PutU32(array, num + 12, chunkInfo.StartMs);
					ChunkLog.PutU32(array, num + 16, chunkInfo.EndMs);
					ChunkLog.PutU32(array, num + 20, (uint)chunkInfo.RawLength);
				}
				byte[] array2 = new byte[20];
				ChunkLog.PutU32(array2, 0, 1481196358u);
				ChunkLog.PutU64(array2, 4, (ulong)end);
				ChunkLog.PutU32(array2, 12, (uint)_index.Count);
				ChunkLog.PutU32(array2, 16, ChunkLog.Crc32(array, ChunkLog.Crc32(array2.AsSpan(0, 16))));
				_stream.Write(array);
				_stream.Write(array2);
				_stream.Flush();
				_finished = true;
			}
		}
	}
	public sealed class ChunkReader
	{
		private readonly Stream _stream;

		private readonly List<ChunkInfo> _chunks = new List<ChunkInfo>();

		public uint FormatVersion { get; }

		public bool IsComplete { get; private set; }

		public IReadOnlyList<ChunkInfo> Chunks => _chunks;

		internal ChunkReader(Stream stream, uint expectedFormatVersion)
		{
			_stream = stream ?? throw new ArgumentNullException("stream");
			Span<byte> span = stackalloc byte[12];
			if (ReadAt(0L, span) != 12 || ChunkLog.GetU32(span, 0) != 1196180294)
			{
				throw new ChunkLogFormatException("This file is not a chunk log.");
			}
			ushort u = ChunkLog.GetU16(span, 4);
			if (u != 1)
			{
				throw new ChunkLogFormatException("The chunk container version is " + u + ", this library reads " + (ushort)1 + ".");
			}
			FormatVersion = ChunkLog.GetU32(span, 8);
			if (FormatVersion != expectedFormatVersion)
			{
				throw new ChunkLogFormatException("The format version is " + FormatVersion + ", expected " + expectedFormatVersion + ".");
			}
			if (!TryReadIndex())
			{
				Scan();
			}
		}

		public byte[] Read(ChunkInfo chunk)
		{
			byte[] array = new byte[28];
			if (ReadAt(chunk.Offset, array) != array.Length || ChunkLog.GetU32(array, 0) != 1263421507)
			{
				throw new InvalidDataException("There is no chunk at offset " + chunk.Offset + ".");
			}
			int num = checked((int)ChunkLog.GetU32(array, 20));
			byte[] array2 = new byte[num];
			if (ReadAt(chunk.Offset + 28, array2) != num)
			{
				throw new InvalidDataException("The chunk at offset " + chunk.Offset + " is cut off.");
			}
			if (ChunkLog.GetU32(array, 24) != ChunkLog.Crc32(array2, ChunkLog.Crc32(array.AsSpan(0, 24))))
			{
				throw new InvalidDataException("The chunk at offset " + chunk.Offset + " fails its checksum.");
			}
			int num2 = checked((int)ChunkLog.GetU32(array, 16));
			if (ChunkLog.GetU16(array, 6) == 0)
			{
				if (num2 != num)
				{
					throw new InvalidDataException("The stored chunk at offset " + chunk.Offset + " has two lengths.");
				}
				return array2;
			}
			byte[] array3 = new byte[num2];
			using DeflateStream deflateStream = new DeflateStream(new MemoryStream(array2), CompressionMode.Decompress);
			int num3;
			for (int i = 0; i < num2; i += num3)
			{
				num3 = deflateStream.Read(array3, i, num2 - i);
				if (num3 == 0)
				{
					throw new InvalidDataException("The chunk at offset " + chunk.Offset + " decompresses short.");
				}
			}
			return array3;
		}

		private int ReadAt(long offset, Span<byte> buffer)
		{
			if (offset < 0 || offset > _stream.Length)
			{
				return 0;
			}
			_stream.Position = offset;
			int i;
			int num;
			for (i = 0; i < buffer.Length; i += num)
			{
				num = _stream.Read(buffer.Slice(i));
				if (num == 0)
				{
					break;
				}
			}
			return i;
		}

		private bool TryReadIndex()
		{
			long length = _stream.Length;
			if (length < 32)
			{
				return false;
			}
			byte[] array = new byte[20];
			if (ReadAt(length - 20, array) != array.Length || ChunkLog.GetU32(array, 0) != 1481196358)
			{
				return false;
			}
			long u = (long)ChunkLog.GetU64(array, 4);
			uint u2 = ChunkLog.GetU32(array, 12);
			long num = (long)u2 * 24L;
			if (u < 12 || u + num + 20 != length)
			{
				return false;
			}
			byte[] array2 = new byte[num];
			if (ReadAt(u, array2) != array2.Length)
			{
				return false;
			}
			if (ChunkLog.GetU32(array, 16) != ChunkLog.Crc32(array2, ChunkLog.Crc32(array.AsSpan(0, 16))))
			{
				return false;
			}
			for (int i = 0; i < u2; i++)
			{
				int num2 = i * 24;
				_chunks.Add(new ChunkInfo(ChunkLog.GetU16(array2, num2 + 8), ChunkLog.GetU32(array2, num2 + 12), ChunkLog.GetU32(array2, num2 + 16), (long)ChunkLog.GetU64(array2, num2), (int)ChunkLog.GetU32(array2, num2 + 20)));
			}
			IsComplete = true;
			return true;
		}

		private void Scan()
		{
			byte[] array = new byte[28];
			long num = 12L;
			uint u;
			for (long length = _stream.Length; num + 28 <= length && ReadAt(num, array) == array.Length; num += 28 + u)
			{
				if (ChunkLog.GetU32(array, 0) != 1263421507)
				{
					break;
				}
				u = ChunkLog.GetU32(array, 20);
				if (num + 28 + u <= length)
				{
					byte[] array2 = new byte[u];
					if (ReadAt(num + 28, array2) == array2.Length && ChunkLog.GetU32(array, 24) == ChunkLog.Crc32(array2, ChunkLog.Crc32(array.AsSpan(0, 24))))
					{
						_chunks.Add(new ChunkInfo(ChunkLog.GetU16(array, 4), ChunkLog.GetU32(array, 8), ChunkLog.GetU32(array, 12), num, (int)ChunkLog.GetU32(array, 16)));
						continue;
					}
					break;
				}
				break;
			}
		}
	}
	public static class Compact
	{
		public const int MaxVarBytes = 10;

		public const int QuaternionBytes = 6;

		private const int QuatBits = 15;

		private const int QuatMax = 32767;

		private const float QuatRange = 0.70710677f;

		public static ulong ZigZag(long value)
		{
			return (ulong)((value << 1) ^ (value >> 63));
		}

		public static long UnZigZag(ulong value)
		{
			return (long)((value >> 1) ^ (0L - (value & 1)));
		}

		public static void WriteVar(byte[] buffer, ref int position, ulong value)
		{
			while (value >= 128)
			{
				buffer[position++] = (byte)(value | 0x80);
				value >>= 7;
			}
			buffer[position++] = (byte)value;
		}

		public static ulong ReadVar(byte[] buffer, ref int position)
		{
			ulong num = 0uL;
			for (int i = 0; i < 64; i += 7)
			{
				if (position >= buffer.Length)
				{
					throw new FormatException("A variable-length integer runs past the end of the data.");
				}
				byte b = buffer[position++];
				if (i == 63 && b > 1)
				{
					throw new FormatException("A variable-length integer does not fit 64 bits.");
				}
				num |= (ulong)((long)(b & 0x7F) << i);
				if ((b & 0x80) == 0)
				{
					return num;
				}
			}
			throw new FormatException("A variable-length integer is longer than 10 bytes.");
		}

		public static void WriteVarSigned(byte[] buffer, ref int position, long value)
		{
			WriteVar(buffer, ref position, ZigZag(value));
		}

		public static long ReadVarSigned(byte[] buffer, ref int position)
		{
			return UnZigZag(ReadVar(buffer, ref position));
		}

		public static void WriteDelta(byte[] buffer, ref int position, long previous, long current)
		{
			WriteVarSigned(buffer, ref position, current - previous);
		}

		public static long ReadDelta(byte[] buffer, ref int position, long previous)
		{
			return previous + ReadVarSigned(buffer, ref position);
		}

		public static int Quantize(float value, float min, float step, int maxSteps)
		{
			if (step <= 0f)
			{
				throw new ArgumentOutOfRangeException("step", "A quantization step is positive.");
			}
			float num = MathF.Round((value - min) / step);
			if (float.IsNaN(num))
			{
				return 0;
			}
			if (!(num <= 0f))
			{
				if (!(num >= (float)maxSteps))
				{
					return (int)num;
				}
				return maxSteps;
			}
			return 0;
		}

		public static float Dequantize(int steps, float min, float step)
		{
			return min + (float)steps * step;
		}

		public static void WriteUInt16(byte[] buffer, ref int position, ushort value)
		{
			buffer[position++] = (byte)value;
			buffer[position++] = (byte)(value >> 8);
		}

		public static ushort ReadUInt16(byte[] buffer, ref int position)
		{
			if (position + 2 > buffer.Length)
			{
				throw new FormatException("A 16-bit value runs past the end of the data.");
			}
			ushort result = (ushort)(buffer[position] | (buffer[position + 1] << 8));
			position += 2;
			return result;
		}

		public static void WriteQuaternion(byte[] buffer, ref int position, float x, float y, float z, float w)
		{
			Span<float> span = stackalloc float[4] { x, y, z, w };
			float num = MathF.Sqrt(x * x + y * y + z * z + w * w);
			if (!(num > 0f))
			{
				throw new ArgumentException("A quaternion cannot be zero or not a number.");
			}
			int num2 = 0;
			for (int i = 1; i < 4; i++)
			{
				if (MathF.Abs(span[i]) > MathF.Abs(span[num2]))
				{
					num2 = i;
				}
			}
			float num3 = ((span[num2] < 0f) ? (-1f) : 1f);
			ulong num4 = (ulong)num2;
			int num5 = 2;
			for (int j = 0; j < 4; j++)
			{
				if (j != num2)
				{
					int num6 = Quantize(span[j] * num3 / num, -0.70710677f, 4.315969E-05f, 32767);
					num4 |= (ulong)((long)num6 << num5);
					num5 += 15;
				}
			}
			for (int k = 0; k < 6; k++)
			{
				buffer[position++] = (byte)(num4 >> 8 * k);
			}
		}

		public static void ReadQuaternion(byte[] buffer, ref int position, out float x, out float y, out float z, out float w)
		{
			if (position + 6 > buffer.Length)
			{
				throw new FormatException("A quaternion runs past the end of the data.");
			}
			ulong num = 0uL;
			for (int i = 0; i < 6; i++)
			{
				num |= (ulong)buffer[position++] << 8 * i;
			}
			int num2 = (int)(num & 3);
			Span<float> span = stackalloc float[4];
			int num3 = 2;
			float num4 = 0f;
			for (int j = 0; j < 4; j++)
			{
				if (j != num2)
				{
					int steps = (int)((num >> num3) & 0x7FFF);
					span[j] = Dequantize(steps, -0.70710677f, 4.315969E-05f);
					num4 += span[j] * span[j];
					num3 += 15;
				}
			}
			span[num2] = MathF.Sqrt(MathF.Max(0f, 1f - num4));
			x = span[0];
			y = span[1];
			z = span[2];
			w = span[3];
		}
	}
	internal sealed class ContentFolderIndex
	{
		private readonly Dictionary<string, SortedSet<string>> byName = new Dictionary<string, SortedSet<string>>(StringComparer.OrdinalIgnoreCase);

		internal List<string> Errors { get; } = new List<string>();

		internal static ContentFolderIndex Build(string root)
		{
			ContentFolderIndex contentFolderIndex = new ContentFolderIndex();
			Stack<string> stack = new Stack<string>();
			stack.Push(root);
			while (stack.Count != 0)
			{
				string text = stack.Pop();
				string[] directories;
				try
				{
					directories = Directory.GetDirectories(text);
				}
				catch (Exception ex)
				{
					contentFolderIndex.Errors.Add(text + ": could not enumerate folders: " + ex);
					continue;
				}
				string[] array = directories;
				foreach (string text2 in array)
				{
					string fileName = Path.GetFileName(text2);
					if (!contentFolderIndex.byName.TryGetValue(fileName, out SortedSet<string> value))
					{
						value = (contentFolderIndex.byName[fileName] = new SortedSet<string>(StringComparer.Ordinal));
					}
					value.Add(Path.GetFullPath(text2));
					stack.Push(text2);
				}
			}
			return contentFolderIndex;
		}

		internal IReadOnlyList<string> Find(string folderName)
		{
			if (!byName.TryGetValue(folderName, out SortedSet<string> value))
			{
				return Array.Empty<string>();
			}
			return new List<string>(value);
		}
	}
	public readonly record struct DamageSettlement(float HealthBefore, float HealthAfter)
	{
		public float Dealt => HealthBefore - HealthAfter;
	}
	public enum DataBlockResult
	{
		Free,
		Added,
		AlreadyAdded,
		Conflict,
		ReadbackFailed
	}
	internal sealed class DataBlockLedger
	{
		private readonly Dictionary<(string Table, uint Id), (string Mod, string Name)> _owners = new Dictionary<(string, uint), (string, string)>();

		internal DataBlockResult Decide(string table, uint id, string modId, string name, string? existingName)
		{
			if (id == 0)
			{
				throw new ArgumentOutOfRangeException("id", "Data block ID 0 is never valid.");
			}
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentException("Name the data block.", "name");
			}
			if (existingName == null)
			{
				return DataBlockResult.Free;
			}
			if (!_owners.TryGetValue((table, id), out (string, string) value) || !(value.Item1 == modId) || !(value.Item2 == name) || !(existingName == name))
			{
				return DataBlockResult.Conflict;
			}
			return DataBlockResult.AlreadyAdded;
		}

		internal void Owns(string table, uint id, string modId, string name)
		{
			_owners[(table, id)] = (modId, name);
		}

		internal string? OwnerOf(string table, uint id)
		{
			if (!_owners.TryGetValue((table, id), out (string, string) value))
			{
				return null;
			}
			return value.Item1;
		}
	}
	public enum EnemyAlert : byte
	{
		Off,
		Sleeping,
		Awake,
		Hunting
	}
	public sealed class Fingerprint
	{
		private readonly IncrementalHash _hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);

		private readonly StringBuilder _line = new StringBuilder();

		public int Count { get; private set; }

		public Fingerprint Add(string label, string? value)
		{
			return Write(label, value ?? "\u0001null");
		}

		public Fingerprint Add(string label, long value)
		{
			return Write(label, value.ToString(CultureInfo.InvariantCulture));
		}

		public Fingerprint Add(string label, ulong value)
		{
			return Write(label, value.ToString(CultureInfo.InvariantCulture));
		}

		public Fingerprint Add(string label, bool value)
		{
			return Write(label, value ? "1" : "0");
		}

		public Fingerprint Add(string label, double value, int decimals)
		{
			if (double.IsNaN(value) || double.IsInfinity(value))
			{
				return Write(label, value.ToString(CultureInfo.InvariantCulture));
			}
			double num = Math.Round(value, decimals, MidpointRounding.AwayFromZero);
			if (num == 0.0)
			{
				num = 0.0;
			}
			return Write(label, num.ToString("F" + decimals, CultureInfo.InvariantCulture));
		}

		public Fingerprint Add(string label, Fingerprint inner)
		{
			return Write(label, inner.Hex());
		}

		private Fingerprint Write(string label, string value)
		{
			_line.Clear();
			_line.Append(label).Append('=').Append(value)
				.Append('\n');
			_hash.AppendData(Encoding.UTF8.GetBytes(_line.ToString()));
			Count++;
			return this;
		}

		public string Hex()
		{
			byte[] currentHash = _hash.GetCurrentHash();
			StringBuilder stringBuilder = new StringBuilder(32);
			for (int i = 0; i < 16; i++)
			{
				stringBuilder.Append(currentHash[i].ToString("x2", CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}
	}
	public static class ContentHash
	{
		private sealed class ReferenceComparer : IEqualityComparer<object>
		{
			internal static readonly ReferenceComparer Instance = new ReferenceComparer();

			public new bool Equals(object? a, object? b)
			{
				return a == b;
			}

			public int GetHashCode(object o)
			{
				return RuntimeHelpers.GetHashCode(o);
			}
		}

		public const int MaxDepth = 12;

		private static readonly HashSet<string> Skipped = new HashSet<string>(StringComparer.Ordinal) { "Pointer", "WasCollected", "ObjectClass", "isWrapped", "Item", "SyncRoot", "IsSynchronized", "IsReadOnly", "IsFixedSize", "IsAlias" };

		private static readonly Dictionary<Type, MemberInfo[]> Members = new Dictionary<Type, MemberInfo[]>();

		private static bool IsPlumbing(Type type)
		{
			string text = type.Assembly.GetName().Name ?? "";
			if (!text.StartsWith("Il2Cpp", StringComparison.Ordinal) && !text.StartsWith("UnityEngine", StringComparison.Ordinal) && !text.StartsWith("System", StringComparison.Ordinal) && !(text == "mscorlib"))
			{
				return text == "netstandard";
			}
			return true;
		}

		public static string Of(object? value, out bool truncated)
		{
			Fingerprint fingerprint = new Fingerprint();
			bool cut = false;
			Walk(fingerprint, "", value, 0, new HashSet<object>(ReferenceComparer.Instance), ref cut);
			truncated = cut;
			return fingerprint.Hex();
		}

		private static void Walk(Fingerprint fp, string path, object? value, int depth, HashSet<object> seen, ref bool cut)
		{
			if (value == null)
			{
				fp.Add(path, (string?)null);
				return;
			}
			Type type = value.GetType();
			if (value is string value2)
			{
				fp.Add(path, value2);
				return;
			}
			if (type.IsEnum)
			{
				fp.Add(path, Convert.ToInt64(value, CultureInfo.InvariantCulture));
				return;
			}
			if (type.IsPrimitive || value is decimal)
			{
				if (!(value is bool value3))
				{
					if (!(value is float num))
					{
						if (!(value is double value4))
						{
							if (value is ulong value5)
							{
								fp.Add(path, value5);
							}
							else
							{
								fp.Add(path, Convert.ToString(value, CultureInfo.InvariantCulture));
							}
						}
						else
						{
							fp.Add(path, value4, 4);
						}
					}
					else
					{
						fp.Add(path, num, 4);
					}
				}
				else
				{
					fp.Add(path, value3);
				}
				return;
			}
			if (depth >= 12)
			{
				cut = true;
				fp.Add(path, "\u0001deep");
				return;
			}
			if (!type.IsValueType && !seen.Add(value))
			{
				fp.Add(path, "\u0001cycle");
				return;
			}
			if (value is IEnumerable enumerable && !(value is string) && IsCollection(type))
			{
				int num2 = 0;
				foreach (object item in enumerable)
				{
					Walk(fp, path + "[" + num2++ + "]", item, depth + 1, seen, ref cut);
				}
				fp.Add(path + ".count", num2);
				if (!type.IsValueType)
				{
					seen.Remove(value);
				}
				return;
			}
			if (!type.IsValueType && IsPlumbing(type))
			{
				fp.Add(path, "\u0001ref:" + type.Name);
				if (!type.IsValueType)
				{
					seen.Remove(value);
				}
				return;
			}
			MemberInfo[] array = MembersOf(type);
			foreach (MemberInfo memberInfo in array)
			{
				object value6;
				try
				{
					value6 = ((memberInfo is PropertyInfo propertyInfo) ? propertyInfo.GetValue(value) : ((FieldInfo)memberInfo).GetValue(value));
				}
				catch (Exception)
				{
					fp.Add(path + "." + memberInfo.Name, "\u0001unreadable");
					continue;
				}
				Walk(fp, path + "." + memberInfo.Name, value6, depth + 1, seen, ref cut);
			}
			if (!type.IsValueType)
			{
				seen.Remove(value);
			}
		}

		private static bool IsCollection(Type type)
		{
			if (!type.IsArray)
			{
				return typeof(IEnumerable).IsAssignableFrom(type);
			}
			return true;
		}

		private static MemberInfo[] MembersOf(Type type)
		{
			lock (Members)
			{
				if (Members.TryGetValue(type, out MemberInfo[] value))
				{
					return value;
				}
				List<MemberInfo> list = new List<MemberInfo>();
				PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
				foreach (PropertyInfo propertyInfo in properties)
				{
					if (!type.IsValueType && !IsPlumbing(propertyInfo.DeclaringType) && propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0 && !Skipped.Contains(propertyInfo.Name) && !propertyInfo.Name.Contains("__BackingField", StringComparison.Ordinal))
					{
						list.Add(propertyInfo);
					}
				}
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
				foreach (FieldInfo fieldInfo in fields)
				{
					if ((!IsPlumbing(fieldInfo.DeclaringType) || type.IsValueType) && !Skipped.Contains(fieldInfo.Name) && !fieldInfo.Name.Contains("__BackingField", StringComparison.Ordinal) && !fieldInfo.Name.StartsWith("NativeFieldInfoPtr_", StringComparison.Ordinal))
					{
						list.Add(fieldInfo);
					}
				}
				list.Sort((MemberInfo a, MemberInfo b) => string.CompareOrdinal(a.Name, b.Name));
				return Members[type] = list.ToArray();
			}
		}
	}
	internal sealed class FrameBook
	{
		private readonly struct Entry
		{
			internal readonly string ModId;

			internal readonly Action Callback;

			internal Entry(string modId, Action callback)
			{
				ModId = modId;
				Callback = callback;
			}
		}

		private readonly string _kind;

		private readonly List<Entry> _entries = new List<Entry>();

		internal int Count => _entries.Count;

		internal FrameBook(string kind)
		{
			_kind = kind;
		}

		internal void Add(string modId, Action callback)
		{
			if (string.IsNullOrWhiteSpace(modId))
			{
				throw new ArgumentException("Name the mod (its plugin GUID).", "modId");
			}
			if (callback == null)
			{
				throw new ArgumentNullException("callback");
			}
			_entries.Add(new Entry(modId, callback));
		}

		internal void Run()
		{
			int count = _entries.Count;
			for (int i = 0; i < count; i++)
			{
				Entry entry = _entries[i];
				try
				{
					entry.Callback();
				}
				catch (Exception ex)
				{
					Log.For(entry.ModId).ErrorOnce(entry.ModId + " " + _kind + " callback failed: " + ex);
				}
			}
		}
	}
	public static class GameEvents
	{
		private static Action? _master;

		private static Action? _language;

		private static Action? _levelEnded;

		private static Action? _checkpointReached;

		private static Action? _checkpointReloaded;

		private static Action? _localPlayerDown;

		private static Action<bool>? _lobby;

		private static Action<uint?, int, int>? _expeditionSelected;

		private static int _languageRevision;

		internal static Action NeedLanguage { get; set; } = delegate
		{
		};

		internal static Action NeedLocalPlayerDown { get; set; } = delegate
		{
		};

		internal static Action NeedExpeditionSelected { get; set; } = delegate
		{
		};

		public static bool InLobby { get; private set; }

		public static ulong Master { get; private set; }

		public static int LanguageRevision
		{
			get
			{
				NeedLanguage();
				return _languageRevision;
			}
		}

		public static event Action<bool>? LobbyChanged
		{
			add
			{
				_lobby = (Action<bool>)Delegate.Combine(_lobby, value);
			}
			remove
			{
				_lobby = (Action<bool>)Delegate.Remove(_lobby, value);
			}
		}

		public static event Action<uint?, int, int>? ExpeditionSelected
		{
			add
			{
				NeedExpeditionSelected();
				_expeditionSelected = (Action<uint?, int, int>)Delegate.Combine(_expeditionSelected, value);
			}
			remove
			{
				_expeditionSelected = (Action<uint?, int, int>)Delegate.Remove(_expeditionSelected, value);
			}
		}

		public static event Action? MasterChanged
		{
			add
			{
				_master = (Action)Delegate.Combine(_master, value);
			}
			remove
			{
				_master = (Action)Delegate.Remove(_master, value);
			}
		}

		public static event Action? LanguageChanged
		{
			add
			{
				NeedLanguage();
				_language = (Action)Delegate.Combine(_language, value);
			}
			remove
			{
				_language = (Action)Delegate.Remove(_language, value);
			}
		}

		public static event Action? LevelEnded
		{
			add
			{
				_levelEnded = (Action)Delegate.Combine(_levelEnded, value);
			}
			remove
			{
				_levelEnded = (Action)Delegate.Remove(_levelEnded, value);
			}
		}

		public static event Action? CheckpointReached
		{
			add
			{
				_checkpointReached = (Action)Delegate.Combine(_checkpointReached, value);
			}
			remove
			{
				_checkpointReached = (Action)Delegate.Remove(_checkpointReached, value);
			}
		}

		public static event Action? CheckpointReloaded
		{
			add
			{
				_checkpointReloaded = (Action)Delegate.Combine(_checkpointReloaded, value);
			}
			remove
			{
				_checkpointReloaded = (Action)Delegate.Remove(_checkpointReloaded, value);
			}
		}

		public static event Action? LocalPlayerDown
		{
			add
			{
				NeedLocalPlayerDown();
				_localPlayerDown = (Action)Delegate.Combine(_localPlayerDown, value);
			}
			remove
			{
				_localPlayerDown = (Action)Delegate.Remove(_localPlayerDown, value);
			}
		}

		internal static void NoteMaster(ulong master)
		{
			if (master != Master)
			{
				Master = master;
				Raise(_master, "master-changed");
			}
		}

		internal static void NoteLanguage()
		{
			_languageRevision++;
			Raise(_language, "language-changed");
		}

		internal static void NoteLevelEnded()
		{
			Raise(_levelEnded, "level-ended");
		}

		internal static void NoteCheckpointReached()
		{
			Raise(_checkpointReached, "checkpoint-reached");
		}

		internal static void NoteCheckpointReloaded()
		{
			Raise(_checkpointReloaded, "checkpoint-reloaded");
		}

		internal static void NoteLocalPlayerDown()
		{
			Raise(_localPlayerDown, "local-player-down");
		}

		internal static void SeedLobby(bool inLobby)
		{
			InLobby = inLobby;
		}

		internal static void NoteLobby(bool inLobby)
		{
			if (inLobby != InLobby)
			{
				InLobby = inLobby;
				Raise(_lobby, inLobby, "lobby-changed");
			}
		}

		internal static void NoteExpeditionSelected(uint? rundownId, int tier, int index)
		{
			Action<uint?, int, int> expeditionSelected = _expeditionSelected;
			if (expeditionSelected == null)
			{
				return;
			}
			Delegate[] invocationList = expeditionSelected.GetInvocationList();
			foreach (Delegate obj in invocationList)
			{
				try
				{
					((Action<uint?, int, int>)obj)(rundownId, tier, index);
				}
				catch (Exception ex)
				{
					Log.For("ForgeCore").ErrorOnce("An expedition-selected handler failed: " + ex);
				}
			}
		}

		private static void Raise(Action<bool>? handlers, bool value, string key)
		{
			if (handlers == null)
			{
				return;
			}
			Delegate[] invocationList = handlers.GetInvocationList();
			foreach (Delegate obj in invocationList)
			{
				try
				{
					((Action<bool>)obj)(value);
				}
				catch (Exception ex)
				{
					Log.For("ForgeCore").ErrorOnce("A " + key + " handler failed: " + ex);
				}
			}
		}

		private static void Raise(Action? handlers, string key)
		{
			if (handlers == null)
			{
				return;
			}
			Delegate[] invocationList = handlers.GetInvocationList();
			foreach (Delegate obj in invocationList)
			{
				try
				{
					((Action)obj)();
				}
				catch (Exception ex)
				{
					Log.For("ForgeCore").ErrorOnce("A " + key + " handler failed: " + ex);
				}
			}
		}

		internal static void Reset()
		{
			_master = (_language = (_levelEnded = (_checkpointReached = (_checkpointReloaded = (_localPlayerDown = null)))));
			_lobby = null;
			_expeditionSelected = null;
			Master = 0uL;
			InLobby = false;
			_languageRevision = 0;
		}
	}
	public static class GearAssembly
	{
		public const int MaxSteps = 256;

		public static int Finish(Func<bool> moveNext)
		{
			for (int i = 0; i < 256; i++)
			{
				if (!moveNext())
				{
					return i;
				}
			}
			return -1;
		}
	}
	public struct InputButtonState
	{
		public bool Held;

		public bool DownPending;

		public bool UpPending;

		public int PressFrame;

		public int ReleaseFrame;

		public int DownQueryFrame;

		public int UpQueryFrame;
	}
	internal sealed class InputLeaseBook
	{
		internal sealed class Entry
		{
			internal readonly string Owner;

			internal readonly bool[] Blocked = new bool[128];

			internal bool Steers;

			internal float Sideways;

			internal float Forward;

			internal readonly InputButtonState[] Buttons = new InputButtonState[128];

			internal readonly bool[] Pressed = new bool[128];

			internal int AxisAction = -1;

			internal float AxisValue;

			internal Func<string?>? Guard;

			internal Action<string>? Invalidated;

			internal Entry(string owner)
			{
				Owner = owner;
			}
		}

		internal const int Actions = 128;

		private readonly List<Entry> _held = new List<Entry>();

		private readonly bool[] _blocked = new bool[128];

		private readonly bool[] _pressed = new bool[128];

		private readonly bool[] _axes = new bool[128];

		internal int Count => _held.Count;

		internal bool Engaged { get; private set; }

		internal Entry Take(string owner)
		{
			if (string.IsNullOrWhiteSpace(owner))
			{
				throw new ArgumentException("Name the mod (its plugin GUID).", "owner");
			}
			Entry entry = new Entry(owner);
			_held.Add(entry);
			return entry;
		}

		internal bool Holds(Entry entry)
		{
			return _held.Contains(entry);
		}

		internal void Block(Entry entry, ReadOnlySpan<int> actions)
		{
			if (!Holds(entry))
			{
				throw new InvalidOperationException("The input lease of " + entry.Owner + " was already released.");
			}
			Array.Clear(entry.Blocked);
			ReadOnlySpan<int> readOnlySpan = actions;
			for (int i = 0; i < readOnlySpan.Length; i++)
			{
				int num = readOnlySpan[i];
				if (num < 0 || num >= 128)
				{
					throw new ArgumentOutOfRangeException("actions", "Input action " + num + " is outside 0.." + 127 + ".");
				}
				entry.Blocked[num] = true;
			}
			Recompute();
		}

		internal void Steer(Entry entry, bool steers, float sideways, float forward)
		{
			if (!Holds(entry))
			{
				throw new InvalidOperationException("The input lease of " + entry.Owner + " was already released.");
			}
			entry.Steers = steers;
			entry.Sideways = Math.Clamp(sideways, -1f, 1f);
			entry.Forward = Math.Clamp(forward, -1f, 1f);
			Recompute();
		}

		internal void Press(Entry entry, int action, int frame)
		{
			Check(entry, action);
			entry.Pressed[action] = true;
			entry.Buttons[action] = new InputButtonState
			{
				Held = true,
				DownPending = true,
				PressFrame = frame,
				ReleaseFrame = -1,
				DownQueryFrame = -1,
				UpQueryFrame = -1
			};
			Recompute();
		}

		internal bool ReleaseButton(Entry entry, int action, int frame)
		{
			if (!Holds(entry))
			{
				throw new InvalidOperationException("The input lease of " + entry.Owner + " was already released.");
			}
			if (action < 0 || action >= 128 || !entry.Pressed[action])
			{
				return false;
			}
			ref InputButtonState reference = ref entry.Buttons[action];
			if (!reference.Held && reference.UpPending)
			{
				return false;
			}
			bool result = reference.DownQueryFrame < 0;
			reference.Held = false;
			reference.ReleaseFrame = frame;
			reference.UpPending = true;
			reference.UpQueryFrame = -1;
			return result;
		}

		internal void SetAxis(Entry entry, int action, float value)
		{
			Check(entry, action);
			if (!float.IsFinite(value))
			{
				throw new ArgumentOutOfRangeException("value");
			}
			if (entry.AxisAction >= 0 && entry.AxisAction != action)
			{
				throw new InvalidOperationException("input.axis-already-leased:" + entry.AxisAction);
			}
			entry.AxisAction = action;
			entry.AxisValue = value;
			Recompute();
		}

		internal void Guarded(Entry entry, Func<string?> invalid, Action<string> invalidated)
		{
			if (!Holds(entry))
			{
				throw new InvalidOperationException("The input lease of " + entry.Owner + " was already released.");
			}
			entry.Guard = invalid;
			entry.Invalidated = invalidated;
		}

		internal bool HasGuard()
		{
			foreach (Entry item in _held)
			{
				if (item.Guard != null)
				{
					return true;
				}
			}
			return false;
		}

		internal List<Entry> Snapshot()
		{
			return new List<Entry>(_held);
		}

		private void Check(Entry entry, int action)
		{
			if (!Holds(entry))
			{
				throw new InvalidOperationException("The input lease of " + entry.Owner + " was already released.");
			}
			if (action <= 0 || action >= 128)
			{
				throw new ArgumentOutOfRangeException("action", "Input action " + action + " is outside 1.." + 127 + ".");
			}
		}

		internal bool Presses(int action)
		{
			if (action >= 0 && action < 128)
			{
				return _pressed[action];
			}
			return false;
		}

		internal bool Drives(int action)
		{
			if (action >= 0 && action < 128)
			{
				return _axes[action];
			}
			return false;
		}

		internal bool Held(int action)
		{
			return Newest(action)?.Buttons[action].Held ?? false;
		}

		internal bool Down(int action, int frame)
		{
			Entry entry = Newest(action);
			if (entry == null)
			{
				return false;
			}
			ref InputButtonState reference = ref entry.Buttons[action];
			if (reference.DownQueryFrame >= 0)
			{
				return frame == reference.DownQueryFrame;
			}
			if (!reference.DownPending)
			{
				return false;
			}
			reference.DownPending = false;
			reference.DownQueryFrame = frame;
			return true;
		}

		internal bool Up(int action, int frame)
		{
			Entry entry = Newest(action);
			if (entry == null)
			{
				return false;
			}
			ref InputButtonState reference = ref entry.Buttons[action];
			if (reference.UpQueryFrame >= 0)
			{
				return frame == reference.UpQueryFrame;
			}
			if (!reference.UpPending)
			{
				return false;
			}
			reference.UpPending = false;
			reference.UpQueryFrame = frame;
			return true;
		}

		internal float Axis(int action)
		{
			for (int num = _held.Count - 1; num >= 0; num--)
			{
				if (_held[num].AxisAction == action)
				{
					return _held[num].AxisValue;
				}
			}
			return 0f;
		}

		private Entry? Newest(int action)
		{
			for (int num = _held.Count - 1; num >= 0; num--)
			{
				if (_held[num].Pressed[action])
				{
					return _held[num];
				}
			}
			return null;
		}

		internal bool Release(Entry entry)
		{
			if (!_held.Remove(entry))
			{
				return false;
			}
			Recompute();
			return true;
		}

		internal int ReleaseAll()
		{
			int count = _held.Count;
			_held.Clear();
			Recompute();
			return count;
		}

		internal bool Blocks(int action)
		{
			if (action >= 0 && action < 128)
			{
				return _blocked[action];
			}
			return false;
		}

		internal float Steered(bool sideways)
		{
			for (int num = _held.Count - 1; num >= 0; num--)
			{
				if (_held[num].Steers)
				{
					if (!sideways)
					{
						return _held[num].Forward;
					}
					return _held[num].Sideways;
				}
			}
			return 0f;
		}

		private void Recompute()
		{
			Array.Clear(_blocked);
			Array.Clear(_pressed);
			Array.Clear(_axes);
			bool flag = false;
			foreach (Entry item in _held)
			{
				flag |= item.Steers;
				if (item.AxisAction >= 0)
				{
					_axes[item.AxisAction] = true;
					flag = true;
				}
				for (int i = 0; i < 128; i++)
				{
					if (item.Pressed[i])
					{
						_pressed[i] = true;
						flag = true;
					}
				}
				for (int j = 0; j < 128; j++)
				{
					if (item.Blocked[j])
					{
						_blocked[j] = true;
						flag = true;
					}
				}
			}
			Engaged = flag;
		}
	}
	public readonly record struct LevelId(uint RundownId, int Tier, int Index)
	{
		public string Coordinate => RundownId.ToString(CultureInfo.InvariantCulture) + "-" + (char)(65 + Tier - 1) + (Index + 1).ToString(CultureInfo.InvariantCulture);

		public bool IsValid
		{
			get
			{
				if (RundownId != 0)
				{
					int tier = Tier;
					if (tier >= 1 && tier <= 5)
					{
						return Index >= 0;
					}
				}
				return false;
			}
		}

		public static bool TryParse(string? text, out LevelId id)
		{
			id = default(LevelId);
			if (text == null)
			{
				return false;
			}
			int num = text.IndexOf('-');
			if (num <= 0 || num + 2 >= text.Length)
			{
				return false;
			}
			if (!uint.TryParse(text.AsSpan(0, num), NumberStyles.None, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			char c = char.ToUpperInvariant(text[num + 1]);
			if ((c < 'A' || c > 'E') ? true : false)
			{
				return false;
			}
			if (!int.TryParse(text.AsSpan(num + 2), NumberStyles.None, CultureInfo.InvariantCulture, out var result2) || result2 < 1)
			{
				return false;
			}
			LevelId levelId = new LevelId(result, c - 65 + 1, result2 - 1);
			if (!levelId.IsValid)
			{
				return false;
			}
			id = levelId;
			return true;
		}
	}
	public readonly record struct LevelSeeds(int BuildSeed, int HostIdSeed, int SessionSeed);
	public sealed class ModLog
	{
		private readonly Action<string> _sink;

		private readonly HashSet<string> _seen = new HashSet<string>(StringComparer.Ordinal);

		public string ModId { get; }

		internal ModLog(string modId, Action<string> sink)
		{
			ModId = modId;
			_sink = sink;
		}

		public void Error(string message)
		{
			Write(message);
		}

		public void ErrorOnce(string message)
		{
			lock (_seen)
			{
				if (!_seen.Add(message))
				{
					return;
				}
			}
			Write(message);
		}

		private void Write(string message)
		{
			try
			{
				_sink(message);
			}
			catch (Exception ex)
			{
				Console.Error.WriteLine(ModId + ": " + message + " (log sink failed: " + ex?.ToString() + ")");
			}
		}
	}
	public static class Log
	{
		private static readonly Dictionary<string, ModLog> Logs = new Dictionary<string, ModLog>(StringComparer.Ordinal);

		internal static Func<string, Action<string>> Sinks { get; set; } = (string id) => delegate(string message)
		{
			Console.WriteLine("[Error: " + id + "] " + message);
		};

		public static string Num(double value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}

		public static string Pos(float x, float y, float z)
		{
			return "(" + x.ToString("0.00", CultureInfo.InvariantCulture) + ", " + y.ToString("0.00", CultureInfo.InvariantCulture) + ", " + z.ToString("0.00", CultureInfo.InvariantCulture) + ")";
		}

		public static ModLog For(string modId)
		{
			if (string.IsNullOrWhiteSpace(modId))
			{
				throw new ArgumentException("Name the mod (its plugin GUID).", "modId");
			}
			lock (Logs)
			{
				if (Logs.TryGetValue(modId, out ModLog value))
				{
					return value;
				}
				value = new ModLog(modId, Sinks(modId));
				Logs.Add(modId, value);
				return value;
			}
		}

		public static string Pos(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return Pos(position.x, position.y, position.z);
		}
	}
	internal static class Modules
	{
		internal readonly record struct Target(string Type, string Method);

		internal static readonly (string Entry, string Summary)[] Public = new(string, string)[31]
		{
			("Assets", "Loads asset bundles (each file once, reused afterwards), returns assets and shader bytes; a bad bundle or a missing asset fails explicitly and is logged."),
			("Log", "A log source per mod ID; only errors are written, the same error once with counts at 1, 10 and 100; formats a number or a position for a message."),
			("Settings", "Binds BepInEx configuration entries per mod ID and tells the mod when one changes at run time."),
			("Pool", "A fixed-capacity object pool that prewarms on demand and allocates nothing on take or return."),
			("Net", "Per-mod network channels with automatic message-format fingerprints (a mismatching player is refused on both sides), reliable or droppable delivery, host broadcast, point to point, per-player lobby data and late-join resend."),
			("Store", "Local progress as one JSON file per mod; a file that cannot be read is reported and never overwritten."),
			("InputLease", "Takes over game input actions for the local player; leases stack and are released at level cleanup and when the player goes down."),
			("GameEvents", "Main-thread events for host change, text language, end of a level, checkpoint reload, the local player going down, the lobby opening or closing and the expedition being chosen (host and clients)."),
			("Enemies", "Tracks live enemies, queries nearby enemies, reads the four alert levels, requests enemy spawns, leases slowdowns and slows every enemy inside a zone for a time."),
			("ColliderQuery", "Sphere queries over colliders with a reusable buffer; a full buffer is reported as truncated, never as complete."),
			("Level", "Finds the course node and dimension of a position, finds a free stand spot near an object, finds a walkable path, a line of sight and a blocked move between points, looks up dimensions, zones and areas, finds the in-level agent of a player by lookup, and reads the active expedition."),
			("HoldPoint", "The game's timed hold point (Interact_Timed): sets up the prompt, duration and callbacks, and shuts a point so it stays shut when the game switches it on again, until it is opened."),
			("Il2CppArrays", "Copies whole game arrays to and from managed arrays and uploads texture pixels in one write."),
			("PlayerNoise", "Makes sleeping enemies hear a player's movement noise as a multiple of its distance; leases end with the level."),
			("Combat", "The game's own damage, push, melee, explosion, force and noise requests, reporting what the game actually settled, a bullet kill event, a shot from a point at a collider settled as a stock bullet, the bullet ray that steps over the shooter's own colliders, plus which receiver a collider belongs to."),
			("Gear", "Swaps the local player's gun with its assets preloaded, writes ammunition back through the game, and arbitrates the special weapon slot."),
			("PlayerHits", "The one patch on the game's player hit path (OnIncomingDamage, ModifyInfection): mods redirect the damage of a hit, refuse an infection write or read both through registrations instead of patching the methods themselves."),
			("Compact", "Allocation-free compact encoding: variable-length and zig-zag integers, deltas, range quantization and smallest-three quaternions; a malformed read throws, never guesses."),
			("ChunkLog", "An append-only file of independently compressed, checksummed chunks with a trailing index; after a crash the reader scans chunk headers and keeps every whole chunk, and another format version is refused with the difference named."),
			("Items", "Spawns an item pickup the way the game does, in play as well as before it; the Spawned event reports every item the game spawns, so no other package patches the game's spawn method."),
			("SoloDrop", "Starts one chosen expedition as a private solo host with chosen seeds through the game's own menu calls; stops at the first unexpected state and never retries."),
			("Fingerprint", "A stable SHA-256 fingerprint over labelled values, with floats rounded to a stated precision."),
			("ContentHash", "Hashes the contents of any object graph by its public fields, properties and items, so equal contents hash equally across processes."),
			("DataBlockHashes", "Content hashes of the loaded game data blocks, one per block type, read from memory."),
			("InstalledPlugins", "The loaded BepInEx plugins with version and DLL hash, and one fingerprint over them."),
			("DataBlocks", "Adds new gear and item data blocks to the game's tables; an ID already in use is refused."),
			("Frame", "Per-frame callbacks for mods (update, late update, quit) run from ForgeCore's one driver; a callback that throws is reported once and never stops the others."),
			("Patches", "Puts a mod's own Harmony patches on at first use and keeps them, so a session that never uses the feature never pays for them."),
			("StrictJson", "Strict reading of a JSON object: unknown or repeated fields are refused, a wrong type names its field, a number outside its range is refused, never clamped; also canonical JSON."),
			("GameHooks", "The game methods that more than one package reads, each patched here once and handed to every subscriber around the game's own body: door, reactor, scan, container, generator, pickup and terminal state callbacks, noise, scout and wave events, a player's damage receive entries, a revive, the four firing bodies and the weapon ray casts, push, sentry placement and every sentry lifecycle step, mine placement, item spawn, level cleanup, marker and prefab placement, survival waves, the end and the failure check of an expedition, enemy setup, death, despawn, destruction, mode change, spawn sync and received damage, the local player's death, the bullet hit entry, weapon wield, dry fire, gear assembly, reload start, the deployed marker, sentry spawn, the objective panel and intel messages, host migration, and the game event log; no package patches these methods itself."),
			("Fnv", "FNV-1a 32 and 64 bit over UTF-8 text or bytes, the one hash behind wire formats and saved keys.")
		};

		internal const string SpecialSlot = "Gear.SpecialSlot";

		internal const string GearReload = "Gear.Reload";

		internal const string PlayerNoise = "PlayerNoise";

		internal const string PlayerHits = "PlayerHits";

		internal const string CombatKills = "Combat.Killed";

		internal const string Language = "GameEvents.Language";

		internal const string ExpeditionSelected = "GameEvents.ExpeditionSelected";

		internal const string ItemSpawned = "Items.Spawned";

		internal const string HoldPointShut = "HoldPoint.Shut";

		internal const string StackedVolumes = "Level.StackedVolumes";

		internal static readonly (string Module, Target[] Targets)[] Patched = new(string, Target[])[76]
		{
			("Gear.SpecialSlot", new Target[2]
			{
				new Target("Player.PlayerAmmoStorage", "PickupAmmo"),
				new Target("Player.PlayerAmmoStorage", "NeedWeaponAmmo")
			}),
			("Gear.Reload", new Target[1]
			{
				new Target("Gear.BulletWeapon", "get_ReloadTime")
			}),
			("PlayerHits", new Target[2]
			{
				new Target("Dam_PlayerDamageBase", "OnIncomingDamage"),
				new Target("Dam_PlayerDamageBase", "ModifyInfection")
			}),
			("PlayerNoise", new Target[1]
			{
				new Target("Enemies.EnemyDetection", "UpdateDirectionDistanceToTarget_Hibernate")
			}),
			("Combat.Killed", new Target[1]
			{
				new Target("Dam_EnemyDamageBase", "ReceiveBulletDamage")
			}),
			("GameEvents.Language", new Target[1]
			{
				new Target("Localization.GameDataTextLocalizationService", "SetCurrentLanguage")
			}),
			("GameEvents.ExpeditionSelected", new Target[2]
			{
				new Target("RundownManager", "MasterSelectActiveExpedition"),
				new Target("RundownManager", "SetActiveExpedition")
			}),
			("HoldPoint.Shut", new Target[1]
			{
				new Target("Interact_Timed", "SetActive")
			}),
			("GameHooks.WeakDoorState", new Target[1]
			{
				new Target("LevelGeneration.LG_WeakDoor", "OnSyncDoorStateChange")
			}),
			("GameHooks.ReactorState", new Target[1]
			{
				new Target("LevelGeneration.LG_WardenObjective_Reactor", "OnStateChange")
			}),
			("GameHooks.PuzzleState", new Target[1]
			{
				new Target("ChainedPuzzles.ChainedPuzzleInstance", "OnStateChange")
			}),
			("GameHooks.ResourceContainerState", new Target[1]
			{
				new Target("LevelGeneration.LG_ResourceContainer_Sync", "OnStateChange")
			}),
			("GameHooks.GeneratorState", new Target[1]
			{
				new Target("LevelGeneration.LG_PowerGenerator_Core", "OnStateChange")
			}),
			("GameHooks.PickupItemState", new Target[1]
			{
				new Target("LevelGeneration.LG_PickupItem_Sync", "OnStateChange")
			}),
			("GameHooks.TerminalCommand", new Target[1]
			{
				new Target("LevelGeneration.LG_ComputerTerminalCommandInterpreter", "ReceiveCommand")
			}),
			("GameHooks.NoiseMade", new Target[1]
			{
				new Target("NoiseManager", "MakeNoise")
			}),
			("GameHooks.ScoutScreamActivated", new Target[1]
			{
				new Target("Enemies.ES_ScoutScream", "ActivateState")
			}),
			("GameHooks.ScoutDetectionEntered", new Target[1]
			{
				new Target("ES_ScoutDetection", "CommonEnter")
			}),
			("GameHooks.EnemyExplosionReceived", new Target[1]
			{
				new Target("Dam_EnemyDamageBase", "ReceiveExplosionDamage")
			}),
			("GameHooks.WaveStopped", new Target[1]
			{
				new Target("SurvivalWave", "StopEvent")
			}),
			("GameHooks.FirstPersonHolderLateUpdate", new Target[1]
			{
				new Target("FirstPersonItemHolder", "LateUpdate")
			}),
			("GameHooks.ReloadAnimationStarted", new Target[1]
			{
				new Target("ItemEquippable", "TryTriggerReloadAnimationSequence")
			}),
			("GameHooks.EnemyAttackStarted", new Target[1]
			{
				new Target("Enemies.ES_EnemyAttackBase", "DoStartAttack")
			}),
			("GameHooks.EnemyHitreacted", new Target[1]
			{
				new Target("ES_Hitreact", "DoHitReact")
			}),
			("GameHooks.HibernateWakeActivated", new Target[1]
			{
				new Target("Enemies.ES_HibernateWakeUp", "ActivateState")
			}),
			("GameHooks.EnemyScreamStarted", new Target[1]
			{
				new Target("Enemies.ES_Scream", "ActivateState")
			}),
			("GameHooks.EnemyScreamed", new Target[1]
			{
				new Target("Enemies.ES_Scream", "OnStateShouldBeActivated")
			}),
			("GameHooks.PlayerReceived", new Target[6]
			{
				new Target("Dam_PlayerDamageBase", "ReceiveBulletDamage"),
				new Target("Dam_PlayerDamageBase", "ReceiveMeleeDamage"),
				new Target("Dam_PlayerDamageBase", "ReceiveExplosionDamage"),
				new Target("Dam_PlayerDamageBase", "ReceiveTentacleAttackDamage"),
				new Target("Dam_PlayerDamageBase", "ReceiveShooterProjectileDamage"),
				new Target("Dam_PlayerDamageBase", "ReceiveFallDamage")
			}),
			("GameHooks.PlayerRevived", new Target[1]
			{
				new Target("PLOC_Downed", "OnPlayerRevived")
			}),
			("GameHooks.Fire.BulletWeapon", new Target[1]
			{
				new Target("Gear.BulletWeapon", "Fire")
			}),
			("GameHooks.Fire.Shotgun", new Target[1]
			{
				new Target("Gear.Shotgun", "Fire")
			}),
			("GameHooks.Fire.BulletWeaponSynced", new Target[1]
			{
				new Target("Gear.BulletWeaponSynced", "Fire")
			}),
			("GameHooks.Fire.ShotgunSynced", new Target[1]
			{
				new Target("Gear.ShotgunSynced", "Fire")
			}),
			("GameHooks.MuzzleRay", new Target[1]
			{
				new Target("Weapon", "CastWeaponRay")
			}),
			("GameHooks.AlignedRay", new Target[1]
			{
				new Target("Weapon", "CastWeaponRay")
			}),
			("GameHooks.PushEntered", new Target[1]
			{
				new Target("Gear.MWS_Push", "Enter")
			}),
			("GameHooks.SentryGearSpawned", new Target[1]
			{
				new Target("SentryGunInstance", "OnGearSpawnComplete")
			}),
			("GameHooks.SentryDespawned", new Target[1]
			{
				new Target("SentryGunInstance", "OnDespawn")
			}),
			("GameHooks.SentryLifecycle", new Target[7]
			{
				new Target("SentryGunInstance", "OnGearSpawnComplete"),
				new Target("SentryGunInstance", "PickUp"),
				new Target("SentryGunInstance", "SyncedPickup"),
				new Target("SentryGunInstance", "OnDespawn"),
				new Target("SentryGunInstance", "OnDestroy"),
				new Target("SentryGunInstance", "OnBackpackCleanupCallback"),
				new Target("SentryGunInstance", "CleanUp")
			}),
			("GameHooks.MineSpawned", new Target[1]
			{
				new Target("MineDeployerInstance", "OnSpawn")
			}),
			("GameHooks.MinePickedUp", new Target[1]
			{
				new Target("MineDeployerInstance", "SyncedPickup")
			}),
			("GameHooks.LevelCleanup", new Target[1]
			{
				new Target("LevelGeneration.Builder", "OnLevelCleanup")
			}),
			("GameHooks.MarkerSpawned", new Target[1]
			{
				new Target("LG_MarkerSpawner", "TrySpawnRandomPrefab")
			}),
			("GameHooks.PrefabBuilt", new Target[1]
			{
				new Target("LevelGeneration.LG_PrefabSpawner", "OnBuild")
			}),
			("GameHooks.SurvivalWaveStarted", new Target[1]
			{
				new Target("Mastermind", "TriggerSurvivalWave")
			}),
			("GameHooks.ExpeditionEnded", new Target[1]
			{
				new Target("RundownManager", "OnExpeditionEnded")
			}),
			("GameHooks.ExpeditionFailedChecked", new Target[1]
			{
				new Target("WardenObjectiveManager", "CheckExpeditionFailed")
			}),
			("GameHooks.PlayerDespawned", new Target[1]
			{
				new Target("Player.PlayerManager", "OnPlayerDespawned")
			}),
			("GameHooks.EnemySetup", new Target[1]
			{
				new Target("Enemies.EnemyAgent", "Setup")
			}),
			("GameHooks.EnemyDead", new Target[1]
			{
				new Target("Enemies.EnemyAgent", "OnDead")
			}),
			("GameHooks.EnemyDespawned", new Target[1]
			{
				new Target("Enemies.EnemyAgent", "OnDeSpawn")
			}),
			("GameHooks.EnemyDestroyed", new Target[1]
			{
				new Target("Enemies.EnemyAgent", "OnDestroy")
			}),
			("GameHooks.EnemyModeChanged", new Target[1]
			{
				new Target("Enemies.EnemyAI", "ModeChange")
			}),
			("GameHooks.EnemySyncSpawned", new Target[1]
			{
				new Target("Enemies.EnemySync", "OnSpawn")
			}),
			("GameHooks.EnemySyncDespawned", new Target[1]
			{
				new Target("Enemies.EnemySync", "OnDespawn")
			}),
			("GameHooks.EnemyDamageReceived", new Target[1]
			{
				new Target("Dam_EnemyDamageBase", "ProcessReceivedDamage")
			}),
			("GameHooks.LocalPlayerDied", new Target[1]
			{
				new Target("Dam_PlayerDamageLocal", "ReceiveSetDead")
			}),
			("GameHooks.BulletHit", new Target[1]
			{
				new Target("Gear.BulletWeapon", "BulletHit")
			}),
			("GameHooks.BulletWeaponWielded", new Target[1]
			{
				new Target("Gear.BulletWeapon", "OnWield")
			}),
			("GameHooks.BulletWeaponUnwielded", new Target[1]
			{
				new Target("Gear.BulletWeapon", "OnUnWield")
			}),
			("GameHooks.AutoDryFired", new Target[1]
			{
				new Target("Gear.BWA_Auto", "OnFireShotEmptyClip")
			}),
			("GameHooks.SemiDryFired", new Target[1]
			{
				new Target("Gear.BWA_Semi", "OnFireShotEmptyClip")
			}),
			("GameHooks.GearPartsSpawned", new Target[1]
			{
				new Target("Gear.GearPartHolder", "OnAllPartsSpawned")
			}),
			("GameHooks.GearSpawnCompleted", new Target[1]
			{
				new Target("ItemEquippable", "OnGearSpawnComplete")
			}),
			("GameHooks.ReloadTriggered", new Target[1]
			{
				new Target("ItemEquippable", "TryTriggerReloadAnimationSequence")
			}),
			("GameHooks.BackpackDeployed", new Target[1]
			{
				new Target("Player.PlayerBackpack", "SetDeployed")
			}),
			("GameHooks.SentrySpawned", new Target[1]
			{
				new Target("SentryGunInstance", "OnSpawn")
			}),
			("GameHooks.ProgressionObjectiveSet", new Target[1]
			{
				new Target("PUI_GameObjectives", "SetProgressionObjective")
			}),
			("GameHooks.ProgressionObjectiveRemoved", new Target[1]
			{
				new Target("PUI_GameObjectives", "RemoveProgressionObjective")
			}),
			("GameHooks.ObjectivesCleaned", new Target[1]
			{
				new Target("PUI_GameObjectives", "OnLevelCleanup")
			}),
			("GameHooks.SubObjectiveShown", new Target[1]
			{
				new Target("PUI_WardenIntel", "ShowSubObjectiveMessage")
			}),
			("GameHooks.PrioritySubObjectiveShown", new Target[1]
			{
				new Target("PUI_WardenIntel", "ShowPrioritySubObjectiveMessage")
			}),
			("GameHooks.MasterMigrationFound", new Target[1]
			{
				new Target("SNetwork.SNet_SyncManager", "OnFoundNewMasterDuringMigration")
			}),
			("GameHooks.GameLogAdded", new Target[1]
			{
				new Target("PUI_GameEventLog", "AddLogItem")
			}),
			("Items.Spawned", new Target[1]
			{
				new Target("ItemReplicationManager", "OnItemSpawn")
			}),
			("Level.StackedVolumes", new Target[1]
			{
				new Target("AIGraph.AIG_GeomorphNodeVolume", "TryGetGeomorphVolume")
			})
		};

		internal static Target[] TargetsOf(string module)
		{
			(string, Target[])[] patched = Patched;
			for (int i = 0; i < patched.Length; i++)
			{
				var (text, result) = patched[i];
				if (text == module)
				{
					return result;
				}
			}
			throw new ArgumentException("Unknown module " + module + ".", "module");
		}
	}
	public readonly record struct NativeDataBlockRegistration(string Type, string Name, uint PersistentId, string Json, uint? SourceId = null);
	public sealed class NativeDataBlockRollbackException : Exception
	{
		internal NativeDataBlockRollbackException(Exception error)
			: base("Native table rollback failed.", error)
		{
		}
	}
	public sealed class NativeDataBlockConflictException : Exception
	{
		internal NativeDataBlockConflictException(string message)
			: base(message)
		{
		}
	}
	public static class NativeDataBlockTables
	{
		private sealed class Prepared
		{
			internal Type Table;

			internal NativeDataBlockRegistration Definition;

			internal object Value;

			internal JsonElement? Supplied;

			internal object Blocks;

			internal object ById;

			internal object ByName;

			internal bool BlockAttempted;

			internal bool IdAttempted;

			internal bool NameAttempted;

			internal bool NameAdded;

			internal bool ReadbackFailed;

			internal Prepared(Type table, NativeDataBlockRegistration definition, object value, JsonElement? supplied)
			{
				Table = table;
				Definition = definition;
				Value = value;
				Supplied = supplied;
			}
		}

		private sealed record NativeBlockReader(Func<uint, object?> Get, Func<object, uint> PersistentId);

		private const BindingFlags Members = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		private const BindingFlags Fields = BindingFlags.Instance | BindingFlags.Public;

		private static readonly Dictionary<string, Type> NativeTypes = new Dictionary<string, Type>(StringComparer.Ordinal);

		private static readonly Dictionary<Type, MethodInfo> Deserializers = new Dictionary<Type, MethodInfo>();

		private static readonly Dictionary<Type, NativeBlockReader> BlockReaders = new Dictionary<Type, NativeBlockReader>();

		public static Exception? LastRollbackFailure { get; private set; }

		public static bool HasUnknownState => LastRollbackFailure != null;

		public static void RequireSourceLinks(string type, uint sourceId, IReadOnlyDictionary<string, uint> expected)
		{
			object target = RequireBlock(type, sourceId);
			foreach (KeyValuePair<string, uint> item in expected)
			{
				if (!(Get(target, item.Key) is uint num) || num != item.Value)
				{
					throw new InvalidOperationException("Native source link differs: " + type + "/" + sourceId + "." + item.Key);
				}
			}
		}

		public static object RequireBlock(string type, uint id)
		{
			NativeBlockReader nativeBlockReader = BlockReader(NativeType("GameData." + type + "DataBlock"));
			object obj = nativeBlockReader.Get(id);
			if (obj == null || nativeBlockReader.PersistentId(obj) != id)
			{
				throw new InvalidOperationException("Missing native block " + type + "/" + id);
			}
			return obj;
		}

		public static void Register(IReadOnlyList<NativeDataBlockRegistration> definitions, bool strictFields = false)
		{
			ArgumentNullException.ThrowIfNull(definitions, "definitions");
			Exception lastRollbackFailure = LastRollbackFailure;
			if (lastRollbackFailure != null)
			{
				throw new NativeDataBlockRollbackException(lastRollbackFailure);
			}
			List<Prepared> list = new List<Prepared>();
			HashSet<(string, uint)> hashSet = new HashSet<(string, uint)>();
			HashSet<(string, string)> hashSet2 = new HashSet<(string, string)>();
			foreach (NativeDataBlockRegistration definition in definitions)
			{
				if (definition.PersistentId == 0 || string.IsNullOrEmpty(definition.Name) || !hashSet.Add((definition.Type, definition.PersistentId)) || !hashSet2.Add((definition.Type, definition.Name)))
				{
					throw new InvalidOperationException("Duplicate or invalid native block identity: " + definition.Name);
				}
				Type type = NativeType("GameData." + definition.Type + "DataBlock");
				uint? sourceId = definition.SourceId;
				if (sourceId.HasValue)
				{
					uint valueOrDefault = sourceId.GetValueOrDefault();
					object obj = Call(type, "GetBlock", valueOrDefault);
					if (obj == null || (uint)Get(obj, "persistentID") != valueOrDefault)
					{
						throw new InvalidOperationException("Missing native source " + definition.Type + "/" + valueOrDefault);
					}
				}
				using JsonDocument jsonDocument = JsonDocument.Parse(definition.Json);
				if (strictFields)
				{
					Validate(jsonDocument.RootElement, type, definition.Name);
				}
				object obj2 = Deserialize(type, definition.Json);
				if (strictFields)
				{
					Validate(jsonDocument.RootElement, type, definition.Name, obj2, compare: true);
				}
				if ((uint)Get(obj2, "persistentID") != definition.PersistentId || (string)Get(obj2, "name") != definition.Name)
				{
					throw new InvalidOperationException("Native block identity changed during deserialization: " + definition.Name);
				}
				if ((bool)Call(Get(type, "s_blockByID"), "ContainsKey", definition.PersistentId))
				{
					object target = Call(Get(type, "s_blockByID"), "get_Item", definition.PersistentId);
					throw new NativeDataBlockConflictException("Native ID collision: " + definition.Name + " and loaded:" + definition.Type + "/" + Get(target, "name")?.ToString() + " / " + definition.PersistentId);
				}
				if ((bool)Call(Get(type, "s_blockIDByName"), "ContainsKey", definition.Name))
				{
					throw new NativeDataBlockConflictException("Native name collision: " + definition.Name + " / " + Call(Get(type, "s_blockIDByName"), "get_Item", definition.Name));
				}
				list.Add(new Prepared(type, definition, obj2, strictFields ? new JsonElement?(jsonDocument.RootElement.Clone()) : ((JsonElement?)null)));
			}
			try
			{
				foreach (Prepared item2 in list)
				{
					item2.Blocks = Get(Get(item2.Table, "Wrapper"), "Blocks");
					item2.ById = Get(item2.Table, "s_blockByID");
					item2.ByName = Get(item2.Table, "s_blockIDByName");
					if (Has(item2.ById, item2.Definition.PersistentId) || Has(item2.ByName, item2.Definition.Name))
					{
						throw new NativeDataBlockConflictException("Native identity became occupied: " + item2.Definition.Name);
					}
					Set(item2.Value, "Index", (int)Get(item2.Blocks, "Count"));
					item2.BlockAttempted = true;
					Call(item2.Blocks, "Add", item2.Value);
					item2.IdAttempted = true;
					Call(item2.ById, "Add", item2.Definition.PersistentId, item2.Value);
					item2.NameAttempted = true;
					Call(item2.ByName, "Add", item2.Definition.Name, item2.Definition.PersistentId);
					item2.NameAdded = true;
				}
				foreach (Prepared item3 in list)
				{
					try
					{
						object obj3 = Call(Get(item3.Table, "s_blockByID"), "get_Item", item3.Definition.PersistentId);
						object obj4 = Call(Get(item3.Table, "s_blockIDByName"), "get_Item", item3.Definition.Name);
						object obj5 = Get(Get(item3.Table, "Wrapper"), "Blocks");
						object obj6 = Call(obj5, "get_Item", (int)Get(item3.Value, "Index"));
						object obj7 = Call(item3.Table, "GetBlock", item3.Definition.PersistentId);
						if ((uint)Get(obj3, "persistentID") != item3.Definition.PersistentId || (uint)obj4 != item3.Definition.PersistentId || (uint)Get(obj6, "persistentID") != item3.Definition.PersistentId || (string)Get(obj6, "name") != item3.Definition.Name || obj7 == null || (uint)Get(obj7, "persistentID") != item3.Definition.PersistentId || (string)Get(obj7, "name") != item3.Definition.Name)
						{
							throw new InvalidOperationException("Native table readback failed: " + item3.Definition.Name);
						}
						JsonElement? supplied = item3.Supplied;
						if (supplied.HasValue)
						{
							JsonElement valueOrDefault2 = supplied.GetValueOrDefault();
							Validate(valueOrDefault2, item3.Table, item3.Definition.Name + ".byID", obj3, compare: true);
							Validate(valueOrDefault2, item3.Table, item3.Definition.Name + ".Wrapper", obj6, compare: true);
							Validate(valueOrDefault2, item3.Table, item3.Definition.Name + ".GetBlock", obj7, compare: true);
						}
						if (!SameObject(obj3, item3.Value) || !SameObject(obj6, item3.Value) || !SameObject(obj7, item3.Value) || !SameObject(Get(item3.Table, "s_blockByID"), item3.ById) || !SameObject(Get(item3.Table, "s_blockIDByName"), item3.ByName) || !SameObject(obj5, item3.Blocks))
						{
							throw new InvalidOperationException("Native publication was replaced: " + item3.Definition.Name);
						}
					}
					catch
					{
						item3.ReadbackFailed = true;
						throw;
					}
				}
			}
			catch (Exception ex)
			{
				List<Exception> failures = new List<Exception>();
				foreach (Prepared row in list.AsEnumerable().Reverse())
				{
					if (row.NameAttempted)
					{
						Undo(delegate
						{
							if (Has(row.ByName, row.Definition.Name) && (uint)Call(row.ByName, "get_Item", row.Definition.Name) == row.Definition.PersistentId && OwnsId(row))
							{
								if (!row.NameAdded)
								{
									throw new InvalidOperationException("Uncertain native name publication: " + row.Definition.Name);
								}
								Call(row.ByName, "Remove", row.Definition.Name);
								if (Has(row.ByName, row.Definition.Name) && (uint)Call(row.ByName, "get_Item", row.Definition.Name) == row.Definition.PersistentId)
								{
									throw new InvalidOperationException("Rollback left native name: " + row.Definition.Name);
								}
							}
						});
					}
					if (row.IdAttempted)
					{
						Undo(delegate
						{
							if (OwnsId(row))
							{
								Call(row.ById, "Remove", row.Definition.PersistentId);
							}
							if (OwnsId(row))
							{
								throw new InvalidOperationException("Rollback left native ID: " + row.Definition.Name);
							}
						});
					}
					if (!row.BlockAttempted)
					{
						continue;
					}
					Undo(delegate
					{
						int num = -1;
						int num2 = (int)Get(row.Blocks, "Count");
						for (int i = 0; i < num2; i++)
						{
							if (SameObject(Call(row.Blocks, "get_Item", i), row.Value))
							{
								num = i;
								break;
							}
						}
						if (num >= 0)
						{
							if (num != num2 - 1)
							{
								throw new InvalidOperationException("Rollback would shift another native row: " + row.Definition.Name);
							}
							Call(row.Blocks, "Remove", row.Value);
							for (int j = 0; j < (int)Get(row.Blocks, "Count"); j++)
							{
								if (SameObject(Call(row.Blocks, "get_Item", j), row.Value))
								{
									throw new InvalidOperationException("Rollback left native wrapper row: " + row.Definition.Name);
								}
							}
						}
					});
				}
				foreach (Prepared row2 in list)
				{
					if (row2.IdAttempted)
					{
						Undo(delegate
						{
							if (OwnsId(row2) || (row2.ReadbackFailed && Has(row2.ById, row2.Definition.PersistentId)))
							{
								throw new InvalidOperationException("Rollback left an owned or unreadable native ID: " + row2.Definition.Name);
							}
						});
					}
					if (row2.NameAttempted)
					{
						Undo(delegate
						{
							if (Has(row2.ByName, row2.Definition.Name) && (uint)Call(row2.ByName, "get_Item", row2.Definition.Name) == row2.Definition.PersistentId && (!Has(row2.ById, row2.Definition.PersistentId) || OwnsId(row2)))
							{
								throw new InvalidOperationException("Rollback left an uncertain native name: " + row2.Definition.Name);
							}
						});
					}
					if (!row2.BlockAttempted)
					{
						continue;
					}
					Undo(delegate
					{
						for (int i = 0; i < (int)Get(row2.Blocks, "Count"); i++)
						{
							object obj9 = Call(row2.Blocks, "get_Item", i);
							if (SameObject(obj9, row2.Value) || (row2.ReadbackFailed && (uint)Get(obj9, "persistentID") == row2.Definition.PersistentId))
							{
								throw new InvalidOperationException("Rollback left an owned or unreadable native wrapper row: " + row2.Definition.Name);
							}
						}
					});
				}
				if (failures.Count != 0)
				{
					LastRollbackFailure = new AggregateException(new Exception[1] { ex }.Concat(failures));
					throw new NativeDataBlockRollbackException(LastRollbackFailure);
				}
				throw;
				void Undo(Action action)
				{
					try
					{
						action();
					}
					catch (Exception item)
					{
						failures.Add(item);
					}
				}
			}
		}

		private static bool Has(object map, object key)
		{
			return (bool)Call(map, "ContainsKey", key);
		}

		private static bool OwnsId(Prepared row)
		{
			if (Has(row.ById, row.Definition.PersistentId))
			{
				return SameObject(Call(row.ById, "get_Item", row.Definition.PersistentId), row.Value);
			}
			return false;
		}

		private static bool SameObject(object? left, object right)
		{
			if (left == right)
			{
				return true;
			}
			if (left == null)
			{
				return false;
			}
			IntPtr intPtr = Pointer(left);
			if (intPtr != IntPtr.Zero)
			{
				return intPtr == Pointer(right);
			}
			return false;
			static IntPtr Pointer(object value)
			{
				Type type = value.GetType();
				while (type != null)
				{
					if (type.FullName == "Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase")
					{
						object obj = type.GetProperty("Pointer", BindingFlags.Instance | BindingFlags.Public)?.GetValue(value);
						if (obj is IntPtr)
						{
							return (IntPtr)obj;
						}
						return IntPtr.Zero;
					}
					type = type.BaseType;
				}
				return IntPtr.Zero;
			}
		}

		private static void Validate(JsonElement value, Type type, string path, object? actual = null, bool compare = false)
		{
			Type underlyingType = Nullable.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				if (value.ValueKind == JsonValueKind.Null)
				{
					if (compare && actual != null)
					{
						Mismatch();
					}
					return;
				}
				type = underlyingType;
			}
			if (type.FullName == "Localization.LocalizedText")
			{
				if (value.ValueKind == JsonValueKind.Number && value.TryGetUInt32(out var value2))
				{
					if (compare && (actual == null || !object.Equals(Get(actual, "Id"), value2)))
					{
						Mismatch();
					}
					return;
				}
				if (value.ValueKind == JsonValueKind.String)
				{
					if (compare && (actual == null || !object.Equals(Get(actual, "UntranslatedText"), value.GetString())))
					{
						Mismatch();
					}
					return;
				}
				throw new InvalidOperationException("Invalid native localized text: " + path);
			}
			bool flag2;
			object objA;
			if (type == typeof(string))
			{
				JsonValueKind valueKind = value.ValueKind;
				bool flag = ((valueKind == JsonValueKind.String || valueKind == JsonValueKind.Null) ? true : false);
				flag2 = flag;
				objA = (flag2 ? value.GetString() : null);
			}
			else if (type == typeof(bool))
			{
				JsonValueKind valueKind = value.ValueKind;
				bool flag = valueKind - 5 <= JsonValueKind.Object;
				flag2 = flag;
				objA = flag2 && value.GetBoolean();
			}
			else
			{
				if (type.IsEnum)
				{
					if (value.ValueKind == JsonValueKind.String)
					{
						string value3 = value.GetString();
						if (!Enum.GetNames(type).Contains<string>(value3, StringComparer.Ordinal))
						{
							throw new InvalidOperationException("Invalid native enum name: " + path);
						}
						objA = Enum.Parse(type, value3);
						if (compare && !object.Equals(objA, actual))
						{
							Mismatch();
						}
					}
					else
					{
						Type underlyingType2 = Enum.GetUnderlyingType(type);
						Validate(value, underlyingType2, path, (compare && actual != null) ? Convert.ChangeType(actual, underlyingType2, CultureInfo.InvariantCulture) : actual, compare);
					}
					return;
				}
				if (type == typeof(byte))
				{
					flag2 = value.TryGetByte(out var value4);
					objA = value4;
				}
				else if (type == typeof(sbyte))
				{
					flag2 = value.TryGetSByte(out var value5);
					objA = value5;
				}
				else if (type == typeof(short))
				{
					flag2 = value.TryGetInt16(out var value6);
					objA = value6;
				}
				else if (type == typeof(ushort))
				{
					flag2 = value.TryGetUInt16(out var value7);
					objA = value7;
				}
				else if (type == typeof(int))
				{
					flag2 = value.TryGetInt32(out var value8);
					objA = value8;
				}
				else if (type == typeof(uint))
				{
					flag2 = value.TryGetUInt32(out var value9);
					objA = value9;
				}
				else if (type == typeof(long))
				{
					flag2 = value.TryGetInt64(out var value10);
					objA = value10;
				}
				else if (type == typeof(ulong))
				{
					flag2 = value.TryGetUInt64(out var value11);
					objA = value11;
				}
				else if (type == typeof(float))
				{
					flag2 = value.TryGetSingle(out var value12) && float.IsFinite(value12);
					objA = value12;
				}
				else
				{
					if (!(type == typeof(double)))
					{
						Type type2 = ListElementType(type);
						if (type2 != null)
						{
							if (value.ValueKind != JsonValueKind.Array)
							{
								throw new InvalidOperationException("Expected native list: " + path);
							}
							PropertyInfo propertyInfo = (type.IsArray ? null : type.GetProperty("Item", BindingFlags.Instance | BindingFlags.Public, null, type2, new Type[1] { typeof(int) }, null));
							if (compare)
							{
								if (actual == null)
								{
									Mismatch();
								}
								if ((type.IsArray ? ((Array)actual).Length : ((int)Get(actual, (type.GetProperty("Count", BindingFlags.Instance | BindingFlags.Public) != null) ? "Count" : "Length"))) != value.GetArrayLength())
								{
									Mismatch();
								}
							}
							int num = 0;
							{
								foreach (JsonElement item in value.EnumerateArray())
								{
									object actual2 = ((!compare) ? null : (type.IsArray ? ((Array)actual).GetValue(num) : propertyInfo.GetValue(actual, new object[1] { num })));
									Validate(item, type2, path + "[" + num++ + "]", actual2, compare);
								}
								return;
							}
						}
						if (value.ValueKind != JsonValueKind.Object)
						{
							throw new InvalidOperationException("Expected native object: " + path);
						}
						if (compare && actual == null)
						{
							Mismatch();
						}
						HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
						{
							foreach (JsonProperty item2 in value.EnumerateObject())
							{
								if (!hashSet.Add(item2.Name))
								{
									throw new InvalidOperationException("Duplicate native field: " + path + "." + item2.Name);
								}
								PropertyInfo property = type.GetProperty(item2.Name, BindingFlags.Instance | BindingFlags.Public);
								FieldInfo field = type.GetField(item2.Name, BindingFlags.Instance | BindingFlags.Public);
								Type type3 = (((object)property != null && property.CanRead && property.CanWrite && property.GetIndexParameters().Length == 0) ? property.PropertyType : (((object)field != null && !field.IsInitOnly) ? field.FieldType : null));
								if (type3 == null)
								{
									throw new InvalidOperationException("Unknown or unwritable native field: " + path + "." + item2.Name);
								}
								object actual3 = ((!compare) ? null : ((property != null) ? property.GetValue(actual) : field.GetValue(actual)));
								Validate(item2.Value, type3, path + "." + item2.Name, actual3, compare);
							}
							return;
						}
					}
					flag2 = value.TryGetDouble(out var value13) && double.IsFinite(value13);
					objA = value13;
				}
			}
			if (!flag2)
			{
				throw new InvalidOperationException("Invalid native field type: " + path + " (" + type.Name + ")");
			}
			if (compare && !object.Equals(objA, actual))
			{
				Mismatch();
			}
			void Mismatch()
			{
				throw new InvalidOperationException("Native field value changed: " + path);
			}
		}

		private static Type? ListElementType(Type type)
		{
			if (type.IsArray)
			{
				return type.GetElementType();
			}
			Type type2 = type;
			while (type2 != null)
			{
				bool flag = type2.IsGenericType;
				if (flag)
				{
					bool flag2;
					switch (type2.GetGenericTypeDefinition().FullName)
					{
					case "System.Collections.Generic.List`1":
					case "Il2CppSystem.Collections.Generic.List`1":
					case "Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppArrayBase`1":
						flag2 = true;
						break;
					default:
						flag2 = false;
						break;
					}
					flag = flag2;
				}
				if (flag)
				{
					return type2.GetGenericArguments()[0];
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		private static NativeBlockReader BlockReader(Type type)
		{
			if (BlockReaders.TryGetValue(type, out NativeBlockReader value))
			{
				return value;
			}
			MethodInfo? obj = type.GetMethod("GetBlock", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(uint) }, null) ?? throw new MissingMethodException(type.FullName, "GetBlock(uint)");
			if (!obj.IsStatic)
			{
				throw new MissingMethodException(type.FullName, "static GetBlock(uint)");
			}
			PropertyInfo property = type.GetProperty("persistentID", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			FieldInfo field = type.GetField("persistentID", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (property == null && field == null)
			{
				throw new MissingMemberException(type.FullName, "persistentID");
			}
			ParameterExpression parameterExpression = Expression.Parameter(typeof(uint), "id");
			ParameterExpression parameterExpression2 = Expression.Parameter(typeof(object), "value");
			UnaryExpression expression = Expression.Convert(parameterExpression2, type);
			MemberExpression memberExpression = ((property != null) ? Expression.Property(expression, property) : Expression.Field(expression, field));
			if (memberExpression.Type != typeof(uint))
			{
				throw new InvalidOperationException("Native persistentID is not UInt32: " + type.FullName);
			}
			NativeBlockReader nativeBlockReader = new NativeBlockReader(Expression.Lambda<Func<uint, object>>(Expression.Convert(Expression.Call(obj, parameterExpression), typeof(object)), new ParameterExpression[1] { parameterExpression }).Compile(), Expression.Lambda<Func<object, uint>>(Expression.Convert(memberExpression, typeof(uint)), new ParameterExpression[1] { parameterExpression2 }).Compile());
			BlockReaders.Add(type, nativeBlockReader);
			return nativeBlockReader;
		}

		private static Type NativeType(string name)
		{
			if (NativeTypes.TryGetValue(name, out Type value))
			{
				return value;
			}
			Type type = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
				select assembly.GetType(name, throwOnError: false)).FirstOrDefault((Type type2) => type2 != null) ?? throw new InvalidOperationException("Native type unavailable: " + name);
			NativeTypes.Add(name, type);
			return type;
		}

		private static object? Get(object target, string name)
		{
			Type type = (target as Type) ?? target.GetType();
			object obj = ((target is Type) ? null : target);
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (!(property != null))
			{
				return (type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) ?? throw new MissingMemberException(type.FullName, name)).GetValue(obj);
			}
			return property.GetValue(obj);
		}

		private static void Set(object target, string name, object? value)
		{
			Type type = target.GetType();
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (property != null)
			{
				property.SetValue(target, value);
			}
			else
			{
				(type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) ?? throw new MissingMemberException(type.FullName, name)).SetValue(target, value);
			}
		}

		private static object? Call(object target, string name, params object?[] args)
		{
			return ((target as Type) ?? target.GetType()).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Single((MethodInfo candidate) => candidate.Name == name && !candidate.IsGenericMethodDefinition && candidate.GetParameters().Length == args.Length && candidate.GetParameters().Select((ParameterInfo parameter, int index) => args[index] == null || parameter.ParameterType.IsInstanceOfType(args[index])).All((bool match) => match)).Invoke((target is Type) ? null : target, args);
		}

		private static object Deserialize(Type type, string json)
		{
			Type type2 = NativeType("CellJSON");
			if (!Deserializers.TryGetValue(type, out MethodInfo value))
			{
				Deserializers.Add(type, value = type2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Single((MethodInfo candidate) => candidate.Name == "Deserialize" && candidate.IsGenericMethodDefinition).MakeGenericMethod(type));
			}
			return value.Invoke(null, new object[2]
			{
				json,
				Get(type2, "JSONSettings_GameData")
			}) ?? throw new InvalidOperationException("Native block deserialization returned null.");
		}
	}
	public enum PeerState : byte
	{
		Unknown,
		Compatible,
		Refused
	}
	public enum NetDelivery : byte
	{
		Reliable,
		Lossy
	}
	internal interface INetTransport
	{
		ulong Local { get; }

		ulong Host { get; }

		void Register<T>(string name, Action<ulong, T> receive) where T : unmanaged;

		void RegisterBytes(string name, Action<ulong, byte[]> receive);

		void Send<T>(string name, T message, ulong to, NetDelivery delivery) where T : unmanaged;

		void SendBytes(string name, byte[] data, ulong to, NetDelivery delivery);

		int Others(ulong[] into);
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	internal struct NetHello
	{
		internal ulong Format;

		internal ulong Content;

		internal byte Answer;

		internal static NetHello Of(ulong format, string content, bool answer)
		{
			return new NetHello
			{
				Format = format,
				Content = Fnv.Hash64(content),
				Answer = (answer ? ((byte)1) : ((byte)0))
			};
		}
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	internal struct NetReady : INetEpoch
	{
		internal ulong Epoch;

		internal int Ready;

		ulong INetEpoch.Epoch
		{
			readonly get
			{
				return Epoch;
			}
			set
			{
				Epoch = value;
			}
		}
	}
	internal static class NetFormat
	{
		internal readonly struct Entry
		{
			internal readonly string Name;

			internal readonly NetDelivery Delivery;

			internal readonly ulong Layout;

			internal Entry(string name, NetDelivery delivery, ulong layout)
			{
				Name = name;
				Delivery = delivery;
				Layout = layout;
			}
		}

		internal const string BytesLayout = "bytes";

		internal static string LayoutOf<T>() where T : unmanaged
		{
			StringBuilder stringBuilder = new StringBuilder();
			Describe(typeof(T), stringBuilder);
			return stringBuilder.Append('#').Append(Unsafe.SizeOf<T>()).ToString();
		}

		private static void Describe(Type type, StringBuilder text)
		{
			if (type.IsEnum)
			{
				text.Append("enum:").Append(Enum.GetUnderlyingType(type).Name);
				return;
			}
			if (type.IsPrimitive)
			{
				text.Append(type.Name);
				return;
			}
			text.Append('{');
			bool flag = true;
			foreach (FieldInfo item in from f in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				orderby f.MetadataToken
				select f)
			{
				if (!flag)
				{
					text.Append(',');
				}
				flag = false;
				text.Append(item.Name).Append(':');
				FixedBufferAttribute customAttribute = item.GetCustomAttribute<FixedBufferAttribute>();
				if (customAttribute != null)
				{
					Describe(customAttribute.ElementType, text);
					text.Append('[').Append(customAttribute.Length).Append(']');
				}
				else
				{
					Describe(item.FieldType, text);
				}
			}
			text.Append('}');
		}

		internal static ulong Fingerprint(List<Entry> entries)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Entry item in entries.OrderBy<Entry, string>((Entry e) => e.Name, StringComparer.Ordinal))
			{
				stringBuilder.Append(item.Name).Append('|').Append((int)item.Delivery)
					.Append('|')
					.Append(item.Layout.ToString("x16", CultureInfo.InvariantCulture))
					.Append('\n');
			}
			return Fnv.Hash64(stringBuilder.ToString());
		}

		internal static byte[] Write(List<Entry> entries)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(entries.Count);
				foreach (Entry entry in entries)
				{
					binaryWriter.Write(entry.Name);
					binaryWriter.Write((byte)entry.Delivery);
					binaryWriter.Write(entry.Layout);
				}
			}
			return memoryStream.ToArray();
		}

		internal static List<Entry>? Read(byte[] data)
		{
			try
			{
				using BinaryReader binaryReader = new BinaryReader(new MemoryStream(data), Encoding.UTF8);
				int num = binaryReader.ReadInt32();
				if (num < 0 || num > 4096)
				{
					return null;
				}
				List<Entry> list = new List<Entry>(num);
				for (int i = 0; i < num; i++)
				{
					list.Add(new Entry(binaryReader.ReadString(), (NetDelivery)binaryReader.ReadByte(), binaryReader.ReadUInt64()));
				}
				return list;
			}
			catch (Exception)
			{
				return null;
			}
		}

		internal static string Differences(List<Entry> here, List<Entry> there)
		{
			Dictionary<string, Entry> dictionary = new Dictionary<string, Entry>(StringComparer.Ordinal);
			foreach (Entry item in there)
			{
				dictionary[item.Name] = item;
			}
			List<string> list = new List<string>();
			foreach (Entry item2 in here.OrderBy<Entry, string>((Entry e) => e.Name, StringComparer.Ordinal))
			{
				if (!dictionary.TryGetValue(item2.Name, out var value))
				{
					list.Add("message '" + item2.Name + "' is missing there");
					continue;
				}
				if (value.Delivery != item2.Delivery)
				{
					list.Add("message '" + item2.Name + "' travels " + item2.Delivery.ToString() + " here but " + value.Delivery.ToString() + " there");
				}
				if (value.Layout != item2.Layout)
				{
					list.Add("message '" + item2.Name + "' has a different field layout");
				}
			}
			HashSet<string> hashSet = new HashSet<string>(here.Select((Entry e) => e.Name), StringComparer.Ordinal);
			foreach (Entry item3 in there.OrderBy<Entry, string>((Entry e) => e.Name, StringComparer.Ordinal))
			{
				if (!hashSet.Contains(item3.Name))
				{
					list.Add("message '" + item3.Name + "' exists only there");
				}
			}
			return string.Join("; ", list);
		}
	}
	public sealed class NetChannel
	{
		private sealed class Peer
		{
			internal PeerState State;

			internal bool Greeted;

			internal double GreetedAt;

			internal bool ReportedSilent;

			internal ulong RefusedFormat;

			internal ulong RefusedContent;

			internal bool ContentDiffers;

			internal bool Reported;

			internal List<NetFormat.Entry>? There;
		}

		private sealed class Route
		{
			internal string Event = "";

			internal NetDelivery Delivery;
		}

		internal const double AnswerSeconds = 10.0;

		private readonly INetTransport _transport;

		private readonly ModLog _log;

		private readonly int _capacity;

		private readonly object _gate = new object();

		private readonly Queue<(ulong Sender, Action Work, bool Lossy)> _inbox = new Queue<(ulong, Action, bool)>();

		private readonly Queue<(ulong Sender, NetHello Hello)> _hellos = new Queue<(ulong, NetHello)>();

		private readonly Dictionary<ulong, Peer> _peers = new Dictionary<ulong, Peer>();

		private readonly List<ulong> _gone = new List<ulong>(4);

		private readonly List<ulong> _silent = new List<ulong>(4);

		private readonly Queue<(ulong Sender, byte[] Data)> _formats = new Queue<(ulong, byte[])>();

		private readonly Dictionary<string, Route> _routes = new Dictionary<string, Route>(StringComparer.Ordinal);

		private readonly List<NetFormat.Entry> _entries = new List<NetFormat.Entry>();

		private ulong _format;

		private bool _formatDirty = true;

		private readonly List<NetLobbyValue> _lobby = new List<NetLobbyValue>();

		private readonly ulong[] _members = new ulong[8];

		private int _memberCount;

		private double _nextSilent = double.PositiveInfinity;

		private bool _greetAgain;

		private bool _announce;

		private string _content = "";

		private ulong _host;

		private ulong _local;

		private int _dropped;

		private int _lossyQueued;

		private volatile int _control;

		private bool _readiness;

		private readonly Dictionary<ulong, bool> _ready = new Dictionary<ulong, bool>();

		private readonly Dictionary<ulong, ulong> _highWater = new Dictionary<ulong, ulong>();

		private ulong _epochFrom;

		private bool _hostReady;

		private ulong _unlisted;

		private double _unlistedSince;

		private const double RosterRetrySeconds = 5.0;

		private ulong _epoch;

		internal const string LobbyPrefix = "lobby/";

		private const string ReadyEvent = "ForgeCore/ready";

		private bool _live;

		private bool _ready0;

		public string ModId { get; }

		public string Format => Fingerprint().ToString("x16", CultureInfo.InvariantCulture);

		public string Content
		{
			get
			{
				return _content;
			}
			set
			{
				if (!(_content == (value ?? "")))
				{
					_content = value ?? "";
					AskAgain();
				}
			}
		}

		public ulong Epoch
		{
			get
			{
				return _epoch;
			}
			set
			{
				if (_epoch != value)
				{
					_epoch = value;
					_announce = true;
				}
			}
		}

		public bool IsHost
		{
			get
			{
				if (_transport.Host != 0L)
				{
					return _transport.Host == _transport.Local;
				}
				return false;
			}
		}

		public bool Live
		{
			get
			{
				return _live;
			}
			set
			{
				if (_live != value)
				{
					_live = value;
					_announce = true;
				}
			}
		}

		public bool Ready
		{
			get
			{
				return _ready0;
			}
			set
			{
				if (_ready0 != value)
				{
					_ready0 = value;
					_announce = true;
				}
			}
		}

		public bool HostReady => _hostReady;

		public event Action<ulong, string>? Refused;

		public event Action? EpochChanged;

		public event Action<ulong>? PeerSynced;

		public event Action<ulong>? PeerSilent;

		internal NetChannel(INetTransport transport, string modId, int capacity)
		{
			if (string.IsNullOrWhiteSpace(modId))
			{
				throw new ArgumentException("Name the mod (its plugin GUID).", "modId");
			}
			if (capacity <= 0)
			{
				throw new ArgumentOutOfRangeException("capacity");
			}
			_transport = transport;
			ModId = modId;
			_capacity = capacity;
			_log = Log.For(modId);
			transport.Register(HelloName(modId), delegate(ulong sender, NetHello hello)
			{
				lock (_gate)
				{
					if (_hellos.Count < 64)
					{
						_hellos.Enqueue((sender, hello));
						_control++;
						return;
					}
				}
				ControlOverflow();
			});
			transport.RegisterBytes(FormatsName(modId), delegate(ulong sender, byte[] data)
			{
				lock (_gate)
				{
					if (_formats.Count < 64)
					{
						_formats.Enqueue((sender, data));
						_control++;
						return;
					}
				}
				ControlOverflow();
			});
		}

		private void ControlOverflow()
		{
			_log.ErrorOnce("The handshake queue of " + ModId + " overflowed (64 waiting); a handshake message was dropped, so that player may stay unanswered.");
		}

		private void AskAgain()
		{
			foreach (Peer value in _peers.Values)
			{
				value.State = PeerState.Unknown;
				value.Greeted = false;
				value.ReportedSilent = false;
			}
			_greetAgain = true;
		}

		private ulong Fingerprint()
		{
			if (_formatDirty)
			{
				_format = NetFormat.Fingerprint(_entries);
				_formatDirty = false;
			}
			return _format;
		}

		public PeerState State(ulong player)
		{
			if (!_peers.TryGetValue(player, out Peer value))
			{
				return PeerState.Unknown;
			}
			return value.State;
		}

		public string EventName(string name)
		{
			return ModId + "/" + name;
		}

		internal static string HelloName(string modId)
		{
			return "ForgeCore/handshake/" + modId;
		}

		internal static string FormatsName(string modId)
		{
			return "ForgeCore/formats/" + modId;
		}

		public void On<T>(string name, Action<ulong, T> handler) where T : unmanaged
		{
			On(name, handler, NetDelivery.Reliable);
		}

		public void On<T>(string name, Action<ulong, T> handler, NetDelivery delivery) where T : unmanaged
		{
			if (handler == null)
			{
				throw new ArgumentNullException("handler");
			}
			Route route = Claim(name, delivery, NetFormat.LayoutOf<T>());
			_transport.Register(route.Event, delegate(ulong sender, T message)
			{
				Enqueue(sender, delegate
				{
					handler(sender, message);
				}, delivery);
			});
		}

		public void OnEpoch<T>(string name, Action<ulong, T> handler, NetDelivery delivery = NetDelivery.Reliable) where T : unmanaged, INetEpoch
		{
			if (handler == null)
			{
				throw new ArgumentNullException("handler");
			}
			On(name, delegate(ulong sender, T message)
			{
				if (message.Epoch != 0L && message.Epoch == Epoch)
				{
					handler(sender, message);
				}
			}, delivery);
		}

		public bool TryToHost<T>(string name, T message) where T : unmanaged
		{
			if (_transport.Host == 0L)
			{
				return false;
			}
			ToHost(name, message);
			return true;
		}

		public void ToHost<T>(string name, T message) where T : unmanaged
		{
			Route route = RouteOf(name);
			INetTransport transport = _transport;
			string name2 = route.Event;
			if (_transport.Host == 0L)
			{
				throw new InvalidOperationException("No host to send " + name + " to.");
			}
			transport.Send(name2, message, _transport.Host, route.Delivery);
		}

		public void To<T>(ulong player, string name, T message) where T : unmanaged
		{
			Route route = RouteOf(name);
			INetTransport transport = _transport;
			string name2 = route.Event;
			if (player == 0L)
			{
				throw new ArgumentException("Name the player.", "player");
			}
			transport.Send(name2, message, player, route.Delivery);
		}

		public void Broadcast<T>(string name, T message) where T : unmanaged
		{
			Route route = RouteOf(name);
			_transport.Send(route.Event, message, 0uL, route.Delivery);
		}

		public NetLobbyValue Lobby(string name)
		{
			Route route = Claim("lobby/" + name, NetDelivery.Reliable, "bytes");
			NetLobbyValue value = new NetLobbyValue(this, name, _log);
			_transport.RegisterBytes(route.Event, delegate(ulong sender, byte[] data)
			{
				Enqueue(sender, delegate
				{
					value.Receive(sender, data);
				}, NetDelivery.Reliable);
			});
			_lobby.Add(value);
			return value;
		}

		internal bool IsMember(ulong player)
		{
			if (_host != 0L)
			{