Decompiled source of WKRace v0.9.0

WKRace.dll

Decompiled 3 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using WKRace.Protocol;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI (code, refactoring and documentation generation).")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")]
[assembly: AssemblyMetadata("AI_Model", "Claude")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WKRace.Protocol
{
	public sealed class BanReady : Message
	{
		public Guid MatchId;

		public List<string> Pool = new List<string>();

		public override MessageType Type => MessageType.BanReady;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			BanLists.Write(w, Pool);
		}

		public static BanReady Read(PacketReader r)
		{
			return new BanReady
			{
				MatchId = r.ReadGuid(),
				Pool = BanLists.Read(r)
			};
		}
	}
	public sealed class BanPick : Message
	{
		public Guid MatchId;

		public string Item = "";

		public override MessageType Type => MessageType.BanPick;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteString(Item, 48);
		}

		public static BanPick Read(PacketReader r)
		{
			return new BanPick
			{
				MatchId = r.ReadGuid(),
				Item = r.ReadString(48)
			};
		}
	}
	public sealed class BanState : Message
	{
		public Guid MatchId;

		public List<string> Pool = new List<string>();

		public List<string> Banned = new List<string>();

		public List<string> BannedBy = new List<string>();

		public string TurnName = "";

		public bool YourTurn;

		public int SecondsLeft;

		public bool Done;

		public override MessageType Type => MessageType.BanState;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			BanLists.Write(w, Pool);
			BanLists.Write(w, Banned);
			BanLists.Write(w, BannedBy, 24);
			w.WriteString(TurnName, 24);
			w.WriteBool(YourTurn);
			w.WriteInt32(SecondsLeft);
			w.WriteBool(Done);
		}

		public static BanState Read(PacketReader r)
		{
			BanState banState = new BanState();
			banState.MatchId = r.ReadGuid();
			banState.Pool = BanLists.Read(r);
			banState.Banned = BanLists.Read(r);
			banState.BannedBy = BanLists.Read(r, 24);
			banState.TurnName = r.ReadString(24);
			banState.YourTurn = r.ReadBool();
			banState.SecondsLeft = r.ReadInt32();
			banState.Done = r.ReadBool();
			if (banState.Banned.Count != banState.BannedBy.Count)
			{
				throw new ProtocolException("Ban lists differ in length");
			}
			return banState;
		}
	}
	internal static class BanLists
	{
		public static void Write(PacketWriter w, List<string> items, int maxChars = 48)
		{
			int num = items?.Count ?? 0;
			if (num > 16)
			{
				throw new ProtocolException("Too many items");
			}
			w.WriteByte((byte)num);
			for (int i = 0; i < num; i++)
			{
				w.WriteString(items[i], maxChars);
			}
		}

		public static List<string> Read(PacketReader r, int maxChars = 48)
		{
			int num = r.ReadByte();
			if (num > 16)
			{
				throw new ProtocolException("Too many items");
			}
			List<string> list = new List<string>(num);
			for (int i = 0; i < num; i++)
			{
				list.Add(r.ReadString(maxChars));
			}
			return list;
		}
	}
	public sealed class BugReport
	{
		public const byte FormatVersion = 1;

		public const int MaxTitle = 120;

		public const int MaxDescription = 8000;

		public const int MaxContact = 120;

		public const int MaxInfo = 2000;

		public const int MaxFileName = 100;

		public const int MaxAttachments = 10;

		public const int MaxAttachmentBytes = 20971520;

		public const int MaxFileBytes = 15728640;

		public const int MaxLogBytes = 1048576;

		public const int MaxBodyBytes = 22282240;

		public static readonly string[] AllowedExtensions = new string[8] { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".txt", ".log" };

		public Guid PlayerId;

		public byte[] Token = new byte[32];

		public string Title = "";

		public string Description = "";

		public string Contact = "";

		public string Info = "";

		public byte[] Log = new byte[0];

		public List<BugAttachment> Attachments = new List<BugAttachment>();

		public static bool IsAllowedFile(string name)
		{
			string value = Path.GetExtension(name ?? "").ToLowerInvariant();
			return Array.IndexOf(AllowedExtensions, value) >= 0;
		}

		public byte[] ToBytes()
		{
			PacketWriter packetWriter = new PacketWriter(65536);
			packetWriter.WriteByte(1);
			packetWriter.WriteGuid(PlayerId);
			packetWriter.WriteFixedBytes(Token, 32);
			packetWriter.WriteString(Title, 120);
			packetWriter.WriteString(Description, 8000);
			packetWriter.WriteString(Contact, 120);
			packetWriter.WriteString(Info, 2000);
			WriteBlob(packetWriter, Log);
			packetWriter.WriteByte((byte)Attachments.Count);
			foreach (BugAttachment attachment in Attachments)
			{
				packetWriter.WriteString(attachment.Name, 100);
				WriteBlob(packetWriter, attachment.Data);
			}
			return packetWriter.ToArray();
		}

		public static BugReport FromBytes(byte[] data)
		{
			PacketReader packetReader = new PacketReader(data, 0, data.Length);
			if (packetReader.ReadByte() != 1)
			{
				throw new ProtocolException("Unknown report format");
			}
			BugReport bugReport = new BugReport
			{
				PlayerId = packetReader.ReadGuid(),
				Token = packetReader.ReadFixedBytes(32),
				Title = packetReader.ReadString(120),
				Description = packetReader.ReadString(8000),
				Contact = packetReader.ReadString(120),
				Info = packetReader.ReadString(2000),
				Log = ReadBlob(packetReader, 1048576)
			};
			int num = packetReader.ReadByte();
			if (num > 10)
			{
				throw new ProtocolException("Too many attachments");
			}
			long num2 = 0L;
			for (int i = 0; i < num; i++)
			{
				BugAttachment bugAttachment = new BugAttachment
				{
					Name = packetReader.ReadString(100),
					Data = ReadBlob(packetReader, 15728640)
				};
				num2 += bugAttachment.Data.Length;
				if (num2 > 20971520)
				{
					throw new ProtocolException("Attachments too large");
				}
				if (!IsAllowedFile(bugAttachment.Name))
				{
					throw new ProtocolException("Attachment type not allowed: " + Path.GetExtension(bugAttachment.Name));
				}
				bugReport.Attachments.Add(bugAttachment);
			}
			if (packetReader.Remaining != 0)
			{
				throw new ProtocolException("Trailing data");
			}
			return bugReport;
		}

		public long AttachmentBytes()
		{
			long num = 0L;
			foreach (BugAttachment attachment in Attachments)
			{
				num += attachment.Data.Length;
			}
			return num;
		}

		private static void WriteBlob(PacketWriter w, byte[] data)
		{
			data = data ?? new byte[0];
			w.WriteInt32(data.Length);
			if (data.Length != 0)
			{
				w.WriteFixedBytes(data, data.Length);
			}
		}

		private static byte[] ReadBlob(PacketReader r, int max)
		{
			int num = r.ReadInt32();
			if (num < 0 || num > max)
			{
				throw new ProtocolException("Field too large");
			}
			if (num != 0)
			{
				return r.ReadFixedBytes(num);
			}
			return new byte[0];
		}
	}
	public sealed class BugAttachment
	{
		public string Name = "";

		public byte[] Data = new byte[0];
	}
	public static class BuildTag
	{
		public static string Compose(string steamBuild, string modVersion)
		{
			List<string> list = new List<string>();
			if (!string.IsNullOrEmpty(steamBuild))
			{
				list.Add(steamBuild.StartsWith("steam-", StringComparison.Ordinal) ? steamBuild : ("steam-" + steamBuild));
			}
			string text = ShortVersion(modVersion);
			if (text.Length > 0)
			{
				list.Add("mod-" + text);
			}
			return string.Join("|", list);
		}

		public static bool Compatible(string a, string b)
		{
			Dictionary<string, string> dictionary = Parse(a);
			Dictionary<string, string> dictionary2 = Parse(b);
			foreach (KeyValuePair<string, string> item in dictionary)
			{
				if (dictionary2.TryGetValue(item.Key, out var value) && value != item.Value)
				{
					return false;
				}
			}
			return true;
		}

		public static string ShortVersion(string version)
		{
			if (string.IsNullOrEmpty(version))
			{
				return "";
			}
			string[] array = version.Trim().Split('.');
			if (array.Length <= 3)
			{
				return version.Trim();
			}
			return string.Join(".", array, 0, 3);
		}

		private static Dictionary<string, string> Parse(string tag)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			if (string.IsNullOrEmpty(tag))
			{
				return dictionary;
			}
			string[] array = tag.Split('|');
			foreach (string text in array)
			{
				int num = text.IndexOf('-');
				if (num > 0 && num != text.Length - 1)
				{
					dictionary[text.Substring(0, num)] = text.Substring(num + 1);
				}
			}
			return dictionary;
		}
	}
	public sealed class LobbyCode
	{
		private const string Prefix = "WKR";

		private const string Alphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";

		private const int PayloadLength = 5;

		public uint LobbyId;

		public static string Encode(uint lobbyId)
		{
			PacketWriter packetWriter = new PacketWriter(5);
			packetWriter.WriteUInt32(lobbyId);
			byte[] array = packetWriter.ToArray();
			byte[] array2 = new byte[array.Length + 1];
			Buffer.BlockCopy(array, 0, array2, 0, array.Length);
			array2[array.Length] = Check(array, array.Length);
			string text = ToBase32(array2);
			return "WKR-" + text.Substring(0, 4) + "-" + text.Substring(4);
		}

		public static bool TryDecode(string text, out LobbyCode code)
		{
			code = null;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = text.Trim().ToUpperInvariant().Replace("-", "")
				.Replace(" ", "");
			if (text2.StartsWith("WKR", StringComparison.Ordinal))
			{
				text2 = text2.Substring("WKR".Length);
			}
			text2 = text2.Replace('O', '0').Replace('I', '1').Replace('L', '1');
			if (text2.Length != 8 || !FromBase32(text2, out var data) || data.Length != 5)
			{
				return false;
			}
			if (data[4] != Check(data, 4))
			{
				return false;
			}
			uint num = new PacketReader(data, 0, 4).ReadUInt32();
			if (num == 0)
			{
				return false;
			}
			code = new LobbyCode
			{
				LobbyId = num
			};
			return true;
		}

		public static bool TryParseAddress(string text, out string host, out int port)
		{
			host = null;
			port = 47600;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = text.Trim();
			if (text2.StartsWith("[", StringComparison.Ordinal))
			{
				int num = text2.IndexOf(']');
				if (num < 0)
				{
					return false;
				}
				host = text2.Substring(1, num - 1);
				string text3 = text2.Substring(num + 1);
				if (text3.Length != 0)
				{
					if (text3[0] == ':')
					{
						return TryPort(text3.Substring(1), out port);
					}
					return false;
				}
				return true;
			}
			int num2 = text2.LastIndexOf(':');
			if (num2 >= 0 && text2.IndexOf(':') == num2)
			{
				host = text2.Substring(0, num2);
				if (host.Length > 0)
				{
					return TryPort(text2.Substring(num2 + 1), out port);
				}
				return false;
			}
			host = text2;
			return true;
		}

		private static bool TryPort(string text, out int port)
		{
			if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out port) && port > 0)
			{
				return port < 65536;
			}
			return false;
		}

		private static byte Check(byte[] data, int length)
		{
			uint num = 2166136261u;
			for (int i = 0; i < length; i++)
			{
				num ^= data[i];
				num *= 16777619;
			}
			return (byte)(num ^ (num >> 8) ^ (num >> 16) ^ (num >> 24));
		}

		private static string ToBase32(byte[] data)
		{
			StringBuilder stringBuilder = new StringBuilder((data.Length * 8 + 4) / 5);
			int num = 0;
			int num2 = 0;
			foreach (byte b in data)
			{
				num = (num << 8) | b;
				for (num2 += 8; num2 >= 5; num2 -= 5)
				{
					stringBuilder.Append("0123456789ABCDEFGHJKMNPQRSTVWXYZ"[(num >> num2 - 5) & 0x1F]);
				}
			}
			if (num2 > 0)
			{
				stringBuilder.Append("0123456789ABCDEFGHJKMNPQRSTVWXYZ"[(num << 5 - num2) & 0x1F]);
			}
			return stringBuilder.ToString();
		}

		private static bool FromBase32(string text, out byte[] data)
		{
			data = null;
			List<byte> list = new List<byte>(text.Length * 5 / 8);
			int num = 0;
			int num2 = 0;
			foreach (char value in text)
			{
				int num3 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".IndexOf(value);
				if (num3 < 0)
				{
					return false;
				}
				num = ((num << 5) | num3) & 0xFFFF;
				num2 += 5;
				if (num2 >= 8)
				{
					list.Add((byte)(num >> num2 - 8));
					num2 -= 8;
				}
			}
			data = list.ToArray();
			return true;
		}
	}
	public static class MessageCodec
	{
		public static byte[] Encode(Message message)
		{
			PacketWriter packetWriter = new PacketWriter(64);
			packetWriter.WriteByte((byte)message.Type);
			message.Write(packetWriter);
			if (packetWriter.Length > 2048)
			{
				throw new ProtocolException("Message too large");
			}
			return packetWriter.ToArray();
		}

		public static Message Decode(byte[] payload)
		{
			if (payload == null || payload.Length < 1)
			{
				throw new ProtocolException("Empty payload");
			}
			PacketReader packetReader = new PacketReader(payload, 1, payload.Length - 1);
			Message message = (MessageType)payload[0] switch
			{
				MessageType.ClientHello => ClientHello.Read(packetReader), 
				MessageType.ServerWelcome => ServerWelcome.Read(packetReader), 
				MessageType.Error => ErrorMessage.Read(packetReader), 
				MessageType.Ping => Ping.Read(packetReader), 
				MessageType.Pong => Pong.Read(packetReader), 
				MessageType.QueueJoin => QueueJoin.Read(packetReader), 
				MessageType.QueueLeave => QueueLeave.Read(packetReader), 
				MessageType.QueueStatus => QueueStatus.Read(packetReader), 
				MessageType.MatchFound => MatchFound.Read(packetReader), 
				MessageType.LobbyCreate => LobbyCreate.Read(packetReader), 
				MessageType.LobbyCreated => LobbyCreated.Read(packetReader), 
				MessageType.LobbyJoin => LobbyJoin.Read(packetReader), 
				MessageType.LobbyCancel => LobbyCancel.Read(packetReader), 
				MessageType.LobbyClosed => LobbyClosed.Read(packetReader), 
				MessageType.MatchReady => MatchReady.Read(packetReader), 
				MessageType.MatchStart => MatchStart.Read(packetReader), 
				MessageType.StartCountdown => StartCountdown.Read(packetReader), 
				MessageType.PlayerState => PlayerState.Read(packetReader), 
				MessageType.OpponentState => OpponentState.Read(packetReader), 
				MessageType.SplitReached => SplitReached.Read(packetReader), 
				MessageType.OpponentSplit => OpponentSplit.Read(packetReader), 
				MessageType.AttemptStarted => AttemptStarted.Read(packetReader), 
				MessageType.OpponentAttempt => OpponentAttempt.Read(packetReader), 
				MessageType.PlayerFinished => PlayerFinished.Read(packetReader), 
				MessageType.OpponentFinished => OpponentFinished.Read(packetReader), 
				MessageType.PlayerForfeit => PlayerForfeit.Read(packetReader), 
				MessageType.MatchResult => MatchResult.Read(packetReader), 
				MessageType.GameStatus => GameStatus.Read(packetReader), 
				MessageType.MatchPaused => MatchPaused.Read(packetReader), 
				MessageType.MatchResumed => MatchResumed.Read(packetReader), 
				MessageType.ModHello => ModHello.Read(packetReader), 
				MessageType.ClientStatus => ClientStatus.Read(packetReader), 
				MessageType.RacePaused => RacePaused.Read(packetReader), 
				MessageType.RaceResumed => RaceResumed.Read(packetReader), 
				MessageType.PracticeMark => PracticeMark.Read(packetReader), 
				MessageType.PracticeRestart => PracticeRestart.Read(packetReader), 
				MessageType.PlayerDied => PlayerDied.Read(packetReader), 
				MessageType.BanReady => BanReady.Read(packetReader), 
				MessageType.BanPick => BanPick.Read(packetReader), 
				MessageType.BanState => BanState.Read(packetReader), 
				MessageType.SteamLinkStart => SteamLinkStart.Read(packetReader), 
				MessageType.SteamLinkUrl => SteamLinkUrl.Read(packetReader), 
				MessageType.SteamLinkResult => SteamLinkResult.Read(packetReader), 
				MessageType.LobbyState => LobbyState.Read(packetReader), 
				MessageType.LobbyStart => LobbyStart.Read(packetReader), 
				MessageType.PlayerReady => PlayerReady.Read(packetReader), 
				MessageType.MatchRoster => MatchRoster.Read(packetReader), 
				MessageType.ModSettings => ModSettings.Read(packetReader), 
				MessageType.ChatSend => ChatSend.Read(packetReader), 
				MessageType.ChatMessage => ChatMessage.Read(packetReader), 
				MessageType.VoiceHello => VoiceHello.Read(packetReader), 
				MessageType.VoiceState => VoiceState.Read(packetReader), 
				MessageType.VoicePeers => VoicePeers.Read(packetReader), 
				MessageType.VoiceActivity => VoiceActivity.Read(packetReader), 
				_ => throw new ProtocolException("Unknown message type " + payload[0]), 
			};
			if (packetReader.Remaining != 0)
			{
				throw new ProtocolException("Trailing bytes in message " + message.Type);
			}
			return message;
		}
	}
	public static class FrameIO
	{
		public static byte[] ReadFrame(Stream stream)
		{
			byte[] array = new byte[2];
			if (!ReadExactly(stream, array, 2))
			{
				return null;
			}
			int num = array[0] | (array[1] << 8);
			if (num < 1 || num > 2048)
			{
				throw new ProtocolException("Invalid frame length " + num);
			}
			byte[] array2 = new byte[num];
			if (!ReadExactly(stream, array2, num))
			{
				throw new EndOfStreamException();
			}
			return array2;
		}

		public static async Task<byte[]> ReadFrameAsync(Stream stream, CancellationToken cancellationToken)
		{
			byte[] header = new byte[2];
			if (!(await ReadExactlyAsync(stream, header, 2, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
			{
				return null;
			}
			int num = header[0] | (header[1] << 8);
			if (num < 1 || num > 2048)
			{
				throw new ProtocolException("Invalid frame length " + num);
			}
			byte[] payload = new byte[num];
			if (!(await ReadExactlyAsync(stream, payload, num, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
			{
				throw new EndOfStreamException();
			}
			return payload;
		}

		public static byte[] Pack(byte[] payload)
		{
			if (payload.Length < 1 || payload.Length > 2048)
			{
				throw new ProtocolException("Invalid payload length");
			}
			byte[] array = new byte[payload.Length + 2];
			array[0] = (byte)payload.Length;
			array[1] = (byte)(payload.Length >> 8);
			Buffer.BlockCopy(payload, 0, array, 2, payload.Length);
			return array;
		}

		public static void WriteFrame(Stream stream, byte[] payload)
		{
			byte[] array = Pack(payload);
			stream.Write(array, 0, array.Length);
			stream.Flush();
		}

		public static async Task WriteFrameAsync(Stream stream, byte[] payload, CancellationToken cancellationToken)
		{
			byte[] array = Pack(payload);
			await stream.WriteAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			await stream.FlushAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
		}

		private static bool ReadExactly(Stream stream, byte[] buffer, int count)
		{
			int num;
			for (int i = 0; i < count; i += num)
			{
				num = stream.Read(buffer, i, count - i);
				if (num <= 0)
				{
					if (i == 0)
					{
						return false;
					}
					throw new EndOfStreamException();
				}
			}
			return true;
		}

		private static async Task<bool> ReadExactlyAsync(Stream stream, byte[] buffer, int count, CancellationToken cancellationToken)
		{
			int num;
			for (int read = 0; read < count; read += num)
			{
				num = await stream.ReadAsync(buffer, read, count - read, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				if (num <= 0)
				{
					if (read == 0)
					{
						return false;
					}
					throw new EndOfStreamException();
				}
			}
			return true;
		}
	}
	public enum MessageType : byte
	{
		ClientHello = 1,
		ServerWelcome = 2,
		Error = 3,
		Ping = 4,
		Pong = 5,
		QueueJoin = 10,
		QueueLeave = 11,
		QueueStatus = 12,
		MatchFound = 13,
		LobbyCreate = 14,
		LobbyCreated = 15,
		LobbyJoin = 16,
		LobbyCancel = 17,
		LobbyClosed = 18,
		LobbyState = 19,
		MatchReady = 20,
		MatchStart = 21,
		StartCountdown = 22,
		LobbyStart = 23,
		PlayerReady = 24,
		MatchRoster = 25,
		PlayerState = 30,
		OpponentState = 31,
		SplitReached = 32,
		OpponentSplit = 33,
		AttemptStarted = 34,
		OpponentAttempt = 35,
		PlayerFinished = 36,
		OpponentFinished = 37,
		PlayerForfeit = 38,
		MatchResult = 39,
		GameStatus = 40,
		MatchPaused = 41,
		MatchResumed = 42,
		SteamLinkStart = 43,
		SteamLinkUrl = 44,
		SteamLinkResult = 45,
		PlayerDied = 46,
		BanReady = 50,
		BanPick = 51,
		BanState = 52,
		ModHello = 60,
		ClientStatus = 61,
		RacePaused = 62,
		RaceResumed = 63,
		PracticeMark = 64,
		PracticeRestart = 65,
		ModSettings = 66,
		ChatSend = 70,
		ChatMessage = 71,
		VoiceHello = 80,
		VoiceState = 81,
		VoicePeers = 82,
		VoiceActivity = 83
	}
	public enum ErrorCode : byte
	{
		Unknown,
		VersionMismatch,
		BadToken,
		InvalidName,
		RateLimited,
		ServerFull,
		ProtocolViolation,
		NotInMatch,
		AlreadyQueued,
		AlreadyInMatch,
		ServerShuttingDown,
		LobbyNotFound,
		LobbyUnavailable,
		LobbyBuildMismatch,
		RegistrationLimited,
		LobbySettingsMismatch,
		SteamLinkRequired,
		LobbyFull,
		LobbyBusy,
		NotHost
	}
	public enum MatchMode : byte
	{
		Duel,
		Party
	}
	[Flags]
	public enum MatchRules : byte
	{
		None = 0,
		ArtifactBans = 1,
		DeepStorageFood = 2,
		GuaranteedPerks = 4,
		GuaranteedItems = 8,
		CuratedSeeds = 0xE,
		Ranked = 0xF,
		All = 0xF
	}
	public enum RacerStatus : byte
	{
		Loading,
		Loaded,
		Ready,
		Racing,
		Finished,
		Out,
		Left
	}
	public enum MatchOutcome : byte
	{
		Win = 1,
		Loss = 2,
		Aborted = 4
	}
	public enum MatchEnd : byte
	{
		Other,
		Finish,
		Death
	}
	public enum ClientPhase : byte
	{
		Offline,
		Connecting,
		Idle,
		Searching,
		InMatch
	}
	[Flags]
	public enum StateFlags : byte
	{
		None = 0,
		Dead = 1,
		LeftHold = 2,
		RightHold = 4,
		Loading = 8
	}
	public abstract class Message
	{
		public abstract MessageType Type { get; }

		public abstract void Write(PacketWriter w);
	}
	public sealed class ClientHello : Message
	{
		public int ProtocolVersion = 7;

		public Guid PlayerId;

		public byte[] Token = new byte[32];

		public string Name = "";

		public string ClientVersion = "";

		public bool GameConnected = true;

		public override MessageType Type => MessageType.ClientHello;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(ProtocolVersion);
			w.WriteGuid(PlayerId);
			w.WriteFixedBytes(Token, 32);
			w.WriteString(Name, 24);
			w.WriteString(ClientVersion, 32);
			w.WriteBool(GameConnected);
		}

		public static ClientHello Read(PacketReader r)
		{
			return new ClientHello
			{
				ProtocolVersion = r.ReadInt32(),
				PlayerId = r.ReadGuid(),
				Token = r.ReadFixedBytes(32),
				Name = r.ReadString(24),
				ClientVersion = r.ReadString(32),
				GameConnected = r.ReadBool()
			};
		}
	}
	public sealed class ServerWelcome : Message
	{
		public Guid PlayerId;

		public string Name = "";

		public int Rating;

		public int Wins;

		public int Losses;

		public long ServerTimeMs;

		public int PlayersOnline;

		public int PlayersQueued;

		public bool SteamAvailable;

		public bool SteamLinked;

		public bool SteamRequiredForRanked;

		public override MessageType Type => MessageType.ServerWelcome;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(PlayerId);
			w.WriteString(Name, 24);
			w.WriteInt32(Rating);
			w.WriteInt32(Wins);
			w.WriteInt32(Losses);
			w.WriteInt64(ServerTimeMs);
			w.WriteInt32(PlayersOnline);
			w.WriteInt32(PlayersQueued);
			w.WriteBool(SteamAvailable);
			w.WriteBool(SteamLinked);
			w.WriteBool(SteamRequiredForRanked);
		}

		public static ServerWelcome Read(PacketReader r)
		{
			return new ServerWelcome
			{
				PlayerId = r.ReadGuid(),
				Name = r.ReadString(24),
				Rating = r.ReadInt32(),
				Wins = r.ReadInt32(),
				Losses = r.ReadInt32(),
				ServerTimeMs = r.ReadInt64(),
				PlayersOnline = r.ReadInt32(),
				PlayersQueued = r.ReadInt32(),
				SteamAvailable = r.ReadBool(),
				SteamLinked = r.ReadBool(),
				SteamRequiredForRanked = r.ReadBool()
			};
		}
	}
	public sealed class ErrorMessage : Message
	{
		public ErrorCode Code;

		public string Text = "";

		public override MessageType Type => MessageType.Error;

		public ErrorMessage()
		{
		}

		public ErrorMessage(ErrorCode code, string text)
		{
			Code = code;
			Text = text ?? "";
		}

		public override void Write(PacketWriter w)
		{
			w.WriteByte((byte)Code);
			w.WriteString(Text, 200);
		}

		public static ErrorMessage Read(PacketReader r)
		{
			return new ErrorMessage
			{
				Code = (ErrorCode)r.ReadByte(),
				Text = r.ReadString(200)
			};
		}
	}
	public sealed class Ping : Message
	{
		public long ClientTimeMs;

		public override MessageType Type => MessageType.Ping;

		public override void Write(PacketWriter w)
		{
			w.WriteInt64(ClientTimeMs);
		}

		public static Ping Read(PacketReader r)
		{
			return new Ping
			{
				ClientTimeMs = r.ReadInt64()
			};
		}
	}
	public sealed class Pong : Message
	{
		public long ClientTimeMs;

		public long ServerTimeMs;

		public override MessageType Type => MessageType.Pong;

		public override void Write(PacketWriter w)
		{
			w.WriteInt64(ClientTimeMs);
			w.WriteInt64(ServerTimeMs);
		}

		public static Pong Read(PacketReader r)
		{
			return new Pong
			{
				ClientTimeMs = r.ReadInt64(),
				ServerTimeMs = r.ReadInt64()
			};
		}
	}
	public sealed class QueueJoin : Message
	{
		public string GameBuild = "";

		public override MessageType Type => MessageType.QueueJoin;

		public override void Write(PacketWriter w)
		{
			w.WriteString(GameBuild, 32);
		}

		public static QueueJoin Read(PacketReader r)
		{
			return new QueueJoin
			{
				GameBuild = r.ReadString(32)
			};
		}
	}
	public sealed class QueueLeave : Message
	{
		public override MessageType Type => MessageType.QueueLeave;

		public override void Write(PacketWriter w)
		{
		}

		public static QueueLeave Read(PacketReader r)
		{
			return new QueueLeave();
		}
	}
	public sealed class QueueStatus : Message
	{
		public bool Queued;

		public int PlayersQueued;

		public int WaitSeconds;

		public override MessageType Type => MessageType.QueueStatus;

		public override void Write(PacketWriter w)
		{
			w.WriteBool(Queued);
			w.WriteInt32(PlayersQueued);
			w.WriteInt32(WaitSeconds);
		}

		public static QueueStatus Read(PacketReader r)
		{
			return new QueueStatus
			{
				Queued = r.ReadBool(),
				PlayersQueued = r.ReadInt32(),
				WaitSeconds = r.ReadInt32()
			};
		}
	}
	public sealed class MatchFound : Message
	{
		public Guid MatchId;

		public int Seed;

		public string OpponentName = "";

		public int OpponentRating;

		public bool Competitive = true;

		public int TimeLimitSeconds;

		public int ResumeElapsedMs = -1;

		public int Attempt = 1;

		public int GraceSeconds = 60;

		public bool Ranked = true;

		public MatchRules Rules;

		public MatchMode Mode;

		public byte YourRacer;

		public int PlayerCount = 2;

		public int ReadySeconds;

		public bool ArtifactBans
		{
			get
			{
				return (Rules & MatchRules.ArtifactBans) != 0;
			}
			set
			{
				Rules = (value ? (Rules | MatchRules.ArtifactBans) : (Rules & ~MatchRules.ArtifactBans));
			}
		}

		public bool Party => Mode == MatchMode.Party;

		public override MessageType Type => MessageType.MatchFound;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteInt32(Seed);
			w.WriteString(OpponentName, 24);
			w.WriteInt32(OpponentRating);
			w.WriteBool(Competitive);
			w.WriteInt32(TimeLimitSeconds);
			w.WriteInt32(ResumeElapsedMs);
			w.WriteInt32(Attempt);
			w.WriteInt32(GraceSeconds);
			w.WriteBool(Ranked);
			w.WriteByte((byte)Rules);
			w.WriteByte((byte)Mode);
			w.WriteByte(YourRacer);
			w.WriteInt32(PlayerCount);
			w.WriteInt32(ReadySeconds);
		}

		public static MatchFound Read(PacketReader r)
		{
			return new MatchFound
			{
				MatchId = r.ReadGuid(),
				Seed = r.ReadInt32(),
				OpponentName = r.ReadString(24),
				OpponentRating = r.ReadInt32(),
				Competitive = r.ReadBool(),
				TimeLimitSeconds = r.ReadInt32(),
				ResumeElapsedMs = r.ReadInt32(),
				Attempt = r.ReadInt32(),
				GraceSeconds = r.ReadInt32(),
				Ranked = r.ReadBool(),
				Rules = (MatchRules)(r.ReadByte() & 0xF),
				Mode = ProtocolChecks.Mode(r.ReadByte()),
				YourRacer = r.ReadByte(),
				PlayerCount = r.ReadInt32(),
				ReadySeconds = r.ReadInt32()
			};
		}
	}
	public sealed class LobbyCreate : Message
	{
		public string GameBuild = "";

		public MatchMode Mode;

		public MatchRules Rules = MatchRules.Ranked;

		public override MessageType Type => MessageType.LobbyCreate;

		public override void Write(PacketWriter w)
		{
			w.WriteString(GameBuild, 32);
			w.WriteByte((byte)Mode);
			w.WriteByte((byte)Rules);
		}

		public static LobbyCreate Read(PacketReader r)
		{
			return new LobbyCreate
			{
				GameBuild = r.ReadString(32),
				Mode = ProtocolChecks.Mode(r.ReadByte()),
				Rules = (MatchRules)(r.ReadByte() & 0xF)
			};
		}
	}
	public sealed class LobbyCreated : Message
	{
		public uint LobbyId;

		public string PublicAddress = "";

		public int ExpiresInSeconds;

		public override MessageType Type => MessageType.LobbyCreated;

		public override void Write(PacketWriter w)
		{
			w.WriteUInt32(LobbyId);
			w.WriteString(PublicAddress, 100);
			w.WriteInt32(ExpiresInSeconds);
		}

		public static LobbyCreated Read(PacketReader r)
		{
			return new LobbyCreated
			{
				LobbyId = r.ReadUInt32(),
				PublicAddress = r.ReadString(100),
				ExpiresInSeconds = r.ReadInt32()
			};
		}
	}
	public sealed class LobbyJoin : Message
	{
		public uint LobbyId;

		public string GameBuild = "";

		public override MessageType Type => MessageType.LobbyJoin;

		public override void Write(PacketWriter w)
		{
			w.WriteUInt32(LobbyId);
			w.WriteString(GameBuild, 32);
		}

		public static LobbyJoin Read(PacketReader r)
		{
			return new LobbyJoin
			{
				LobbyId = r.ReadUInt32(),
				GameBuild = r.ReadString(32)
			};
		}
	}
	public sealed class LobbyCancel : Message
	{
		public override MessageType Type => MessageType.LobbyCancel;

		public override void Write(PacketWriter w)
		{
		}

		public static LobbyCancel Read(PacketReader r)
		{
			return new LobbyCancel();
		}
	}
	public sealed class LobbyClosed : Message
	{
		public uint LobbyId;

		public string Reason = "";

		public override MessageType Type => MessageType.LobbyClosed;

		public override void Write(PacketWriter w)
		{
			w.WriteUInt32(LobbyId);
			w.WriteString(Reason, 200);
		}

		public static LobbyClosed Read(PacketReader r)
		{
			return new LobbyClosed
			{
				LobbyId = r.ReadUInt32(),
				Reason = r.ReadString(200)
			};
		}
	}
	public sealed class MatchReady : Message
	{
		public Guid MatchId;

		public uint WorldHash;

		public int LevelCount;

		public override MessageType Type => MessageType.MatchReady;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteUInt32(WorldHash);
			w.WriteInt32(LevelCount);
		}

		public static MatchReady Read(PacketReader r)
		{
			return new MatchReady
			{
				MatchId = r.ReadGuid(),
				WorldHash = r.ReadUInt32(),
				LevelCount = r.ReadInt32()
			};
		}
	}
	public sealed class MatchStart : Message
	{
		public Guid MatchId;

		public long GoServerTimeMs;

		public override MessageType Type => MessageType.MatchStart;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteInt64(GoServerTimeMs);
		}

		public static MatchStart Read(PacketReader r)
		{
			return new MatchStart
			{
				MatchId = r.ReadGuid(),
				GoServerTimeMs = r.ReadInt64()
			};
		}
	}
	public sealed class StartCountdown : Message
	{
		public Guid MatchId;

		public int MsUntilGo;

		public override MessageType Type => MessageType.StartCountdown;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteInt32(MsUntilGo);
		}

		public static StartCountdown Read(PacketReader r)
		{
			return new StartCountdown
			{
				MatchId = r.ReadGuid(),
				MsUntilGo = r.ReadInt32()
			};
		}
	}
	public abstract class StateMessage : Message
	{
		public const float MaxCoordinate = 1000000f;

		public int Attempt;

		public int RaceTimeMs;

		public float X;

		public float Y;

		public float Z;

		public float Yaw;

		public float Pitch;

		public StateFlags Flags;

		public float LeftX;

		public float LeftY;

		public float LeftZ;

		public float RightX;

		public float RightY;

		public float RightZ;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(Attempt);
			w.WriteInt32(RaceTimeMs);
			w.WriteFloat(X);
			w.WriteFloat(Y);
			w.WriteFloat(Z);
			w.WriteFloat(Yaw);
			w.WriteFloat(Pitch);
			w.WriteByte((byte)Flags);
			if ((Flags & StateFlags.LeftHold) != StateFlags.None)
			{
				w.WriteFloat(LeftX);
				w.WriteFloat(LeftY);
				w.WriteFloat(LeftZ);
			}
			if ((Flags & StateFlags.RightHold) != StateFlags.None)
			{
				w.WriteFloat(RightX);
				w.WriteFloat(RightY);
				w.WriteFloat(RightZ);
			}
		}

		protected void ReadBody(PacketReader r)
		{
			Attempt = r.ReadInt32();
			RaceTimeMs = r.ReadInt32();
			X = ReadCoordinate(r);
			Y = ReadCoordinate(r);
			Z = ReadCoordinate(r);
			Yaw = r.ReadFiniteFloat();
			Pitch = r.ReadFiniteFloat();
			Flags = (StateFlags)r.ReadByte();
			if ((Flags & StateFlags.LeftHold) != StateFlags.None)
			{
				LeftX = ReadCoordinate(r);
				LeftY = ReadCoordinate(r);
				LeftZ = ReadCoordinate(r);
			}
			if ((Flags & StateFlags.RightHold) != StateFlags.None)
			{
				RightX = ReadCoordinate(r);
				RightY = ReadCoordinate(r);
				RightZ = ReadCoordinate(r);
			}
		}

		private static float ReadCoordinate(PacketReader r)
		{
			float num = r.ReadFiniteFloat();
			if (num > 1000000f || num < -1000000f)
			{
				throw new ProtocolException("Coordinate out of range");
			}
			return num;
		}

		public void CopyFrom(StateMessage other)
		{
			Attempt = other.Attempt;
			RaceTimeMs = other.RaceTimeMs;
			X = other.X;
			Y = other.Y;
			Z = other.Z;
			Yaw = other.Yaw;
			Pitch = other.Pitch;
			Flags = other.Flags;
			LeftX = other.LeftX;
			LeftY = other.LeftY;
			LeftZ = other.LeftZ;
			RightX = other.RightX;
			RightY = other.RightY;
			RightZ = other.RightZ;
		}
	}
	public sealed class PlayerState : StateMessage
	{
		public override MessageType Type => MessageType.PlayerState;

		public static PlayerState Read(PacketReader r)
		{
			PlayerState playerState = new PlayerState();
			playerState.ReadBody(r);
			return playerState;
		}
	}
	public sealed class OpponentState : StateMessage
	{
		public byte Racer;

		public override MessageType Type => MessageType.OpponentState;

		public override void Write(PacketWriter w)
		{
			base.Write(w);
			w.WriteByte(Racer);
		}

		public static OpponentState Read(PacketReader r)
		{
			OpponentState opponentState = new OpponentState();
			opponentState.ReadBody(r);
			opponentState.Racer = (byte)((r.Remaining > 0) ? r.ReadByte() : 0);
			return opponentState;
		}
	}
	public abstract class SplitMessage : Message
	{
		public int Attempt;

		public int Index;

		public string Level = "";

		public string Region = "";

		public int RaceTimeMs;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(Attempt);
			w.WriteInt32(Index);
			w.WriteString(Level, 64);
			w.WriteString(Region, 64);
			w.WriteInt32(RaceTimeMs);
		}

		protected void ReadBody(PacketReader r)
		{
			Attempt = r.ReadInt32();
			Index = r.ReadInt32();
			Level = r.ReadString(64);
			Region = r.ReadString(64);
			RaceTimeMs = r.ReadInt32();
		}
	}
	public sealed class SplitReached : SplitMessage
	{
		public override MessageType Type => MessageType.SplitReached;

		public static SplitReached Read(PacketReader r)
		{
			SplitReached splitReached = new SplitReached();
			splitReached.ReadBody(r);
			return splitReached;
		}
	}
	public sealed class OpponentSplit : SplitMessage
	{
		public byte Racer;

		public override MessageType Type => MessageType.OpponentSplit;

		public override void Write(PacketWriter w)
		{
			base.Write(w);
			w.WriteByte(Racer);
		}

		public static OpponentSplit Read(PacketReader r)
		{
			OpponentSplit opponentSplit = new OpponentSplit();
			opponentSplit.ReadBody(r);
			opponentSplit.Racer = (byte)((r.Remaining > 0) ? r.ReadByte() : 0);
			return opponentSplit;
		}
	}
	public abstract class AttemptMessage : Message
	{
		public int Attempt;

		public int RaceTimeMs;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(Attempt);
			w.WriteInt32(RaceTimeMs);
		}

		protected void ReadBody(PacketReader r)
		{
			Attempt = r.ReadInt32();
			RaceTimeMs = r.ReadInt32();
		}
	}
	public sealed class AttemptStarted : AttemptMessage
	{
		public override MessageType Type => MessageType.AttemptStarted;

		public static AttemptStarted Read(PacketReader r)
		{
			AttemptStarted attemptStarted = new AttemptStarted();
			attemptStarted.ReadBody(r);
			return attemptStarted;
		}
	}
	public sealed class OpponentAttempt : AttemptMessage
	{
		public byte Racer;

		public override MessageType Type => MessageType.OpponentAttempt;

		public override void Write(PacketWriter w)
		{
			base.Write(w);
			w.WriteByte(Racer);
		}

		public static OpponentAttempt Read(PacketReader r)
		{
			OpponentAttempt opponentAttempt = new OpponentAttempt();
			opponentAttempt.ReadBody(r);
			opponentAttempt.Racer = (byte)((r.Remaining > 0) ? r.ReadByte() : 0);
			return opponentAttempt;
		}
	}
	public abstract class FinishMessage : Message
	{
		public int Attempt;

		public int RaceTimeMs;

		public bool Cheated;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(Attempt);
			w.WriteInt32(RaceTimeMs);
			w.WriteBool(Cheated);
		}

		protected void ReadBody(PacketReader r)
		{
			Attempt = r.ReadInt32();
			RaceTimeMs = r.ReadInt32();
			Cheated = r.ReadBool();
		}
	}
	public sealed class PlayerFinished : FinishMessage
	{
		public override MessageType Type => MessageType.PlayerFinished;

		public static PlayerFinished Read(PacketReader r)
		{
			PlayerFinished playerFinished = new PlayerFinished();
			playerFinished.ReadBody(r);
			return playerFinished;
		}
	}
	public sealed class OpponentFinished : FinishMessage
	{
		public byte Racer;

		public override MessageType Type => MessageType.OpponentFinished;

		public override void Write(PacketWriter w)
		{
			base.Write(w);
			w.WriteByte(Racer);
		}

		public static OpponentFinished Read(PacketReader r)
		{
			OpponentFinished opponentFinished = new OpponentFinished();
			opponentFinished.ReadBody(r);
			opponentFinished.Racer = (byte)((r.Remaining > 0) ? r.ReadByte() : 0);
			return opponentFinished;
		}
	}
	public sealed class PlayerDied : Message
	{
		public int Attempt;

		public int RaceTimeMs;

		public string Cause = "";

		public string Text = "";

		public override MessageType Type => MessageType.PlayerDied;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(Attempt);
			w.WriteInt32(RaceTimeMs);
			w.WriteString(Cause, 32);
			w.WriteString(Text, 64);
		}

		public static PlayerDied Read(PacketReader r)
		{
			return new PlayerDied
			{
				Attempt = r.ReadInt32(),
				RaceTimeMs = r.ReadInt32(),
				Cause = r.ReadString(32),
				Text = r.ReadString(64)
			};
		}
	}
	public sealed class PlayerForfeit : Message
	{
		public override MessageType Type => MessageType.PlayerForfeit;

		public override void Write(PacketWriter w)
		{
		}

		public static PlayerForfeit Read(PacketReader r)
		{
			return new PlayerForfeit();
		}
	}
	public sealed class MatchResult : Message
	{
		public Guid MatchId;

		public MatchOutcome Outcome;

		public int YourTimeMs = -1;

		public int OpponentTimeMs = -1;

		public int OldRating;

		public int NewRating;

		public string Reason = "";

		public MatchEnd End;

		public int Place;

		public List<Standing> Standings = new List<Standing>();

		public override MessageType Type => MessageType.MatchResult;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteByte((byte)Outcome);
			w.WriteInt32(YourTimeMs);
			w.WriteInt32(OpponentTimeMs);
			w.WriteInt32(OldRating);
			w.WriteInt32(NewRating);
			w.WriteString(Reason, 200);
			w.WriteByte((byte)End);
			w.WriteByte((byte)Place);
			Standing.WriteList(w, Standings);
		}

		public static MatchResult Read(PacketReader r)
		{
			return new MatchResult
			{
				MatchId = r.ReadGuid(),
				Outcome = (MatchOutcome)r.ReadByte(),
				YourTimeMs = r.ReadInt32(),
				OpponentTimeMs = r.ReadInt32(),
				OldRating = r.ReadInt32(),
				NewRating = r.ReadInt32(),
				Reason = r.ReadString(200),
				End = (MatchEnd)r.ReadByte(),
				Place = r.ReadByte(),
				Standings = Standing.ReadList(r)
			};
		}
	}
	public sealed class GameStatus : Message
	{
		public bool Connected;

		public override MessageType Type => MessageType.GameStatus;

		public override void Write(PacketWriter w)
		{
			w.WriteBool(Connected);
		}

		public static GameStatus Read(PacketReader r)
		{
			return new GameStatus
			{
				Connected = r.ReadBool()
			};
		}
	}
	public sealed class MatchPaused : Message
	{
		public Guid MatchId;

		public string AwayName = "";

		public int SecondsLeft;

		public int RaceTimeMs;

		public override MessageType Type => MessageType.MatchPaused;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteString(AwayName, 56);
			w.WriteInt32(SecondsLeft);
			w.WriteInt32(RaceTimeMs);
		}

		public static MatchPaused Read(PacketReader r)
		{
			return new MatchPaused
			{
				MatchId = r.ReadGuid(),
				AwayName = r.ReadString(56),
				SecondsLeft = r.ReadInt32(),
				RaceTimeMs = r.ReadInt32()
			};
		}
	}
	public sealed class MatchResumed : Message
	{
		public Guid MatchId;

		public long GoServerTimeMs;

		public long ResumeServerTimeMs;

		public override MessageType Type => MessageType.MatchResumed;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteInt64(GoServerTimeMs);
			w.WriteInt64(ResumeServerTimeMs);
		}

		public static MatchResumed Read(PacketReader r)
		{
			return new MatchResumed
			{
				MatchId = r.ReadGuid(),
				GoServerTimeMs = r.ReadInt64(),
				ResumeServerTimeMs = r.ReadInt64()
			};
		}
	}
	public sealed class ModHello : Message
	{
		public int ProtocolVersion = 7;

		public string ModVersion = "";

		public string GameVersion = "";

		public override MessageType Type => MessageType.ModHello;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(ProtocolVersion);
			w.WriteString(ModVersion, 32);
			w.WriteString(GameVersion, 64);
		}

		public static ModHello Read(PacketReader r)
		{
			return new ModHello
			{
				ProtocolVersion = r.ReadInt32(),
				ModVersion = r.ReadString(32),
				GameVersion = r.ReadString(64)
			};
		}
	}
	public sealed class ClientStatus : Message
	{
		public const string MismatchPrefix = "VERSION MISMATCH";

		public ClientPhase Phase;

		public string PlayerName = "";

		public int Rating;

		public string Info = "";

		public override MessageType Type => MessageType.ClientStatus;

		public override void Write(PacketWriter w)
		{
			w.WriteByte((byte)Phase);
			w.WriteString(PlayerName, 24);
			w.WriteInt32(Rating);
			w.WriteString(Info, 200);
		}

		public static ClientStatus Read(PacketReader r)
		{
			return new ClientStatus
			{
				Phase = (ClientPhase)r.ReadByte(),
				PlayerName = r.ReadString(24),
				Rating = r.ReadInt32(),
				Info = r.ReadString(200)
			};
		}
	}
	public sealed class RacePaused : Message
	{
		public Guid MatchId;

		public string Reason = "";

		public int SecondsLeft;

		public int RaceTimeMs = -1;

		public override MessageType Type => MessageType.RacePaused;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteString(Reason, 200);
			w.WriteInt32(SecondsLeft);
			w.WriteInt32(RaceTimeMs);
		}

		public static RacePaused Read(PacketReader r)
		{
			return new RacePaused
			{
				MatchId = r.ReadGuid(),
				Reason = r.ReadString(200),
				SecondsLeft = r.ReadInt32(),
				RaceTimeMs = r.ReadInt32()
			};
		}
	}
	public sealed class PracticeMark : Message
	{
		public Guid MatchId;

		public int BestTimeMs = -1;

		public bool BestFinished;

		public int BestLevels;

		public override MessageType Type => MessageType.PracticeMark;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteInt32(BestTimeMs);
			w.WriteBool(BestFinished);
			w.WriteInt32(BestLevels);
		}

		public static PracticeMark Read(PacketReader r)
		{
			return new PracticeMark
			{
				MatchId = r.ReadGuid(),
				BestTimeMs = r.ReadInt32(),
				BestFinished = r.ReadBool(),
				BestLevels = r.ReadInt32()
			};
		}
	}
	public sealed class PracticeRestart : Message
	{
		public Guid MatchId;

		public override MessageType Type => MessageType.PracticeRestart;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
		}

		public static PracticeRestart Read(PacketReader r)
		{
			return new PracticeRestart
			{
				MatchId = r.ReadGuid()
			};
		}
	}
	public sealed class RaceResumed : Message
	{
		public Guid MatchId;

		public int MsUntilGo;

		public int MsUntilResume;

		public override MessageType Type => MessageType.RaceResumed;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteInt32(MsUntilGo);
			w.WriteInt32(MsUntilResume);
		}

		public static RaceResumed Read(PacketReader r)
		{
			return new RaceResumed
			{
				MatchId = r.ReadGuid(),
				MsUntilGo = r.ReadInt32(),
				MsUntilResume = r.ReadInt32()
			};
		}
	}
	public sealed class ProtocolException : Exception
	{
		public ProtocolException(string message)
			: base(message)
		{
		}
	}
	[StructLayout(LayoutKind.Explicit)]
	internal struct FloatBits
	{
		[FieldOffset(0)]
		public float Float;

		[FieldOffset(0)]
		public uint Bits;
	}
	public sealed class PacketWriter
	{
		private byte[] _data;

		private int _length;

		public int Length => _length;

		public byte[] Data => _data;

		public PacketWriter(int capacity = 128)
		{
			_data = new byte[capacity];
		}

		public void Reset()
		{
			_length = 0;
		}

		public byte[] ToArray()
		{
			byte[] array = new byte[_length];
			Buffer.BlockCopy(_data, 0, array, 0, _length);
			return array;
		}

		private void Ensure(int extra)
		{
			int num = _length + extra;
			if (num > _data.Length)
			{
				int num2;
				for (num2 = _data.Length * 2; num2 < num; num2 *= 2)
				{
				}
				Array.Resize(ref _data, num2);
			}
		}

		public void WriteByte(byte value)
		{
			Ensure(1);
			_data[_length++] = value;
		}

		public void WriteBool(bool value)
		{
			WriteByte(value ? ((byte)1) : ((byte)0));
		}

		public void WriteUInt16(ushort value)
		{
			Ensure(2);
			_data[_length++] = (byte)value;
			_data[_length++] = (byte)(value >> 8);
		}

		public void WriteUInt32(uint value)
		{
			Ensure(4);
			for (int i = 0; i < 4; i++)
			{
				_data[_length++] = (byte)(value >> 8 * i);
			}
		}

		public void WriteInt32(int value)
		{
			WriteUInt32((uint)value);
		}

		public void WriteInt64(long value)
		{
			Ensure(8);
			for (int i = 0; i < 8; i++)
			{
				_data[_length++] = (byte)((ulong)value >> 8 * i);
			}
		}

		public void WriteFloat(float value)
		{
			FloatBits floatBits = new FloatBits
			{
				Float = value
			};
			WriteUInt32(floatBits.Bits);
		}

		public void WriteGuid(Guid value)
		{
			byte[] src = value.ToByteArray();
			Ensure(16);
			Buffer.BlockCopy(src, 0, _data, _length, 16);
			_length += 16;
		}

		public void WriteFixedBytes(byte[] value, int count)
		{
			if (value == null || value.Length != count)
			{
				throw new ProtocolException("Fixed byte field has wrong length");
			}
			Ensure(count);
			Buffer.BlockCopy(value, 0, _data, _length, count);
			_length += count;
		}

		public void WriteString(string value, int maxChars)
		{
			if (value == null)
			{
				value = string.Empty;
			}
			if (value.Length > maxChars)
			{
				value = value.Substring(0, maxChars);
			}
			byte[] bytes = Encoding.UTF8.GetBytes(value);
			WriteUInt16((ushort)bytes.Length);
			Ensure(bytes.Length);
			Buffer.BlockCopy(bytes, 0, _data, _length, bytes.Length);
			_length += bytes.Length;
		}
	}
	public sealed class PacketReader
	{
		private readonly byte[] _data;

		private readonly int _end;

		private int _position;

		public int Remaining => _end - _position;

		public PacketReader(byte[] data, int offset, int count)
		{
			if (data == null || offset < 0 || count < 0 || offset + count > data.Length)
			{
				throw new ProtocolException("Invalid packet bounds");
			}
			_data = data;
			_position = offset;
			_end = offset + count;
		}

		private void Need(int count)
		{
			if (count < 0 || _end - _position < count)
			{
				throw new ProtocolException("Packet truncated");
			}
		}

		public byte ReadByte()
		{
			Need(1);
			return _data[_position++];
		}

		public bool ReadBool()
		{
			return ReadByte() != 0;
		}

		public ushort ReadUInt16()
		{
			Need(2);
			ushort result = (ushort)(_data[_position] | (_data[_position + 1] << 8));
			_position += 2;
			return result;
		}

		public uint ReadUInt32()
		{
			Need(4);
			uint num = 0u;
			for (int i = 0; i < 4; i++)
			{
				num |= (uint)(_data[_position + i] << 8 * i);
			}
			_position += 4;
			return num;
		}

		public int ReadInt32()
		{
			return (int)ReadUInt32();
		}

		public long ReadInt64()
		{
			Need(8);
			ulong num = 0uL;
			for (int i = 0; i < 8; i++)
			{
				num |= (ulong)_data[_position + i] << 8 * i;
			}
			_position += 8;
			return (long)num;
		}

		public float ReadFloat()
		{
			FloatBits floatBits = new FloatBits
			{
				Bits = ReadUInt32()
			};
			return floatBits.Float;
		}

		public float ReadFiniteFloat()
		{
			float num = ReadFloat();
			if (float.IsNaN(num) || float.IsInfinity(num))
			{
				throw new ProtocolException("Non-finite float");
			}
			return num;
		}

		public Guid ReadGuid()
		{
			Need(16);
			byte[] array = new byte[16];
			Buffer.BlockCopy(_data, _position, array, 0, 16);
			_position += 16;
			return new Guid(array);
		}

		public byte[] ReadFixedBytes(int count)
		{
			Need(count);
			byte[] array = new byte[count];
			Buffer.BlockCopy(_data, _position, array, 0, count);
			_position += count;
			return array;
		}

		public string ReadString(int maxChars)
		{
			int num = ReadUInt16();
			if (num > maxChars * 3)
			{
				throw new ProtocolException("String too long");
			}
			Need(num);
			string text = Encoding.UTF8.GetString(_data, _position, num);
			_position += num;
			if (text.Length > maxChars)
			{
				throw new ProtocolException("String too long");
			}
			return text;
		}
	}
	internal static class ProtocolChecks
	{
		public static MatchMode Mode(byte value)
		{
			if (value != 0 && value != 1)
			{
				throw new ProtocolException("Unknown match mode");
			}
			return (MatchMode)value;
		}

		public static RacerStatus Status(byte value)
		{
			if (value > 6)
			{
				throw new ProtocolException("Unknown player status");
			}
			return (RacerStatus)value;
		}

		public static int Count(PacketReader r, int max)
		{
			byte num = r.ReadByte();
			if (num > max)
			{
				throw new ProtocolException("Too many entries");
			}
			return num;
		}
	}
	public sealed class LobbyMember
	{
		public string Name = "";

		public int Rating;

		public bool Host;

		public bool Racing;
	}
	public sealed class LobbyState : Message
	{
		public uint LobbyId;

		public MatchMode Mode;

		public MatchRules Rules;

		public int MaxPlayers;

		public bool YouAreHost;

		public bool Racing;

		public int ExpiresInSeconds = -1;

		public List<LobbyMember> Members = new List<LobbyMember>();

		public override MessageType Type => MessageType.LobbyState;

		public override void Write(PacketWriter w)
		{
			w.WriteUInt32(LobbyId);
			w.WriteByte((byte)Mode);
			w.WriteByte((byte)Rules);
			w.WriteByte((byte)MaxPlayers);
			w.WriteBool(YouAreHost);
			w.WriteBool(Racing);
			w.WriteInt32(ExpiresInSeconds);
			if (Members.Count > 15)
			{
				throw new ProtocolException("Too many lobby members");
			}
			w.WriteByte((byte)Members.Count);
			foreach (LobbyMember member in Members)
			{
				w.WriteString(member.Name, 24);
				w.WriteInt32(member.Rating);
				w.WriteBool(member.Host);
				w.WriteBool(member.Racing);
			}
		}

		public static LobbyState Read(PacketReader r)
		{
			LobbyState lobbyState = new LobbyState
			{
				LobbyId = r.ReadUInt32(),
				Mode = ProtocolChecks.Mode(r.ReadByte()),
				Rules = (MatchRules)(r.ReadByte() & 0xF),
				MaxPlayers = r.ReadByte(),
				YouAreHost = r.ReadBool(),
				Racing = r.ReadBool(),
				ExpiresInSeconds = r.ReadInt32()
			};
			int num = ProtocolChecks.Count(r, 15);
			for (int i = 0; i < num; i++)
			{
				lobbyState.Members.Add(new LobbyMember
				{
					Name = r.ReadString(24),
					Rating = r.ReadInt32(),
					Host = r.ReadBool(),
					Racing = r.ReadBool()
				});
			}
			return lobbyState;
		}
	}
	public sealed class LobbyStart : Message
	{
		public override MessageType Type => MessageType.LobbyStart;

		public override void Write(PacketWriter w)
		{
		}

		public static LobbyStart Read(PacketReader r)
		{
			return new LobbyStart();
		}
	}
	public sealed class PlayerReady : Message
	{
		public Guid MatchId;

		public override MessageType Type => MessageType.PlayerReady;

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
		}

		public static PlayerReady Read(PacketReader r)
		{
			return new PlayerReady
			{
				MatchId = r.ReadGuid()
			};
		}
	}
	public sealed class RosterEntry
	{
		public byte Racer;

		public string Name = "";

		public int Rating;

		public RacerStatus Status;

		public int TimeMs = -1;
	}
	public sealed class MatchRoster : Message
	{
		public Guid MatchId;

		public byte YourRacer;

		public int ReadySecondsLeft = -1;

		public List<RosterEntry> Racers = new List<RosterEntry>();

		public override MessageType Type => MessageType.MatchRoster;

		public RosterEntry Find(int racer)
		{
			foreach (RosterEntry racer2 in Racers)
			{
				if (racer2.Racer == racer)
				{
					return racer2;
				}
			}
			return null;
		}

		public override void Write(PacketWriter w)
		{
			w.WriteGuid(MatchId);
			w.WriteByte(YourRacer);
			w.WriteInt32(ReadySecondsLeft);
			if (Racers.Count > 15)
			{
				throw new ProtocolException("Too many players");
			}
			w.WriteByte((byte)Racers.Count);
			foreach (RosterEntry racer in Racers)
			{
				w.WriteByte(racer.Racer);
				w.WriteString(racer.Name, 24);
				w.WriteInt32(racer.Rating);
				w.WriteByte((byte)racer.Status);
				w.WriteInt32(racer.TimeMs);
			}
		}

		public static MatchRoster Read(PacketReader r)
		{
			MatchRoster matchRoster = new MatchRoster
			{
				MatchId = r.ReadGuid(),
				YourRacer = r.ReadByte(),
				ReadySecondsLeft = r.ReadInt32()
			};
			int num = ProtocolChecks.Count(r, 15);
			for (int i = 0; i < num; i++)
			{
				matchRoster.Racers.Add(new RosterEntry
				{
					Racer = r.ReadByte(),
					Name = r.ReadString(24),
					Rating = r.ReadInt32(),
					Status = ProtocolChecks.Status(r.ReadByte()),
					TimeMs = r.ReadInt32()
				});
			}
			return matchRoster;
		}
	}
	public sealed class Standing
	{
		public byte Racer;

		public string Name = "";

		public int Place;

		public RacerStatus Status;

		public int TimeMs = -1;

		public int Levels;

		public string Note = "";

		internal static void WriteList(PacketWriter w, List<Standing> list)
		{
			int num = list?.Count ?? 0;
			if (num > 15)
			{
				throw new ProtocolException("Too many standings");
			}
			w.WriteByte((byte)num);
			for (int i = 0; i < num; i++)
			{
				Standing standing = list[i];
				w.WriteByte(standing.Racer);
				w.WriteString(standing.Name, 24);
				w.WriteByte((byte)standing.Place);
				w.WriteByte((byte)standing.Status);
				w.WriteInt32(standing.TimeMs);
				w.WriteInt32(standing.Levels);
				w.WriteString(standing.Note, 64);
			}
		}

		internal static List<Standing> ReadList(PacketReader r)
		{
			int num = ProtocolChecks.Count(r, 15);
			List<Standing> list = new List<Standing>(num);
			for (int i = 0; i < num; i++)
			{
				list.Add(new Standing
				{
					Racer = r.ReadByte(),
					Name = r.ReadString(24),
					Place = r.ReadByte(),
					Status = ProtocolChecks.Status(r.ReadByte()),
					TimeMs = r.ReadInt32(),
					Levels = r.ReadInt32(),
					Note = r.ReadString(64)
				});
			}
			return list;
		}
	}
	public sealed class ModSettings : Message
	{
		public int GhostOpacityPercent = 45;

		public override MessageType Type => MessageType.ModSettings;

		public override void Write(PacketWriter w)
		{
			w.WriteByte((byte)Math.Max(0, Math.Min(100, GhostOpacityPercent)));
		}

		public static ModSettings Read(PacketReader r)
		{
			return new ModSettings
			{
				GhostOpacityPercent = Math.Min(100, (int)r.ReadByte())
			};
		}
	}
	public sealed class ChatSend : Message
	{
		public string Text = "";

		public override MessageType Type => MessageType.ChatSend;

		public override void Write(PacketWriter w)
		{
			w.WriteString(Text, 120);
		}

		public static ChatSend Read(PacketReader r)
		{
			return new ChatSend
			{
				Text = r.ReadString(120)
			};
		}
	}
	public sealed class ChatMessage : Message
	{
		public string From = "";

		public string Text = "";

		public bool System;

		public override MessageType Type => MessageType.ChatMessage;

		public override void Write(PacketWriter w)
		{
			w.WriteString(From, 24);
			w.WriteString(Text, 200);
			w.WriteBool(System);
		}

		public static ChatMessage Read(PacketReader r)
		{
			return new ChatMessage
			{
				From = r.ReadString(24),
				Text = r.ReadString(200),
				System = r.ReadBool()
			};
		}
	}
	public static class ProtocolConstants
	{
		public const int ProtocolVersion = 7;

		public const int DefaultServerPort = 47600;

		public const int DefaultLocalPort = 47677;

		public const int MaxFrameSize = 2048;

		public const int MaxNameLength = 24;

		public const int MaxLevelNameLength = 64;

		public const int MaxTextLength = 200;

		public const int MaxAddressLength = 100;

		public const int MaxUrlLength = 1500;

		public const int MaxItemNameLength = 48;

		public const int MaxBanPool = 16;

		public const int TokenLength = 32;

		public const int MinSeed = 1000000;

		public const int MaxSeed = 9999999;

		public const int MaxSplitIndex = 1000;

		public const int MaxAttempt = 100000;

		public const int MaxPartyPlayers = 15;

		public const int MaxChatLength = 120;

		public const int MaxDeathCauseLength = 32;

		public const int MaxDeathTextLength = 64;
	}
	public static class RaceFormat
	{
		public static string LevelLabel(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return "";
			}
			return Regex.Replace(Regex.Replace(Regex.Replace(raw, "^M\\d+_", ""), "^(Silos|Pipeworks|Habitation)_", "").Replace("Campaign_Interlude_", "").Replace('_', ' '), "(?<=[a-z])(?=[A-Z])", " ").Trim();
		}

		public static string ItemLabel(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return "";
			}
			return Regex.Replace(Regex.Replace(raw, "^Item_(Artifact_)?", "").Replace('_', ' '), "(?<=[a-z])(?=[A-Z])", " ").Trim();
		}

		public static string Time(long ms, int decimals = 2)
		{
			if (ms < 0)
			{
				ms = 0L;
			}
			long num = ms / 1000;
			int millis = (int)(ms % 1000);
			long num2 = num / 3600;
			int num3 = (int)(num / 60 % 60);
			int num4 = (int)(num % 60);
			string text = Fraction(millis, decimals);
			if (num2 > 0)
			{
				return string.Format(CultureInfo.InvariantCulture, "{0}:{1:00}:{2:00}{3}", num2, num3, num4, text);
			}
			return string.Format(CultureInfo.InvariantCulture, "{0}:{1:00}{2}", num3, num4, text);
		}

		public static string Delta(long ms, int decimals = 2)
		{
			string text = ((ms < 0) ? "-" : "+");
			long num = Math.Abs(ms);
			if (num < 60000)
			{
				return text + (num / 1000).ToString(CultureInfo.InvariantCulture) + Fraction((int)(num % 1000), decimals);
			}
			return text + Time(num, decimals);
		}

		private static string Fraction(int millis, int decimals)
		{
			if (decimals <= 0)
			{
				return string.Empty;
			}
			string text = millis.ToString("000", CultureInfo.InvariantCulture);
			return "." + text.Substring(0, Math.Min(3, decimals));
		}
	}
	public static class Fnv1a
	{
		public const uint Offset = 2166136261u;

		public static uint Add(uint hash, string value)
		{
			if (value != null)
			{
				for (int i = 0; i < value.Length; i++)
				{
					hash ^= value[i];
					hash *= 16777619;
				}
			}
			hash ^= 0xFF;
			hash *= 16777619;
			return hash;
		}

		public static uint Add(uint hash, int value)
		{
			for (int i = 0; i < 4; i++)
			{
				hash ^= (byte)(value >> 8 * i);
				hash *= 16777619;
			}
			return hash;
		}
	}
	public static class SplitKeys
	{
		public static string Key(string level, int occurrence)
		{
			string text = level ?? "";
			if (occurrence > 1)
			{
				return text + "#" + occurrence;
			}
			return text;
		}

		public static List<string> Build(IEnumerable<string> levelsInOrder)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			List<string> list = new List<string>();
			foreach (string item in levelsInOrder)
			{
				string text = item ?? "";
				dictionary.TryGetValue(text, out var value);
				value = (dictionary[text] = value + 1);
				list.Add(Key(text, value));
			}
			return list;
		}
	}
	public sealed class RacerProgress
	{
		public int Racer;

		public string Name = "";

		public bool Finished;

		public int FinishMs = -1;

		public int FinishOrder;

		public bool Out;

		public int OutMs = -1;

		public bool Disqualified;

		public readonly List<string> Route = new List<string>();

		public readonly List<int> RouteMs = new List<int>();

		public readonly List<string> Regions = new List<string>();

		public int Levels => Route.Count;

		public int LastMs
		{
			get
			{
				if (RouteMs.Count != 0)
				{
					return RouteMs[RouteMs.Count - 1];
				}
				return 0;
			}
		}

		public bool Done
		{
			get
			{
				if (!Finished && !Out)
				{
					return Disqualified;
				}
				return true;
			}
		}

		public bool OnShortcut
		{
			get
			{
				if (!Done && Regions.Count > 0)
				{
					return Standings.IsShortcut(Regions[Regions.Count - 1]);
				}
				return false;
			}
		}

		public void AddSplit(string level, string region, int timeMs)
		{
			Route.Add(level ?? "");
			Regions.Add(region ?? "");
			RouteMs.Add(timeMs);
		}
	}
	public static class Standings
	{
		private static readonly string[] ShortcutRegions = new string[5] { "shortcut", "Tangled Sink", "Expulsion Chute", "Rotten Duct", "Hadal Diver" };

		public static bool IsShortcut(string region)
		{
			if (string.IsNullOrEmpty(region))
			{
				return false;
			}
			string[] shortcutRegions = ShortcutRegions;
			for (int i = 0; i < shortcutRegions.Length; i++)
			{
				if (string.Equals(shortcutRegions[i], region.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		public static bool SameRoute(RacerProgress a, RacerProgress b)
		{
			int num = Math.Min(a.Route.Count, b.Route.Count);
			for (int i = 0; i < num; i++)
			{
				if (!string.Equals(a.Route[i], b.Route[i], StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
			}
			return true;
		}

		public static int Compare(RacerProgress a, RacerProgress b, int nowMs)
		{
			if (a.Disqualified != b.Disqualified)
			{
				if (!a.Disqualified)
				{
					return -1;
				}
				return 1;
			}
			if (a.Finished != b.Finished)
			{
				if (!a.Finished)
				{
					return 1;
				}
				return -1;
			}
			if (a.Finished)
			{
				if (a.FinishMs != b.FinishMs)
				{
					return a.FinishMs.CompareTo(b.FinishMs);
				}
				if (a.FinishOrder > 0 && b.FinishOrder > 0)
				{
					return a.FinishOrder.CompareTo(b.FinishOrder);
				}
			}
			if (SameRoute(a, b))
			{
				if (a.Levels != b.Levels)
				{
					return b.Levels.CompareTo(a.Levels);
				}
				int num = a.LastMs.CompareTo(b.LastMs);
				if (num != 0)
				{
					return num;
				}
			}
			else
			{
				int num2 = (a.Out ? a.OutMs : nowMs);
				int num3 = (b.Out ? b.OutMs : nowMs);
				if (num2 != num3)
				{
					return num3.CompareTo(num2);
				}
				if (a.Levels != b.Levels)
				{
					return b.Levels.CompareTo(a.Levels);
				}
			}
			if (a.Out != b.Out)
			{
				if (!a.Out)
				{
					return -1;
				}
				return 1;
			}
			return a.Racer.CompareTo(b.Racer);
		}

		public static List<RacerProgress> Rank(IEnumerable<RacerProgress> racers, int nowMs)
		{
			List<RacerProgress> list = new List<RacerProgress>();
			foreach (RacerProgress racer in racers)
			{
				int num = list.Count;
				while (num > 0 && Compare(racer, list[num - 1], nowMs) < 0)
				{
					num--;
				}
				list.Insert(num, racer);
			}
			return list;
		}

		public static string Ordinal(int place)
		{
			int num = place % 100;
			string text = ((num >= 11 && num <= 13) ? "th" : ((place % 10 == 1) ? "st" : ((place % 10 == 2) ? "nd" : ((place % 10 == 3) ? "rd" : "th"))));
			return place + text;
		}
	}
	public static class DeathText
	{
		public static string ForOthers(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			string[] array = text.Split(' ');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i];
				string text3 = text2.TrimEnd('.', ',', '!', '?');
				string text4 = text2.Substring(text3.Length);
				string text5 = Swap(text3);
				if (text5 != null)
				{
					array[i] = text5 + text4;
				}
			}
			return string.Join(" ", array);
		}

		private static string Swap(string word)
		{
			string text;
			switch (word.ToLowerInvariant())
			{
			case "your":
				text = "their";
				break;
			case "yours":
				text = "theirs";
				break;
			case "yourself":
				text = "themselves";
				break;
			case "you":
				text = "they";
				break;
			default:
				return null;
			}
			if (word == word.ToUpperInvariant())
			{
				return text.ToUpperInvariant();
			}
			if (char.IsUpper(word[0]))
			{
				return char.ToUpperInvariant(text[0]) + text.Substring(1);
			}
			return text;
		}
	}
	public static class RulesText
	{
		public static string Describe(MatchRules rules)
		{
			List<string> list = new List<string>();
			if ((rules & MatchRules.ArtifactBans) != MatchRules.None)
			{
				list.Add("artifact bans");
			}
			MatchRules matchRules = rules & MatchRules.CuratedSeeds;
			switch (matchRules)
			{
			case MatchRules.CuratedSeeds:
				list.Add("curated seeds");
				break;
			default:
			{
				List<string> list2 = new List<string>();
				if ((matchRules & MatchRules.DeepStorageFood) != MatchRules.None)
				{
					list2.Add("food");
				}
				if ((matchRules & MatchRules.GuaranteedPerks) != MatchRules.None)
				{
					list2.Add("perks");
				}
				if ((matchRules & MatchRules.GuaranteedItems) != MatchRules.None)
				{
					list2.Add("items");
				}
				list.Add("curated seeds (" + string.Join(", ", list2) + ")");
				break;
			}
			case MatchRules.None:
				break;
			}
			if (list.Count == 0)
			{
				return "No modifiers";
			}
			string text = string.Join(", ", list);
			return char.ToUpperInvariant(text[0]) + text.Substring(1);
		}

		public static string Mode(MatchMode mode)
		{
			if (mode != MatchMode.Party)
			{
				return "1v1";
			}
			return "Party";
		}
	}
	public sealed class SteamLinkStart : Message
	{
		public override MessageType Type => MessageType.SteamLinkStart;

		public override void Write(PacketWriter w)
		{
		}

		public static SteamLinkStart Read(PacketReader r)
		{
			return new SteamLinkStart();
		}
	}
	public sealed class SteamLinkUrl : Message
	{
		public string Url = "";

		public override MessageType Type => MessageType.SteamLinkUrl;

		public override void Write(PacketWriter w)
		{
			w.WriteString(Url, 1500);
		}

		public static SteamLinkUrl Read(PacketReader r)
		{
			return new SteamLinkUrl
			{
				Url = r.ReadString(1500)
			};
		}
	}
	public sealed class SteamLinkResult : Message
	{
		public bool Ok;

		public string Message = "";

		public Guid PlayerId;

		public byte[] Token = new byte[0];

		public string Name = "";

		public int Rating;

		public int Wins;

		public int Losses;

		public override MessageType Type => MessageType.SteamLinkResult;

		public override void Write(PacketWriter w)
		{
			w.WriteBool(Ok);
			w.WriteString(Message, 200);
			w.WriteGuid(PlayerId);
			if (Token.Length != 0 && Token.Length != 32)
			{
				throw new ProtocolException("Bad token length");
			}
			w.WriteBool(Token.Length != 0);
			if (Token.Length != 0)
			{
				w.WriteFixedBytes(Token, 32);
			}
			w.WriteString(Name, 24);
			w.WriteInt32(Rating);
			w.WriteInt32(Wins);
			w.WriteInt32(Losses);
		}

		public static SteamLinkResult Read(PacketReader r)
		{
			return new SteamLinkResult
			{
				Ok = r.ReadBool(),
				Message = r.ReadString(200),
				PlayerId = r.ReadGuid(),
				Token = (r.ReadBool() ? r.ReadFixedBytes(32) : new byte[0]),
				Name = r.ReadString(24),
				Rating = r.ReadInt32(),
				Wins = r.ReadInt32(),
				Losses = r.ReadInt32()
			};
		}
	}
	public sealed class VoiceHello : Message
	{
		public int Port;

		public byte[] Key = new byte[16];

		public ulong Token;

		public int RangeMeters = 65;

		public override MessageType Type => MessageType.VoiceHello;

		public override void Write(PacketWriter w)
		{
			w.WriteInt32(Port);
			w.WriteFixedBytes(Key, 16);
			w.WriteInt64((long)Token);
			w.WriteInt32(RangeMeters);
		}

		public static VoiceHello Read(PacketReader r)
		{
			return new VoiceHello
			{
				Port = r.ReadInt32(),
				Key = r.ReadFixedBytes(16),
				Token = (ulong)r.ReadInt64(),
				RangeMeters = r.ReadInt32()
			};
		}
	}
	public sealed class VoiceState : Message
	{
		public bool Enabled;

		public override MessageType Type => MessageType.VoiceState;

		public override void Write(PacketWriter w)
		{
			w.WriteBool(Enabled);
		}

		public static VoiceState Read(PacketReader r)
		{
			return new VoiceState
			{
				Enabled = r.ReadBool()
			};
		}
	}
	public sealed class VoicePeer
	{
		public ushort VoiceId;

		public string Name = "";

		public byte Racer = byte.MaxValue;

		public const byte NoRacer = byte.MaxValue;
	}
	public sealed class VoicePeers : Message
	{
		public List<VoicePeer> Peers = new List<VoicePeer>();

		public override MessageType Type => MessageType.VoicePeers;

		public override void Write(PacketWriter w)
		{
			if (Peers.Count > 15)
			{
				throw new ProtocolException("Too many voice peers");
			}
			w.WriteByte((byte)Peers.Count);
			foreach (VoicePeer peer in Peers)
			{
				w.WriteUInt16(peer.VoiceId);
				w.WriteString(peer.Name, 24);
				w.WriteByte(peer.Racer);
			}
		}

		public static VoicePeers Read(PacketReader r)
		{
			VoicePeers voicePeers = new VoicePeers();
			int num = ProtocolChecks.Count(r, 15);
			for (int i = 0; i < num; i++)
			{
				voicePeers.Peers.Add(new VoicePeer
				{
					VoiceId = r.ReadUInt16(),
					Name = r.ReadString(24),
					Racer = r.ReadByte()
				});
			}
			return voicePeers;
		}
	}
	public sealed class VoiceActivity : Message
	{
		public bool On;

		public bool Me;

		public List<byte> Racers = new List<byte>();

		public override MessageType Type => MessageType.VoiceActivity;

		public override void Write(PacketWriter w)
		{
			w.WriteBool(On);
			w.WriteBool(Me);
			int num = Math.Min(Racers.Count, 15);
			w.WriteByte((byte)num);
			for (int i = 0; i < num; i++)
			{
				w.WriteByte(Racers[i]);
			}
		}

		public static VoiceActivity Read(PacketReader r)
		{
			VoiceActivity voiceActivity = new VoiceActivity
			{
				On = r.ReadBool(),
				Me = r.ReadBool()
			};
			int num = ProtocolChecks.Count(r, 15);
			for (int i = 0; i < num; i++)
			{
				voiceActivity.Racers.Add(r.ReadByte());
			}
			return voiceActivity;
		}
	}
	public static class VoiceWire
	{
		public const byte Magic = 87;

		public const byte KindAudio = 1;

		public const byte KindKeepAlive = 2;

		public const byte KindRelay = 3;

		public const int KeyLength = 16;

		public const int TagLength = 16;

		public const int HeaderLength = 18;

		public const int MaxOpusBytes = 400;

		public const int MaxPacket = 438;

		public const int SampleRate = 48000;

		public const int FrameSamples = 960;

		private const byte ToServer = 1;

		private const byte ToClient = 2;

		public static byte[] NewKey()
		{
			byte[] array = new byte[16];
			RandomNumberGenerator.Fill(array);
			return array;
		}

		public static byte[] SealToServer(byte[] key, ulong token, ulong counter, byte kind, uint seq, ReadOnlySpan<byte> opus)
		{
			return Seal(key, kind, token, counter, 1, seq, opus, kind == 1);
		}

		public static byte[] SealToClient(byte[] listenerKey, ushort speaker, ulong counter, uint seq, ReadOnlySpan<byte> opus)
		{
			return Seal(listenerKey, 3, speaker, counter, 2, seq, opus, audio: true);
		}

		private static byte[] Seal(byte[] key, byte kind, ulong id, ulong counter, byte direction, uint seq, ReadOnlySpan<byte> opus, bool audio)
		{
			int num = (audio ? (4 + opus.Length) : 0);
			byte[] array = new byte[18 + num + 16];
			array[0] = 87;
			array[1] = kind;
			WriteU64(array, 2, id);
			WriteU64(array, 10, counter);
			byte[] array2 = new byte[num];
			if (audio)
			{
				array2[0] = (byte)seq;
				array2[1] = (byte)(seq >> 8);
				array2[2] = (byte)(seq >> 16);
				array2[3] = (byte)(seq >> 24);
				opus.CopyTo(array2.AsSpan(4));
			}
			using AesGcm aesGcm = new AesGcm(key);
			aesGcm.Encrypt(Nonce(direction, counter), array2, array.AsSpan(18, num), array.AsSpan(18 + num, 16), array.AsSpan(0, 18));
			return array;
		}

		public static bool TryHeader(byte[] packet, int length, out byte kind, out ulong id, out ulong counter)
		{
			kind = 0;
			id = 0uL;
			counter = 0uL;
			if (packet == null || length < 34 || length > 438 || packet[0] != 87)
			{
				return false;
			}
			kind = packet[1];
			id = ReadU64(packet, 2);
			counter = ReadU64(packet, 10);
			if (kind != 1 && kind != 2)
			{
				return kind == 3;
			}
			return true;
		}

		public static bool TryOpen(byte[] key, byte[] packet, int length, bool toServer, out byte[] body)
		{
			body = null;
			if (!TryHeader(packet, length, out var _, out var _, out var counter))
			{
				return false;
			}
			int num = length - 18 - 16;
			if (num != 0 && num < 4)
			{
				return false;
			}
			byte[] array = new byte[num];
			try
			{
				using AesGcm aesGcm = new AesGcm(key);
				aesGcm.Decrypt(Nonce((byte)(toServer ? 1 : 2), counter), packet.AsSpan(18, num), packet.AsSpan(18 + num, 16), array, packet.AsSpan(0, 18));
			}
			catch (CryptographicException)
			{
				return false;
			}
			body = array;
			return true;
		}

		public static uint Seq(byte[] body)
		{
			return (uint)(body[0] | (body[1] << 8) | (body[2] << 16) | (body[3] << 24));
		}

		private static byte[] Nonce(byte direction, ulong counter)
		{
			byte[] obj = new byte[12]
			{
				direction, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0
			};
			WriteU64(obj, 4, counter);
			return obj;
		}

		private static void WriteU64(byte[] b, int at, ulong v)
		{
			for (int i = 0; i < 8; i++)
			{
				b[at + i] = (byte)(v >> 8 * i);
			}
		}

		private static ulong ReadU64(byte[] b, int at)
		{
			ulong num = 0uL;
			for (int i = 0; i < 8; i++)
			{
				num |= (ulong)b[at + i] << 8 * i;
			}
			return num;
		}
	}
}
namespace WKRace.Mod
{
	internal static class ArtifactBans
	{
		private static List<string> _pool;

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

		private static readonly HashSet<string> Banned = new HashSet<string>();

		public static void Apply(Harmony harmony)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ENV_ArtifactDevice), "Start", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogError((object)"Patch target missing for artifact bans. The game may have been updated.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ArtifactBans), "BeforeDeviceStart", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		public static List<string> Pool()
		{
			if (_pool != null)
			{
				return _pool;
			}
			try
			{
				List<string> list = new List<string>();
				WKAssetDatabase fullCombinedAssetDatabase = CL_AssetManager.GetFullCombinedAssetDatabase();
				if ((Object)(object)fullCombinedAssetDatabase == (Object)null || fullCombinedAssetDatabase.levelAssets == null || fullCombinedAssetDatabase.levelAssets.Count == 0)
				{
					return null;
				}
				foreach (LevelAssetHolder levelAsset in fullCombinedAssetDatabase.levelAssets)
				{
					M_Level val = ((levelAsset != null) ? levelAsset.GetLevel() : null);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					ENV_ArtifactDevice[] componentsInChildren = ((Component)val).GetComponentsInChildren<ENV_ArtifactDevice>(true);
					foreach (ENV_ArtifactDevice val2 in componentsInChildren)
					{
						if (val2.artifacts == null)
						{
							continue;
						}
						foreach (Item_Object artifact in val2.artifacts)
						{
							if (!((Object)(object)artifact == (Object)null) && !list.Contains(((Object)artifact).name))
							{
								list.Add(((Object)artifact).name);
								string value = ((artifact.itemData != null) ? artifact.itemData.itemName : null);
								if (!string.IsNullOrEmpty(value))
								{
									Labels[((Object)artifact).name] = value;
								}
							}
						}
					}
				}
				_pool = list;
				Plugin.Log.LogInfo((object)("Artifact pool: " + string.Join(", ", list)));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not read the artifact pool: " + ex.Message));
			}
			return _pool;
		}

		public static string Label(string item)
		{
			if (item != null && Labels.TryGetValue(item, out var value))
			{
				return value;
			}
			return RaceFormat.ItemLabel(item);
		}

		public static void Set(IEnumerable<string> items)
		{
			Banned.Clear();
			if (items == null)
			{
				return;
			}
			foreach (string item in items)
			{
				Banned.Add(item);
			}
		}

		public static void Clear()
		{
			Banned.Clear();
		}

		public static IEnumerable<string> Current()
		{
			return Banned.OrderBy<string, string>((string b) => b, StringComparer.Ordinal);
		}

		private static void BeforeDeviceStart(ENV_ArtifactDevice __instance)
		{
			try
			{
				if (!RaceRules.InRaceWorld || Banned.Count == 0 || __instance.artifacts == null)
				{
					return;
				}
				List<Item_Object> list = __instance.artifacts.Where((Item_Object a) => (Object)(object)a != (Object)null && !Banned.Contains(((Object)a).name)).ToList();
				if (list.Count != __instance.artifacts.Count)
				{
					if (__instance.artifactSpawners != null && list.Count < __instance.artifactSpawners.Count)
					{
						__instance.artifactSpawners = __instance.artifactSpawners.Take(list.Count).ToList();
					}
					__instance.artifacts = list;
					Plugin.Log.LogInfo((object)("Artifact bans applied: " + string.Join(", ", Banned) + " (" + list.Count + " left)"));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Artifact bans failed: " + ex.Message));
			}
		}
	}
	internal sealed class ChatBox
	{
		public struct Line
		{
			public string From;

			public string Text;

			public bool System;

			public float At;
		}

		public const int ShownLines = 8;

		public const float LineSeconds = 14f;

		public readonly List<Line> Lines = new List<Line>();

		private int _openedFrame;

		public bool Typing { get; private set; }

		public string Draft { get; private set; } = "";

		public void Add(ChatMessage m)
		{
			Lines.Add(new Line
			{
				From = (m.From ?? ""),
				Text = (m.Text ?? ""),
				System = m.System,
				At = Time.unscaledTime
			});
			while (Lines.Count > 30)
			{
				Lines.RemoveAt(0);
			}
		}

		public void Note(string text)
		{
			Lines.Add(new Line
			{
				Text = text,
				System = true,
				At = Time.unscaledTime
			});
		}

		public void Open()
		{
			if (!Typing)
			{
				Typing = true;
				Draft = "";
				_openedFrame = Time.frameCount;
				ChatInputBlock.Begin();
			}
		}

		public void Close()
		{
			if (Typing)
			{
				Typing = false;
				Draft = "";
				ChatInputBlock.End();
			}
		}

		public string Update()
		{
			if (!Typing)
			{
				return null;
			}
			if (Input.GetKeyDown((KeyCode)27))
			{
				Close();
				return null;
			}
			if (Time.frameCount == _openedFrame)
			{
				return null;
			}
			bool flag = Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271);
			StringBuilder stringBuilder = new StringBuilder(Draft);
			string inputString = Input.inputString;
			foreach (char c in inputString)
			{
				switch (c)
				{
				case '\b':
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Length--;
					}
					break;
				case '\n':
				case '\r':
					flag = true;
					break;
				default:
					if (!char.IsControl(c) && stringBuilder.Length < 120)
					{
						stringBuilder.Append(c);
					}
					break;
				}
			}
			Draft = stringBuilder.ToString();
			if (!flag)
			{
				return null;
			}
			string text = Draft.Trim();
			Close();
			if (text.Length <= 0)
			{
				return null;
			}
			return text;
		}
	}
	internal static class ChatInputBlock
	{
		private static int _endFrame = -10;

		private static bool _typing;

		private static readonly Dictionary<ButtonInput, bool> Held = new Dictionary<ButtonInput, bool>();

		private static FieldRef<InputManager, List<ButtonInput>> _buttons;

		private static FieldRef<InputManager, List<VectorInput>> _vectors;

		public static bool Active
		{
			get
			{
				if (!_typing)
				{
					return Time.frameCount <= _endFrame + 1;
				}
				return true;
			}
		}

		public static void Apply(Harmony harmony)
		{
			_buttons = AccessTools.FieldRefAccess<InputManager, List<ButtonInput>>("buttonInputs");
			_vectors = AccessTools.FieldRefAccess<InputManager, List<VectorInput>>("vectorInputs");
			PatchOne(harmony, AccessTools.Method(typeof(InputManager), "UpdateInputs", (Type[])null, (Type[])null), null, "AfterUpdateInputs");
			PatchOne(harmony, AccessTools.Method(typeof(CommandConsole), "LateUpdate", (Type[])null, (Type[])null), "BeforeConsoleUpdate", null);
		}

		private static void PatchOne(Harmony harmony, MethodBase target, string prefix, string postfix)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (target == null)
			{
				Plugin.Log.LogError((object)("Chat input block: patch target missing for " + (prefix ?? postfix) + ". The game may have been updated."));
				return;
			}
			try
			{
				harmony.Patch(target, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ChatInputBlock), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ChatInputBlock), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Chat input block: could not patch " + target.Name + ": " + ex.Message));
			}
		}

		public static void Begin()
		{
			_typing = true;
			Held.Clear();
			InputManager instance = InputManager.instance;
			List<ButtonInput> list = (((Object)(object)instance != (Object)null && _buttons != null) ? _buttons.Invoke(instance) : null);
			if (list == null)
			{
				return;
			}
			foreach (ButtonInput item in list)
			{
				if (item != null)
				{
					Held[item] = item.Pressed;
				}
			}
		}

		public static void End()
		{
			_typing = false;
			_endFrame = Time.frameCount;
		}

		private static void AfterUpdateInputs(InputManager __instance)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				return;
			}
			try
			{
				List<ButtonInput> list = _buttons.Invoke(__instance);
				if (list != null)
				{
					foreach (ButtonInput item in list)
					{
						if (item != null)
						{
							item.Pressed = Held.TryGetValue(item, out var value) && value;
							item.Down = false;
							item.Up = false;
						}
					}
				}
				List<VectorInput> list2 = _vectors.Invoke(__instance);
				if (list2 == null)
				{
					return;
				}
				foreach (VectorInput item2 in list2)
				{
					if (item2 != null)
					{
						item2.vector = Vector2.zero;
						item2.axis = 0f;
						item2.Pressed = false;
						item2.Down = false;
						item2.Up = false;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Chat input block failed: " + ex.Message));
				_typing = false;
			}
		}

		private static bool BeforeConsoleUpdate()
		{
			return !Active;
		}
	}
	internal static class ClientStarter
	{
		private const string ClientExe = "WKSR.exe";

		public static void OpenIfInstalled(string pluginPath)
		{
			try
			{
				if (RaceConfig.OpenClient.Value && !string.IsNullOrEmpty(pluginPath))
				{
					string directoryName = Path.GetDirectoryName(pluginPath);
					string text = ((directoryName == null) ? null : Path.Combine(directoryName, "WKSR.exe"));
					if (text != null && File.Exists(text) && Process.GetProcessesByName(Path.GetFileNameWithoutExtension("WKSR.exe")).Length == 0)
					{
						Process.Start(new ProcessStartInfo(text)
						{
							UseShellExecute = true,
							WorkingDirectory = directoryName
						});
						Plugin.Log.LogInfo((object)("Opened WKSR (" + text + ")."));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not open WKSR: " + ex.Message));
			}
		}
	}
	internal static class GameBridge
	{
		public const string MenuScene = "Main-Menu";

		public const string RunScene = "Game-Main";

		public const string CampaignModeName = "Campaign";

		private const string CampaignAssetName = "GM_Campaign";

		public static bool Frozen;

		private static bool _lockedByUs;

		private static Vector3 _savedVelocity;

		private static readonly FieldRef<ENT_Player, Vector3> PlayerVelocity = SafeFieldRef("vel");

		public static bool InMenu
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name == "Main-Menu";
			}
		}

		public static bool InRunScene
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name == "Game-Main";
			}
		}

		public static ENT_Player Player => ENT_Player.playerObject;

		public static string GameVersion()
		{
			try
			{
				return DarkMachineFunctions.GetVersionName("").Trim();
			}
			catch (Exception)
			{
				return Application.version;
			}
		}

		public static M_Gamemode FindCampaign()
		{
			M_Gamemode val = null;
			M_Gamemode[] array = Resources.FindObjectsOfTypeAll<M_Gamemode>();
			foreach (M_Gamemode val2 in array)
			{
				if (((Object)val2).name == "GM_Campaign")
				{
					return val2;
				}
				if ((Object)(object)val == (Object)null && val2.baseGamemode && !val2.devOnly && val2.allowCompetitive && string.Equals(val2.gamemodeName, "Campaign", StringComparison.OrdinalIgnoreCase))
				{
					val = val2;
				}
			}
			return val;
		}

		public static void BeginSeededRun(M_Gamemode gamemode, int seed)
		{
			WorldLoader.SetPresetSeed(seed.ToString(CultureInfo.InvariantCulture));
			CL_GameManager.gamemode = gamemode;
			SceneManager.LoadScene(gamemode.gamemodeScene);
		}

		public static void ApplySeed(int seed)
		{
			WorldLoader.SetPresetSeed(seed.ToString(CultureInfo.InvariantCulture));
		}

		public static bool IsWorldReady()
		{
			if (InRunScene && (Object)(object)WorldLoader.instance != (Object)null && WorldLoader.isLoaded && (Object)(object)CL_GameManager.gMan != (Object)null && (Object)(object)CL_GameManager.gMan.localPlayer != (Object)null)
			{
				return !CL_GameManager.IsLoading();
			}
			return false;
		}

		public static string DeathText(string cause)
		{
			try
			{
				string localization = CL_LocalizationManager.GetLocalization("deathmessages", cause ?? "");
				if (string.IsNullOrEmpty(localization))
				{
					localization = CL_LocalizationManager.GetLocalization("deathmessages", "basic");
				}
				return localization ?? "";
			}
			catch (Exception)
			{
				return "";
			}
		}

		public static Vector3 OriginOffset()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			CL_GameManager gMan = CL_GameManager.gMan;
			if (!((Object)(object)gMan == (Object)null))
			{
				return -gMan.GetCorrectedPosition(Vector3.zero);
			}
			return Vector3.zero;
		}

		public static Vector3 ToShared(Vector3 local)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			CL_GameManager gMan = CL_GameManager.gMan;
			if (!((Object)(object)gMan == (Object)null))
			{
				return gMan.GetCorrectedPosition(local);
			}
			return local;
		}

		public static string LevelNameAhead(int steps)
		{
			WorldLoader instance = WorldLoader.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return "";
			}
			BranchInfo currentBranch = instance.GetCurrentBranch();
			LevelInfo currentLevel = instance.GetCurrentLevel();
			if (currentBranch == null || currentBranch.levelTracker == null || currentLevel == null)
			{
				return "";
			}
			int num = currentBranch.levelTracker.IndexOf(currentLevel) + steps;
			if (num < steps || num >= currentBranch.levelTracker.Count)
			{
				return "";
			}
			M_Level level = currentBranch.levelTracker[num].GetLevel();
			if (!((Object)(object)level != (Object)null))
			{
				return "";
			}
			return level.levelName;
		}

		public static uint WorldFingerprint(out int levelCount)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			levelCount = 0;
			WorldLoader instance = WorldLoader.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0u;
			}
			BranchInfo currentBranch = instance.GetCurrentBranch();
			if (currentBranch == null || currentBranch.levelTracker == null)
			{
				return 0u;
			}
			uint hash = Fnv1a.Add(2166136261u, 2);
			hash = Fnv1a.Add(hash, (int)RaceGuarantees.Rules);
			foreach (string item in ArtifactBans.Current())
			{
				hash = Fnv1a.Add(hash, item);
			}
			foreach (LevelInfo item2 in currentBranch.levelTracker)
			{
				M_Level level = item2.GetLevel();
				if (!((Object)(object)level == (Object)null))
				{
					LevelAssetHolder levelAssetReference = item2.GetLevelAssetReference();
					Vector3 val = ToShared(((Component)level).transform.position);
					hash = Fnv1a.Add(hash, (levelAssetReference != null) ? levelAssetReference.id : level.levelName);
					hash = Fnv1a.Add(hash, Mathf.RoundToInt(val.x * 10f));
					hash = Fnv1a.Add(hash, Mathf.RoundToInt(val.y * 10f));
					hash = Fnv1a.Add(hash, Mathf.RoundToInt(val.z * 10f));
					hash = Fnv1a.Add(hash, (((Component)level).transform.localScale.x < 0f) ? 1 : 0);
					levelCount++;
				}
			}
			if (hash != 0)
			{
				return hash;
			}
			return 1u;
		}

		public static bool TryReadPlayer(PlayerState state)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			ENT_Player playerObject = ENT_Player.playerObject;
			if ((Object)(object)playerObject == (Object)null || (Object)(object)CL_GameManager.gMan == (Object)null)
			{
				return false;
			}
			Transform obj = (((Object)(object)playerObject.cam != (Object)null) ? ((Component)playerObject.cam).transform : ((Component)playerObject).transform);
			Vector3 val = ToShared(obj.position);
			state.X = val.x;
			state.Y = val.y;
			state.Z = val.z;
			Vector3 eulerAngles = obj.eulerAngles;
			state.Yaw = eulerAngles.y;
			state.Pitch = ((eulerAngles.x > 180f) ? (eulerAngles.x - 360f) : eulerAngles.x);
			StateFlags stateFlags = StateFlags.None;
			if (((GameEntity)playerObject).dead)
			{
				stateFlags |= StateFlags.Dead;
			}
			if (!IsWorldReady())
			{
				stateFlags |= StateFlags.Loading;
			}
			Hand[] hands = playerObject.hands;
			if (hands != null && hands.Length >= 2)
			{
				if (hands[0] != null && hands[0].IsHolding())
				{
					Vector3 val2 = ToShared(hands[0].GetHoldWorldPosition());
					state.LeftX = val2.x;
					state.LeftY = val2.y;
					state.LeftZ = val2.z;
					stateFlags |= StateFlags.LeftHold;
				}
				if (hands[1] != null && hands[1].IsHolding())
				{
					Vector3 val3 = ToShared(hands[1].GetHoldWorldPosition());
					state.RightX = val3.x;
					state.RightY = val3.y;
					state.RightZ = val3.z;
					stateFlags |= StateFlags.RightHold;
				}
			}
			state.Flags = stateFlags;
			return true;
		}

		public static void FreezeWorld()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (Frozen)
			{
				return;
			}
			Frozen = true;
			ENT_Player playerObject = ENT_Player.playerObject;
			if ((Object)(object)playerObject != (Object)null)
			{
				if (PlayerVelocity != null)
				{
					_savedVelocity = PlayerVelocity.Invoke(playerObject);
				}
				playerObject.Lock();
				_lockedByUs = true;
			}
			CL_GameManager.RefreshTimescale();
		}

		public static void ThawWorld()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Frozen = false;
			ENT_Player playerObject = ENT_Player.playerObject;
			if (_lockedByUs && (Object)(object)playerObject != (Object)null)
			{
				playerObject.UnLock();
				if (PlayerVelocity != null)
				{
					PlayerVelocity.Invoke(playerObject) = _savedVelocity;
				}
			}
			_lockedByUs = false;
			_savedVelocity = Vector3.zero;
			if ((Object)(object)CL_GameManager.gMan != (Object)null)
			{
				CL_GameManager.RefreshTimescale();
			}
		}

		private static FieldRef<ENT_Player, Vector3> SafeFieldRef(string name)
		{
			try
			{
				return AccessTools.FieldRefAccess<ENT_Player, Vector3>(name);
			}
			catch (Exception)
			{
				return null;
			}
		}

		public static void ClearFreezeFlags()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Frozen = false;
			_lockedByUs = false;
			_savedVelocity = Vector3.zero;
		}
	}
	internal static class RaceRules
	{
		public static bool Active;

		public static bool Competitive = true;

		public static int Seed;

		public static bool InRaceWorld;

		private static M_Gamemode _gamemode;

		private static bool _originalAllowCheats;

		public static void Begin(M_Gamemode gamemode, int seed, bool competitive)
		{
			if (Active && (Object)(object)_gamemode == (Object)(object)gamemode)
			{
				Seed = seed;
				Competitive = competitive;
				return;
			}
			End();
			Active = true;
			Seed = seed;
			Competitive = competitive;
			_gamemode = gamemode;
			_originalAllowCheats = gamemode.allowCheats;
			if (!RaceConfig.CheatsAllowed)
			{
				gamemode.allowCheats = false;
			}
		}

		public static void End()
		{
			if (Active)
			{
				Active = false;
				if ((Object)(object)_gamemode != (Object)null)
				{
					_gamemode.allowCheats = _originalAllowCheats;
				}
				_gamemode = null;
			}
		}
	}
	internal sealed class Ghost
	{
		private const float StaleSeconds = 4f;

		private const float HideBelowDistance = 0.8f;

		private const float FadeBelowDistance = 2.2f;

		private GameObject _root;

		private Transform _head;

		private Transform _body;

		private Transform _nose;

		private Transform _leftHand;

		private Transform _rightHand;

		private Material _material;

		private bool _hasTarget;

		private float _lastUpdate;

		private Vector3 _targetEye;

		private Vector3 _targetLeft;

		private Vector3 _targetRight;

		private bool _holdLeft;

		private bool _holdRight;

		private float _targetYaw;

		private Vector3 _eye;

		private Vector3 _left;

		private Vector3 _right;

		private float _yaw;

		private bool _dead;

		public bool Visible { get; private set; }

		public Vector3 EyeWorld => _eye;

		public void SetTarget(OpponentState s)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)