Clan.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
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 JetBrains.Annotations;
using Jotunn.GUI;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using ServerSync;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.TextCore;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Clan")]
[assembly: AssemblyDescription("Server-authoritative Valheim clan system")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("Clan")]
[assembly: AssemblyCopyright("Copyright © sighsorry 2026")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Clan
{
	public enum ClanRole
	{
		Leader,
		Officer,
		Member,
		Guest
	}
	internal enum ClanRequestType
	{
		RequestSnapshot,
		CreateClan,
		Invite,
		Apply,
		AcceptInvite,
		DeclineInvite,
		AcceptApplication,
		RejectApplication,
		KickPlayer,
		TransferLeadership,
		SetRole,
		LeaveClan,
		SendClanChat,
		SendClanPing,
		UpdatePosition,
		CancelApplication,
		UpdateClanProfile,
		RequestDirectory,
		RenameClan,
		RequestHud
	}
	internal enum ClanOperationResultCode
	{
		None,
		Success,
		Unchanged,
		InvalidName,
		IdentityUnavailable,
		Unauthorized,
		ClanChanged,
		NameTaken,
		RateLimited,
		Unavailable,
		Failed,
		SnapshotRateLimited,
		IdentityRejected
	}
	internal enum ClanResponseType
	{
		Snapshot,
		Chat,
		MapPing,
		PositionUpdate,
		Directory,
		DirectoryInvalidated,
		Hud
	}
	internal enum ClanDirectoryPlayerState
	{
		None,
		Clan,
		Pending,
		Invited
	}
	internal enum ClanValidationField
	{
		None,
		ClanName,
		ClanDescription,
		ClanEmblemKey
	}
	internal enum ClanValidationError
	{
		Required,
		TooLong,
		RichText,
		ControlCharacters,
		InvalidUnicode,
		InvalidCharacters,
		TrailingSeparator,
		MissingLetterOrNumber,
		InvalidStartOrEnd
	}
	internal static class ClanDataRules
	{
		public const int MaxPlatformIdLength = 128;

		public const int MaxPlayerKeyLength = 160;

		public const int MaxPlayerNameLength = 64;

		public const int ClanIdLength = 32;

		public const int MaxClanNameLength = 40;

		public const int MaxClanDescriptionLength = 160;

		public const int MaxClanEmblemKeyLength = 32;

		public const int MaxChatMessageLength = 400;

		public const int MaxStatusLength = 1024;

		public const int MaxInviteIdLength = 64;

		public const int MaxClans = 1024;

		public const int MaxMembersPerClan = 512;

		public const int MaxApplicationsPerClan = 512;

		public const int MaxInvites = 4096;

		public const int MaxDirectoryPlayers = 1024;

		public const int MaxHudPlayers = 10;

		public const float MaxHudHealth = 1000000f;

		private static readonly object ValidationErrorDataKey = new object();

		public static string RequireText(string? value, int maxLength, string fieldName, bool allowEmpty = true, bool allowLineBreaks = false, ClanValidationField validationField = ClanValidationField.None)
		{
			string text = (value ?? "").Trim();
			if (!allowEmpty && text.Length == 0)
			{
				throw ValidationFailure(validationField, ClanValidationError.Required, fieldName + " is required.");
			}
			if (text.Length > maxLength)
			{
				throw ValidationFailure(validationField, ClanValidationError.TooLong, $"{fieldName} exceeds {maxLength} characters.");
			}
			if (text.IndexOf('<') >= 0 || text.IndexOf('>') >= 0)
			{
				throw ValidationFailure(validationField, ClanValidationError.RichText, fieldName + " cannot contain rich-text tags.");
			}
			string text2 = text;
			foreach (char c in text2)
			{
				if (char.IsControl(c) && (!allowLineBreaks || (c != '\r' && c != '\n' && c != '\t')))
				{
					throw ValidationFailure(validationField, ClanValidationError.ControlCharacters, fieldName + " contains unsupported control characters.");
				}
			}
			return text;
		}

		public static string RequireClanDescription(string? value, string fieldName = "clan description")
		{
			return RequireText(value, 160, fieldName, allowEmpty: true, allowLineBreaks: false, ClanValidationField.ClanDescription);
		}

		public static string ReadClanDescription(ZPackage package, string fieldName = "clan description")
		{
			return RequireClanDescription(package.ReadString(), fieldName);
		}

		public static void WriteClanDescription(ZPackage package, string? value, string fieldName = "clan description")
		{
			package.Write(RequireClanDescription(value, fieldName));
		}

		public static string RequireClanName(string? value, string fieldName = "clan name")
		{
			string text;
			try
			{
				text = (value ?? "").Normalize(NormalizationForm.FormC);
			}
			catch (ArgumentException innerException)
			{
				throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.InvalidUnicode, fieldName + " contains invalid Unicode data.", innerException);
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			bool flag = false;
			string text2 = text;
			foreach (char c in text2)
			{
				if (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.SpaceSeparator)
				{
					if (stringBuilder.Length > 0 && !flag)
					{
						stringBuilder.Append(' ');
					}
					flag = true;
				}
				else
				{
					stringBuilder.Append(c);
					flag = false;
				}
			}
			string text3 = stringBuilder.ToString().Trim(new char[1] { ' ' });
			if (text3.Length == 0)
			{
				throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.Required, fieldName + " is required.");
			}
			int num = 0;
			bool flag2 = false;
			bool flag3 = true;
			bool flag4 = false;
			int num2;
			for (int j = 0; j < text3.Length; j += num2)
			{
				char c2 = text3[j];
				num2 = 1;
				if (char.IsHighSurrogate(c2))
				{
					if (j + 1 >= text3.Length || !char.IsLowSurrogate(text3[j + 1]))
					{
						throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.InvalidUnicode, fieldName + " contains invalid Unicode data.");
					}
					num2 = 2;
				}
				else if (char.IsLowSurrogate(c2))
				{
					throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.InvalidUnicode, fieldName + " contains invalid Unicode data.");
				}
				UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(text3, j);
				bool flag5 = (uint)unicodeCategory <= 4u;
				bool flag6 = flag5;
				flag5 = (uint)(unicodeCategory - 8) <= 2u;
				bool flag7 = flag5;
				flag5 = (uint)(unicodeCategory - 5) <= 2u;
				bool flag8 = flag5;
				bool flag9 = num2 == 1 && (c2 == ' ' || c2 == '-' || c2 == '_' || c2 == '·');
				if (flag6 || flag7)
				{
					flag2 = true;
					flag3 = false;
					flag4 = true;
				}
				else if (flag8 && flag4)
				{
					flag3 = false;
				}
				else
				{
					if (!flag9 || flag3)
					{
						throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.InvalidCharacters, fieldName + " can contain only letters, numbers, marks, spaces, '-', '_', or '·'.");
					}
					flag3 = true;
					flag4 = false;
				}
				num++;
				if (num > 40)
				{
					throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.TooLong, $"{fieldName} exceeds {40} Unicode characters.");
				}
			}
			if (flag3)
			{
				throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.TrailingSeparator, fieldName + " cannot end with a separator.");
			}
			if (!flag2)
			{
				throw ValidationFailure(ClanValidationField.ClanName, ClanValidationError.MissingLetterOrNumber, fieldName + " must contain a letter or number.");
			}
			return text3;
		}

		public static string RequireOptionalClanName(string? value, string fieldName = "clan name")
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				return RequireClanName(value, fieldName);
			}
			return "";
		}

		public static string ReadClanName(ZPackage package, string fieldName = "clan name")
		{
			return RequireClanName(package.ReadString(), fieldName);
		}

		public static string ReadOptionalClanName(ZPackage package, string fieldName = "clan name")
		{
			return RequireOptionalClanName(package.ReadString(), fieldName);
		}

		public static void WriteClanName(ZPackage package, string? value, string fieldName = "clan name")
		{
			package.Write(RequireClanName(value, fieldName));
		}

		public static void WriteOptionalClanName(ZPackage package, string? value, string fieldName = "clan name")
		{
			package.Write(RequireOptionalClanName(value, fieldName));
		}

		public static string ReadText(ZPackage package, int maxLength, string fieldName, bool allowEmpty = true, bool allowLineBreaks = false)
		{
			return RequireText(package.ReadString(), maxLength, fieldName, allowEmpty, allowLineBreaks);
		}

		public static void WriteText(ZPackage package, string? value, int maxLength, string fieldName, bool allowEmpty = true, bool allowLineBreaks = false)
		{
			package.Write(RequireText(value, maxLength, fieldName, allowEmpty, allowLineBreaks));
		}

		public static string RequirePlayerKey(string? value, string fieldName = "player key")
		{
			string text = RequireText(value, 160, fieldName, allowEmpty: false);
			if (!TryParsePlayerKey(text, out string _, out long _))
			{
				throw new InvalidDataException(fieldName + " is invalid.");
			}
			return text;
		}

		public static string ReadPlayerKey(ZPackage package, string fieldName = "player key")
		{
			return RequirePlayerKey(package.ReadString(), fieldName);
		}

		public static void WritePlayerKey(ZPackage package, string? value, string fieldName = "player key")
		{
			package.Write(RequirePlayerKey(value, fieldName));
		}

		public static string RequirePlatformId(string? value, string fieldName = "platform id")
		{
			return RequireText(value, 128, fieldName, allowEmpty: false);
		}

		public static long RequireCharacterPlayerId(long value, string fieldName = "character player id")
		{
			if (value == 0L)
			{
				throw new InvalidDataException(fieldName + " must be non-zero.");
			}
			return value;
		}

		public static string BuildPlayerKey(string? platformId, long playerId)
		{
			string text = RequirePlatformId(platformId);
			long num = RequireCharacterPlayerId(playerId);
			return text.Length.ToString(CultureInfo.InvariantCulture) + ":" + text + ":" + num.ToString(CultureInfo.InvariantCulture);
		}

		public static bool TryParsePlayerKey(string? value, out string platformId, out long playerId)
		{
			platformId = "";
			playerId = 0L;
			string text = value ?? "";
			int num = text.IndexOf(':');
			if (num <= 0 || !int.TryParse(text.Substring(0, num), NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result <= 0 || result > 128)
			{
				return false;
			}
			int num2 = num + 1;
			int num3 = num2 + result;
			if (num3 >= text.Length || text[num3] != ':')
			{
				return false;
			}
			string text2 = text.Substring(num2, result);
			if (!long.TryParse(text.Substring(num3 + 1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) || result2 == 0L || NormalizePlatformId(text2).Length == 0)
			{
				return false;
			}
			string x;
			try
			{
				x = BuildPlayerKey(text2, result2);
			}
			catch (InvalidDataException)
			{
				return false;
			}
			if (!StringComparer.Ordinal.Equals(x, text))
			{
				return false;
			}
			platformId = text2;
			playerId = result2;
			return true;
		}

		public static string ReadPlayerName(ZPackage package, string fieldName = "player name")
		{
			return ReadText(package, 64, fieldName);
		}

		public static void WritePlayerName(ZPackage package, string? value, string fieldName = "player name")
		{
			WriteText(package, value, 64, fieldName);
		}

		public static string RequireClanId(string? value, string fieldName = "clan id")
		{
			string text = RequireText(value, 32, fieldName, allowEmpty: false);
			if (text.Length != 32 || !Guid.TryParseExact(text, "N", out var result) || result == Guid.Empty || !StringComparer.Ordinal.Equals(result.ToString("N"), text))
			{
				throw new InvalidDataException(fieldName + " is invalid.");
			}
			return text;
		}

		public static string RequireOptionalClanId(string? value, string fieldName = "clan id")
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				return RequireClanId(value, fieldName);
			}
			return "";
		}

		public static string ReadClanId(ZPackage package, string fieldName = "clan id")
		{
			return RequireClanId(package.ReadString(), fieldName);
		}

		public static string ReadOptionalClanId(ZPackage package, string fieldName = "clan id")
		{
			return RequireOptionalClanId(package.ReadString(), fieldName);
		}

		public static void WriteClanId(ZPackage package, string? value, string fieldName = "clan id")
		{
			package.Write(RequireClanId(value, fieldName));
		}

		public static void WriteOptionalClanId(ZPackage package, string? value, string fieldName = "clan id")
		{
			package.Write(RequireOptionalClanId(value, fieldName));
		}

		public static string RequireClanEmblemKey(string? value, string fieldName = "clan emblem key")
		{
			string text = RequireText(value, 32, fieldName, allowEmpty: true, allowLineBreaks: false, ClanValidationField.ClanEmblemKey);
			if (text.Length == 0)
			{
				return text;
			}
			if (!IsLowerAsciiLetterOrDigit(text[0]) || !IsLowerAsciiLetterOrDigit(text[text.Length - 1]))
			{
				throw ValidationFailure(ClanValidationField.ClanEmblemKey, ClanValidationError.InvalidStartOrEnd, fieldName + " must start and end with a lowercase ASCII letter or digit.");
			}
			string text2 = text;
			foreach (char c in text2)
			{
				if (!IsLowerAsciiLetterOrDigit(c) && c != '_' && c != '-')
				{
					throw ValidationFailure(ClanValidationField.ClanEmblemKey, ClanValidationError.InvalidCharacters, fieldName + " can contain only lowercase ASCII letters, digits, '_' or '-'.");
				}
			}
			return text;
		}

		public static string ReadClanEmblemKey(ZPackage package, string fieldName = "clan emblem key")
		{
			return RequireClanEmblemKey(package.ReadString(), fieldName);
		}

		public static void WriteClanEmblemKey(ZPackage package, string? value, string fieldName = "clan emblem key")
		{
			package.Write(RequireClanEmblemKey(value, fieldName));
		}

		public static T RequireEnum<T>(T value, string fieldName) where T : struct, Enum
		{
			if (!Enum.IsDefined(typeof(T), value))
			{
				throw new InvalidDataException($"{fieldName} has an unknown value ({Convert.ToInt32(value)}).");
			}
			return value;
		}

		public static T ReadEnum<T>(ZPackage package, string fieldName) where T : struct, Enum
		{
			int num = package.ReadInt();
			if (!Enum.IsDefined(typeof(T), num))
			{
				throw new InvalidDataException($"{fieldName} has an unknown value ({num}).");
			}
			return (T)Enum.ToObject(typeof(T), num);
		}

		public static ClanRole RequireAssignableRole(ClanRole role, string fieldName)
		{
			RequireEnum(role, fieldName);
			if (role != ClanRole.Officer && role != ClanRole.Member && role != ClanRole.Guest)
			{
				throw new InvalidDataException(fieldName + " must be Officer, Member, or Guest.");
			}
			return role;
		}

		public static ClanRole ReadAssignableRole(ZPackage package, string fieldName)
		{
			return RequireAssignableRole(ReadEnum<ClanRole>(package, fieldName), fieldName);
		}

		public static int GetRolePower(ClanRole role)
		{
			return RequireEnum(role, "clan role") switch
			{
				ClanRole.Leader => 3, 
				ClanRole.Officer => 2, 
				ClanRole.Member => 1, 
				ClanRole.Guest => 0, 
				_ => throw new InvalidDataException("Clan role is unsupported."), 
			};
		}

		public static int RequireRange(int value, int minimum, int maximum, string fieldName)
		{
			if (value < minimum || value > maximum)
			{
				throw new InvalidDataException(fieldName + " is outside the supported range.");
			}
			return value;
		}

		public static long RequireRequestId(long value, string fieldName = "request id")
		{
			if (value <= 0)
			{
				throw new InvalidDataException(fieldName + " must be positive.");
			}
			return value;
		}

		public static long RequireOptionalRequestId(long value, string fieldName = "response request id")
		{
			if (value < 0)
			{
				throw new InvalidDataException(fieldName + " cannot be negative.");
			}
			return value;
		}

		public static int ReadCount(ZPackage package, int maximum, string fieldName)
		{
			return RequireRange(package.ReadInt(), 0, maximum, fieldName + " count");
		}

		public static void RequireCount(int count, int maximum, string fieldName)
		{
			RequireRange(count, 0, maximum, fieldName + " count");
		}

		public static Vector3 ReadFiniteVector(ZPackage package, string fieldName)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = package.ReadVector3();
			RequireFiniteVector(val, fieldName);
			return val;
		}

		public static void RequireFiniteVector(Vector3 value, string fieldName)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (!IsFinite(value.x) || !IsFinite(value.y) || !IsFinite(value.z))
			{
				throw new InvalidDataException(fieldName + " must contain finite coordinates.");
			}
		}

		public static string NormalizePlayerKey(string? value)
		{
			try
			{
				return RequirePlayerKey(value);
			}
			catch (InvalidDataException)
			{
				return "";
			}
		}

		public static string NormalizePlatformId(string? value)
		{
			try
			{
				return RequirePlatformId(value);
			}
			catch (InvalidDataException)
			{
				return "";
			}
		}

		public static string SanitizePlayerName(string? value)
		{
			string text = (value ?? "").Replace("<", " ").Replace(">", " ");
			StringBuilder stringBuilder = new StringBuilder(Math.Min(text.Length, 64));
			for (int i = 0; i < text.Length; i++)
			{
				if (stringBuilder.Length >= 64)
				{
					break;
				}
				char c = text[i];
				if (char.IsControl(c))
				{
					continue;
				}
				bool flag = char.IsHighSurrogate(c) && i + 1 < text.Length && char.IsLowSurrogate(text[i + 1]);
				if (!char.IsSurrogate(c) || flag)
				{
					int num = ((!flag) ? 1 : 2);
					if (stringBuilder.Length > 64 - num)
					{
						break;
					}
					stringBuilder.Append(c);
					if (flag)
					{
						stringBuilder.Append(text[++i]);
					}
				}
			}
			return stringBuilder.ToString().Trim();
		}

		public static bool TryGetValidationError(InvalidDataException exception, out ClanValidationField field, out ClanValidationError error)
		{
			if (exception.Data[ValidationErrorDataKey] is (ClanValidationField, ClanValidationError) tuple)
			{
				(field, error) = tuple;
				return true;
			}
			field = ClanValidationField.None;
			error = ClanValidationError.Required;
			return false;
		}

		private static InvalidDataException ValidationFailure(ClanValidationField field, ClanValidationError error, string message, Exception? innerException = null)
		{
			InvalidDataException ex = new InvalidDataException(message, innerException);
			if (field != ClanValidationField.None)
			{
				ex.Data[ValidationErrorDataKey] = (field, error);
			}
			return ex;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static bool IsLowerAsciiLetterOrDigit(char value)
		{
			switch (value)
			{
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case 'a':
			case 'b':
			case 'c':
			case 'd':
			case 'e':
			case 'f':
			case 'g':
			case 'h':
			case 'i':
			case 'j':
			case 'k':
			case 'l':
			case 'm':
			case 'n':
			case 'o':
			case 'p':
			case 'q':
			case 'r':
			case 's':
			case 't':
			case 'u':
			case 'v':
			case 'w':
			case 'x':
			case 'y':
			case 'z':
				return true;
			default:
				return false;
			}
		}
	}
	internal readonly struct ClanPlayerRef : IEquatable<ClanPlayerRef>
	{
		public readonly string Id;

		public readonly string PlatformId;

		public readonly long CharacterPlayerId;

		public readonly string Name;

		public bool IsValid
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(PlatformId) && CharacterPlayerId != 0L)
				{
					return !string.IsNullOrWhiteSpace(Id);
				}
				return false;
			}
		}

		public ClanPlayerRef(string? platformId, long playerId, string? name)
		{
			PlatformId = ClanDataRules.NormalizePlatformId(platformId);
			CharacterPlayerId = playerId;
			Name = ClanDataRules.SanitizePlayerName(name);
			Id = ((PlatformId.Length == 0 || CharacterPlayerId == 0L) ? "" : ClanDataRules.BuildPlayerKey(PlatformId, CharacterPlayerId));
		}

		public static ClanPlayerRef Local()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			IDistributionPlatform distributionPlatform = PlatformManager.DistributionPlatform;
			string platformId = ((((distributionPlatform != null) ? distributionPlatform.LocalUser : null) != null) ? ((object)((IUser)distributionPlatform.LocalUser).PlatformUserID/*cast due to .constrained prefix*/).ToString() : "");
			Player localPlayer = Player.m_localPlayer;
			long num2;
			if (localPlayer == null)
			{
				Game instance = Game.instance;
				long? obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					PlayerProfile playerProfile = instance.GetPlayerProfile();
					obj = ((playerProfile != null) ? new long?(playerProfile.GetPlayerID()) : ((long?)null));
				}
				long? num = obj;
				num2 = num.GetValueOrDefault();
			}
			else
			{
				num2 = localPlayer.GetPlayerID();
			}
			long playerId = num2;
			Game instance2 = Game.instance;
			object obj2;
			if (instance2 == null)
			{
				obj2 = null;
			}
			else
			{
				PlayerProfile playerProfile2 = instance2.GetPlayerProfile();
				obj2 = ((playerProfile2 != null) ? playerProfile2.GetName() : null);
			}
			if (obj2 == null)
			{
				obj2 = "Unknown";
			}
			string name = (string)obj2;
			return new ClanPlayerRef(platformId, playerId, name);
		}

		public void Write(ZPackage package)
		{
			ClanDataRules.WriteText(package, PlatformId, 128, "platform id", allowEmpty: false);
			package.Write(ClanDataRules.RequireCharacterPlayerId(CharacterPlayerId));
			ClanDataRules.WritePlayerName(package, Name);
		}

		public static ClanPlayerRef Read(ZPackage package)
		{
			return new ClanPlayerRef(ClanDataRules.ReadText(package, 128, "platform id", allowEmpty: false), ClanDataRules.RequireCharacterPlayerId(package.ReadLong()), ClanDataRules.ReadPlayerName(package));
		}

		public bool Equals(ClanPlayerRef other)
		{
			return StringComparer.Ordinal.Equals(Id, other.Id);
		}

		public override bool Equals(object? obj)
		{
			if (obj is ClanPlayerRef other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			if (Id != null)
			{
				return StringComparer.Ordinal.GetHashCode(Id);
			}
			return 0;
		}

		public override string ToString()
		{
			string text = (IsValid ? $"{PlatformId}/{CharacterPlayerId}" : "");
			if (!string.IsNullOrWhiteSpace(Name))
			{
				return Name + " (" + text + ")";
			}
			return text;
		}

		public static bool operator ==(ClanPlayerRef left, ClanPlayerRef right)
		{
			return left.Equals(right);
		}

		public static bool operator !=(ClanPlayerRef left, ClanPlayerRef right)
		{
			return !left.Equals(right);
		}
	}
	internal sealed class ClanMember
	{
		public ClanPlayerRef Player;

		public ClanRole Role = ClanRole.Member;

		public float LastClanChatTime = float.NegativeInfinity;

		public float LastClanPingTime = float.NegativeInfinity;
	}
	internal sealed class ClanInvite
	{
		public string InviteId = Guid.NewGuid().ToString("N");

		public string ClanId = "";

		public string FromName = "";

		public ClanPlayerRef Target;
	}
	internal sealed class ClanState
	{
		public long CreationOrder;

		public string Name = "";

		public string Description = "";

		public string EmblemKey = "";

		public readonly Dictionary<string, ClanMember> Members = new Dictionary<string, ClanMember>(StringComparer.Ordinal);

		public readonly Dictionary<string, ClanPlayerRef> Applications = new Dictionary<string, ClanPlayerRef>(StringComparer.Ordinal);

		public string ClanId { get; }

		public ClanState(string clanId)
		{
			ClanId = ClanDataRules.RequireClanId(clanId);
		}

		public bool IsLeader(ClanPlayerRef player)
		{
			if (player.IsValid && Members.TryGetValue(player.Id, out ClanMember value))
			{
				return value.Role == ClanRole.Leader;
			}
			return false;
		}
	}
	internal sealed class ClanRequest
	{
		public ClanRequestType Type;

		public string ClanId = "";

		public string ClanName = "";

		public string TargetId = "";

		public string InviteId = "";

		public string Message = "";

		public string Description = "";

		public string EmblemKey = "";

		public ClanRole Role = ClanRole.Member;

		public long RequestId;

		public long HudSelectionRevision;

		public long HudStateRevision;

		public Vector3 Position = Vector3.zero;

		public static ClanRequest Simple(ClanRequestType type)
		{
			return new ClanRequest
			{
				Type = type
			};
		}

		public void Write(ZPackage package)
		{
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			ClanDataRules.RequireEnum(Type, "clan request type");
			package.Write((int)Type);
			switch (Type)
			{
			case ClanRequestType.RequestSnapshot:
			case ClanRequestType.CancelApplication:
				break;
			case ClanRequestType.LeaveClan:
				ClanDataRules.WriteClanId(package, ClanId);
				break;
			case ClanRequestType.RequestHud:
				ClanDataRules.WriteClanId(package, ClanId);
				package.Write(ClanDataRules.RequireOptionalRequestId(HudSelectionRevision, "HUD selection revision"));
				package.Write(ClanDataRules.RequireOptionalRequestId(HudStateRevision, "HUD state revision"));
				break;
			case ClanRequestType.RequestDirectory:
				package.Write(ClanDataRules.RequireRequestId(RequestId));
				break;
			case ClanRequestType.CreateClan:
				package.Write(ClanDataRules.RequireRequestId(RequestId));
				ClanDataRules.WriteClanName(package, ClanName);
				ClanDataRules.WriteClanDescription(package, Description);
				ClanDataRules.WriteClanEmblemKey(package, EmblemKey);
				break;
			case ClanRequestType.Invite:
			case ClanRequestType.AcceptApplication:
			case ClanRequestType.RejectApplication:
			case ClanRequestType.KickPlayer:
			case ClanRequestType.TransferLeadership:
				ClanDataRules.WriteClanId(package, ClanId);
				ClanDataRules.WritePlayerKey(package, TargetId, "target player key");
				break;
			case ClanRequestType.Apply:
				ClanDataRules.WriteClanId(package, ClanId);
				break;
			case ClanRequestType.AcceptInvite:
			case ClanRequestType.DeclineInvite:
				ClanDataRules.WriteText(package, InviteId, 64, "invite id", allowEmpty: false);
				break;
			case ClanRequestType.SetRole:
				ClanDataRules.WriteClanId(package, ClanId);
				ClanDataRules.WritePlayerKey(package, TargetId, "target player key");
				package.Write((int)ClanDataRules.RequireAssignableRole(Role, "clan role"));
				break;
			case ClanRequestType.UpdateClanProfile:
				package.Write(ClanDataRules.RequireRequestId(RequestId));
				ClanDataRules.WriteClanId(package, ClanId);
				ClanDataRules.WriteClanName(package, ClanName);
				ClanDataRules.WriteClanDescription(package, Description);
				ClanDataRules.WriteClanEmblemKey(package, EmblemKey);
				break;
			case ClanRequestType.RenameClan:
				package.Write(ClanDataRules.RequireRequestId(RequestId));
				ClanDataRules.WriteClanId(package, ClanId);
				ClanDataRules.WriteClanName(package, ClanName);
				break;
			case ClanRequestType.SendClanChat:
				ClanDataRules.WriteClanId(package, ClanId);
				ClanDataRules.WriteText(package, Message, 400, "clan chat message", allowEmpty: false);
				break;
			case ClanRequestType.SendClanPing:
			case ClanRequestType.UpdatePosition:
				ClanDataRules.WriteClanId(package, ClanId);
				ClanDataRules.RequireFiniteVector(Position, "position");
				package.Write(Position);
				break;
			default:
				throw new InvalidDataException($"Unknown clan request type ({(int)Type}).");
			}
		}

		public static ClanRequest Read(ZPackage package)
		{
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			ClanRequest clanRequest = new ClanRequest
			{
				Type = ClanDataRules.ReadEnum<ClanRequestType>(package, "clan request type")
			};
			switch (clanRequest.Type)
			{
			case ClanRequestType.LeaveClan:
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				break;
			case ClanRequestType.RequestHud:
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				clanRequest.HudSelectionRevision = ClanDataRules.RequireOptionalRequestId(package.ReadLong(), "HUD selection revision");
				clanRequest.HudStateRevision = ClanDataRules.RequireOptionalRequestId(package.ReadLong(), "HUD state revision");
				break;
			case ClanRequestType.RequestDirectory:
				clanRequest.RequestId = ClanDataRules.RequireRequestId(package.ReadLong());
				break;
			case ClanRequestType.CreateClan:
				clanRequest.RequestId = ClanDataRules.RequireRequestId(package.ReadLong());
				clanRequest.ClanName = ClanDataRules.ReadClanName(package);
				clanRequest.Description = ClanDataRules.ReadClanDescription(package);
				clanRequest.EmblemKey = ClanDataRules.ReadClanEmblemKey(package);
				break;
			case ClanRequestType.Invite:
			case ClanRequestType.AcceptApplication:
			case ClanRequestType.RejectApplication:
			case ClanRequestType.KickPlayer:
			case ClanRequestType.TransferLeadership:
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				clanRequest.TargetId = ClanDataRules.ReadPlayerKey(package, "target player key");
				break;
			case ClanRequestType.Apply:
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				break;
			case ClanRequestType.AcceptInvite:
			case ClanRequestType.DeclineInvite:
				clanRequest.InviteId = ClanDataRules.ReadText(package, 64, "invite id", allowEmpty: false);
				break;
			case ClanRequestType.SetRole:
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				clanRequest.TargetId = ClanDataRules.ReadPlayerKey(package, "target player key");
				clanRequest.Role = ClanDataRules.ReadAssignableRole(package, "clan role");
				break;
			case ClanRequestType.UpdateClanProfile:
				clanRequest.RequestId = ClanDataRules.RequireRequestId(package.ReadLong());
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				clanRequest.ClanName = ClanDataRules.ReadClanName(package);
				clanRequest.Description = ClanDataRules.ReadClanDescription(package);
				clanRequest.EmblemKey = ClanDataRules.ReadClanEmblemKey(package);
				break;
			case ClanRequestType.RenameClan:
				clanRequest.RequestId = ClanDataRules.RequireRequestId(package.ReadLong());
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				clanRequest.ClanName = ClanDataRules.ReadClanName(package);
				break;
			case ClanRequestType.SendClanChat:
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				clanRequest.Message = ClanDataRules.ReadText(package, 400, "clan chat message", allowEmpty: false);
				break;
			case ClanRequestType.SendClanPing:
			case ClanRequestType.UpdatePosition:
				clanRequest.ClanId = ClanDataRules.ReadClanId(package);
				clanRequest.Position = ClanDataRules.ReadFiniteVector(package, "position");
				break;
			}
			if (package.GetPos() != package.Size())
			{
				throw new InvalidDataException("Clan request contains unexpected trailing data.");
			}
			return clanRequest;
		}
	}
	internal sealed class ClanPlayerSummary
	{
		public ClanPlayerRef Player;

		public ClanRole Role = ClanRole.Member;

		public bool IsSelf;

		public bool IsOnline;

		public string Id => Player.Id;

		public string Name => Player.Name;

		public void Write(ZPackage package)
		{
			if (!Player.IsValid)
			{
				throw new InvalidDataException("roster player identity is invalid.");
			}
			Player.Write(package);
			package.Write((int)ClanDataRules.RequireEnum(Role, "clan role"));
			package.Write(IsSelf);
			package.Write(IsOnline);
		}

		public static ClanPlayerSummary Read(ZPackage package)
		{
			return new ClanPlayerSummary
			{
				Player = ClanPlayerRef.Read(package),
				Role = ClanDataRules.ReadEnum<ClanRole>(package, "clan role"),
				IsSelf = package.ReadBool(),
				IsOnline = package.ReadBool()
			};
		}
	}
	internal sealed class ClanHudPlayerSummary
	{
		public string PlayerId = "";

		public bool HasHealth;

		public float CurrentHealth;

		public float MaxHealth;

		public void Write(ZPackage package)
		{
			ClanDataRules.WritePlayerKey(package, PlayerId, "HUD player key");
			package.Write(HasHealth);
			if (HasHealth)
			{
				ValidateHealth(CurrentHealth, MaxHealth);
				package.Write(CurrentHealth);
				package.Write(MaxHealth);
			}
		}

		public static ClanHudPlayerSummary Read(ZPackage package)
		{
			ClanHudPlayerSummary clanHudPlayerSummary = new ClanHudPlayerSummary
			{
				PlayerId = ClanDataRules.ReadPlayerKey(package, "HUD player key"),
				HasHealth = package.ReadBool()
			};
			if (clanHudPlayerSummary.HasHealth)
			{
				clanHudPlayerSummary.CurrentHealth = package.ReadSingle();
				clanHudPlayerSummary.MaxHealth = package.ReadSingle();
				ValidateHealth(clanHudPlayerSummary.CurrentHealth, clanHudPlayerSummary.MaxHealth);
			}
			return clanHudPlayerSummary;
		}

		private static void ValidateHealth(float currentHealth, float maxHealth)
		{
			if (float.IsNaN(currentHealth) || float.IsInfinity(currentHealth) || float.IsNaN(maxHealth) || float.IsInfinity(maxHealth) || maxHealth <= 0f || maxHealth > 1000000f || currentHealth < 0f || currentHealth > maxHealth)
			{
				throw new InvalidDataException("HUD health is outside the supported range.");
			}
		}
	}
	internal sealed class ClanHudSnapshot
	{
		public string ClanId = "";

		public long SelectionRevision;

		public long StateRevision;

		public bool ReplaceSelection;

		public readonly List<ClanHudPlayerSummary> Players = new List<ClanHudPlayerSummary>();

		public void Write(ZPackage package)
		{
			ClanDataRules.WriteOptionalClanId(package, ClanId, "HUD clan id");
			package.Write(ClanDataRules.RequireOptionalRequestId(SelectionRevision, "HUD selection revision"));
			package.Write(ClanDataRules.RequireOptionalRequestId(StateRevision, "HUD state revision"));
			package.Write(ReplaceSelection);
			ClanDataRules.RequireCount(Players.Count, 10, "HUD player");
			package.Write(Players.Count);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (ClanHudPlayerSummary player in Players)
			{
				if (!hashSet.Add(player.PlayerId))
				{
					throw new InvalidDataException("HUD snapshot contains a duplicate player.");
				}
				player.Write(package);
			}
		}

		public static ClanHudSnapshot Read(ZPackage package)
		{
			ClanHudSnapshot clanHudSnapshot = new ClanHudSnapshot
			{
				ClanId = ClanDataRules.ReadOptionalClanId(package, "HUD clan id"),
				SelectionRevision = ClanDataRules.RequireOptionalRequestId(package.ReadLong(), "HUD selection revision"),
				StateRevision = ClanDataRules.RequireOptionalRequestId(package.ReadLong(), "HUD state revision"),
				ReplaceSelection = package.ReadBool()
			};
			int num = ClanDataRules.ReadCount(package, 10, "HUD player");
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			for (int i = 0; i < num; i++)
			{
				ClanHudPlayerSummary clanHudPlayerSummary = ClanHudPlayerSummary.Read(package);
				if (!hashSet.Add(clanHudPlayerSummary.PlayerId))
				{
					throw new InvalidDataException("HUD snapshot contains a duplicate player.");
				}
				clanHudSnapshot.Players.Add(clanHudPlayerSummary);
			}
			return clanHudSnapshot;
		}
	}
	internal sealed class ClanPublicSummary
	{
		public string ClanId = "";

		public string Name = "";

		public string Description = "";

		public string EmblemKey = "";

		public string LeaderName = "";

		public int MemberCount;

		public void Write(ZPackage package)
		{
			ClanDataRules.WriteClanId(package, ClanId);
			ClanDataRules.WriteClanName(package, Name);
			ClanDataRules.WriteClanDescription(package, Description);
			ClanDataRules.WriteClanEmblemKey(package, EmblemKey);
			ClanDataRules.WritePlayerName(package, LeaderName, "clan leader name");
			package.Write(ClanDataRules.RequireRange(MemberCount, 0, 512, "clan member count"));
		}

		public static ClanPublicSummary Read(ZPackage package)
		{
			return new ClanPublicSummary
			{
				ClanId = ClanDataRules.ReadClanId(package),
				Name = ClanDataRules.ReadClanName(package),
				Description = ClanDataRules.ReadClanDescription(package),
				EmblemKey = ClanDataRules.ReadClanEmblemKey(package),
				LeaderName = ClanDataRules.ReadPlayerName(package, "clan leader name"),
				MemberCount = ClanDataRules.RequireRange(package.ReadInt(), 0, 512, "clan member count")
			};
		}
	}
	internal sealed class ClanApplicationSummary
	{
		public string PlayerId = "";

		public string PlayerName = "";

		public void Write(ZPackage package)
		{
			ClanDataRules.WritePlayerKey(package, PlayerId, "applicant key");
			ClanDataRules.WritePlayerName(package, PlayerName, "applicant name");
		}

		public static ClanApplicationSummary Read(ZPackage package)
		{
			return new ClanApplicationSummary
			{
				PlayerId = ClanDataRules.ReadPlayerKey(package, "applicant key"),
				PlayerName = ClanDataRules.ReadPlayerName(package, "applicant name")
			};
		}
	}
	internal sealed class ClanInviteSummary
	{
		public string InviteId = "";

		public string ClanId = "";

		public string ClanName = "";

		public string FromName = "";

		public void Write(ZPackage package)
		{
			ClanDataRules.WriteText(package, InviteId, 64, "invite id", allowEmpty: false);
			ClanDataRules.WriteClanId(package, ClanId);
			ClanDataRules.WriteClanName(package, ClanName);
			ClanDataRules.WritePlayerName(package, FromName, "inviter name");
		}

		public static ClanInviteSummary Read(ZPackage package)
		{
			return new ClanInviteSummary
			{
				InviteId = ClanDataRules.ReadText(package, 64, "invite id", allowEmpty: false),
				ClanId = ClanDataRules.ReadClanId(package),
				ClanName = ClanDataRules.ReadClanName(package),
				FromName = ClanDataRules.ReadPlayerName(package, "inviter name")
			};
		}
	}
	internal sealed class ClanClientSnapshot
	{
		public string Status = "";

		public long ResponseRequestId;

		public ClanOperationResultCode ResponseResultCode;

		public string ClanId = "";

		public string ClanName = "";

		public string ClanDescription = "";

		public string ClanEmblemKey = "";

		public ClanRole SelfRole = ClanRole.Member;

		public string PrimaryClanId = "";

		public string PrimaryClanName = "";

		public ClanRole PrimaryRole = ClanRole.Member;

		public string GuestClanId = "";

		public string GuestClanName = "";

		public readonly List<ClanPlayerSummary> Roster = new List<ClanPlayerSummary>();

		public readonly List<ClanApplicationSummary> Applications = new List<ClanApplicationSummary>();

		public ClanInviteSummary? Invite;

		public string OwnApplicationClanId = "";

		public string OwnApplicationClanName = "";

		public bool HasClan => !string.IsNullOrWhiteSpace(ClanId);

		public bool IsLeader
		{
			get
			{
				if (HasClan)
				{
					return SelfRole == ClanRole.Leader;
				}
				return false;
			}
		}

		public bool CanModerate
		{
			get
			{
				bool flag = HasClan;
				if (flag)
				{
					ClanRole selfRole = SelfRole;
					bool flag2 = (uint)selfRole <= 1u;
					flag = flag2;
				}
				return flag;
			}
		}

		public bool HasPrimaryClan => !string.IsNullOrWhiteSpace(PrimaryClanId);

		public bool HasGuestClan => !string.IsNullOrWhiteSpace(GuestClanId);

		public bool HasAnyClan
		{
			get
			{
				if (!HasPrimaryClan)
				{
					return HasGuestClan;
				}
				return true;
			}
		}

		public bool HasOwnApplication => !string.IsNullOrWhiteSpace(OwnApplicationClanId);

		public bool IsConnectedToClan(string clanId)
		{
			if (!string.IsNullOrWhiteSpace(clanId))
			{
				if (!StringComparer.Ordinal.Equals(PrimaryClanId, clanId))
				{
					return StringComparer.Ordinal.Equals(GuestClanId, clanId);
				}
				return true;
			}
			return false;
		}

		public ClanRole? GetRoleForClan(string clanId)
		{
			if (!string.IsNullOrWhiteSpace(clanId) && StringComparer.Ordinal.Equals(PrimaryClanId, clanId))
			{
				return PrimaryRole;
			}
			if (!string.IsNullOrWhiteSpace(clanId) && StringComparer.Ordinal.Equals(GuestClanId, clanId))
			{
				return ClanRole.Guest;
			}
			return null;
		}

		public void Write(ZPackage package)
		{
			ClanDataRules.RequireCount(Roster.Count, 512, "roster");
			ClanDataRules.RequireCount(Applications.Count, 512, "application");
			ClanDataRules.WriteText(package, Status, 1024, "snapshot status", allowEmpty: true, allowLineBreaks: true);
			package.Write(ClanDataRules.RequireOptionalRequestId(ResponseRequestId));
			package.Write((int)ClanDataRules.RequireEnum(ResponseResultCode, "operation result"));
			ClanDataRules.WriteOptionalClanId(package, ClanId);
			ClanDataRules.WriteOptionalClanName(package, ClanName);
			ClanDataRules.WriteClanDescription(package, ClanDescription);
			ClanDataRules.WriteClanEmblemKey(package, ClanEmblemKey);
			package.Write((int)ClanDataRules.RequireEnum(SelfRole, "self role"));
			ClanDataRules.WriteOptionalClanId(package, PrimaryClanId, "primary clan id");
			ClanDataRules.WriteOptionalClanName(package, PrimaryClanName, "primary clan name");
			package.Write((int)ClanDataRules.RequireEnum(PrimaryRole, "primary clan role"));
			ClanDataRules.WriteOptionalClanId(package, GuestClanId, "guest clan id");
			ClanDataRules.WriteOptionalClanName(package, GuestClanName, "guest clan name");
			package.Write(Roster.Count);
			foreach (ClanPlayerSummary item in Roster)
			{
				item.Write(package);
			}
			package.Write(Applications.Count);
			foreach (ClanApplicationSummary application in Applications)
			{
				application.Write(package);
			}
			package.Write(Invite != null);
			Invite?.Write(package);
			package.Write(HasOwnApplication);
			if (HasOwnApplication)
			{
				ClanDataRules.WriteClanId(package, OwnApplicationClanId, "own application clan id");
				ClanDataRules.WriteClanName(package, OwnApplicationClanName, "own application clan name");
			}
		}

		public static ClanClientSnapshot Read(ZPackage package)
		{
			ClanClientSnapshot clanClientSnapshot = new ClanClientSnapshot
			{
				Status = ClanDataRules.ReadText(package, 1024, "snapshot status", allowEmpty: true, allowLineBreaks: true),
				ResponseRequestId = ClanDataRules.RequireOptionalRequestId(package.ReadLong()),
				ResponseResultCode = ClanDataRules.ReadEnum<ClanOperationResultCode>(package, "operation result"),
				ClanId = ClanDataRules.ReadOptionalClanId(package),
				ClanName = ClanDataRules.ReadOptionalClanName(package),
				ClanDescription = ClanDataRules.ReadClanDescription(package),
				ClanEmblemKey = ClanDataRules.ReadClanEmblemKey(package),
				SelfRole = ClanDataRules.ReadEnum<ClanRole>(package, "self role"),
				PrimaryClanId = ClanDataRules.ReadOptionalClanId(package, "primary clan id"),
				PrimaryClanName = ClanDataRules.ReadOptionalClanName(package, "primary clan name"),
				PrimaryRole = ClanDataRules.ReadEnum<ClanRole>(package, "primary clan role"),
				GuestClanId = ClanDataRules.ReadOptionalClanId(package, "guest clan id"),
				GuestClanName = ClanDataRules.ReadOptionalClanName(package, "guest clan name")
			};
			ReadList(package, clanClientSnapshot.Roster, 512, "roster", ClanPlayerSummary.Read);
			ReadList(package, clanClientSnapshot.Applications, 512, "application", ClanApplicationSummary.Read);
			if (package.ReadBool())
			{
				clanClientSnapshot.Invite = ClanInviteSummary.Read(package);
			}
			if (package.ReadBool())
			{
				clanClientSnapshot.OwnApplicationClanId = ClanDataRules.ReadClanId(package, "own application clan id");
				clanClientSnapshot.OwnApplicationClanName = ClanDataRules.ReadClanName(package, "own application clan name");
			}
			return clanClientSnapshot;
		}

		public bool ContainsClanPlayer(ClanPlayerRef player)
		{
			if (!player.IsValid)
			{
				return false;
			}
			foreach (ClanPlayerSummary item in Roster)
			{
				if (item.IsOnline && StringComparer.Ordinal.Equals(item.Id, player.Id))
				{
					return true;
				}
			}
			return false;
		}

		private static void ReadList<T>(ZPackage package, List<T> items, int maximum, string fieldName, Func<ZPackage, T> read)
		{
			int num = ClanDataRules.ReadCount(package, maximum, fieldName);
			for (int i = 0; i < num; i++)
			{
				items.Add(read(package));
			}
		}
	}
	internal sealed class ClanDirectoryPlayerSummary
	{
		public string PlayerId = "";

		public string PlayerName = "";

		public ClanDirectoryPlayerState State;

		public string ClanName = "";

		public bool IsOnline;

		public bool IsSelf;

		public bool CanInvite;

		public bool CanResolveApplication;

		public long LastSeenUtcTicks;

		public void Write(ZPackage package)
		{
			ClanDataRules.WritePlayerKey(package, PlayerId, "directory player key");
			ClanDataRules.WritePlayerName(package, PlayerName, "directory player name");
			package.Write((int)ClanDataRules.RequireEnum(State, "directory player state"));
			ClanDataRules.WriteOptionalClanName(package, ClanName, "directory clan name");
			package.Write(IsOnline);
			package.Write(IsSelf);
			package.Write(CanInvite);
			package.Write(CanResolveApplication);
			if (LastSeenUtcTicks >= 0)
			{
				long lastSeenUtcTicks = LastSeenUtcTicks;
				DateTime maxValue = DateTime.MaxValue;
				if (lastSeenUtcTicks <= maxValue.Ticks)
				{
					package.Write(LastSeenUtcTicks);
					return;
				}
			}
			throw new InvalidDataException("directory last-seen timestamp is invalid.");
		}

		public static ClanDirectoryPlayerSummary Read(ZPackage package)
		{
			ClanDirectoryPlayerSummary clanDirectoryPlayerSummary = new ClanDirectoryPlayerSummary
			{
				PlayerId = ClanDataRules.ReadPlayerKey(package, "directory player key"),
				PlayerName = ClanDataRules.ReadPlayerName(package, "directory player name"),
				State = ClanDataRules.ReadEnum<ClanDirectoryPlayerState>(package, "directory player state"),
				ClanName = ClanDataRules.ReadOptionalClanName(package, "directory clan name"),
				IsOnline = package.ReadBool(),
				IsSelf = package.ReadBool(),
				CanInvite = package.ReadBool(),
				CanResolveApplication = package.ReadBool()
			};
			clanDirectoryPlayerSummary.LastSeenUtcTicks = package.ReadLong();
			if (clanDirectoryPlayerSummary.LastSeenUtcTicks >= 0)
			{
				long lastSeenUtcTicks = clanDirectoryPlayerSummary.LastSeenUtcTicks;
				DateTime maxValue = DateTime.MaxValue;
				if (lastSeenUtcTicks <= maxValue.Ticks)
				{
					return clanDirectoryPlayerSummary;
				}
			}
			throw new InvalidDataException("directory last-seen timestamp is invalid.");
		}
	}
	internal sealed class ClanDirectorySnapshot
	{
		public long RequestId;

		public ClanOperationResultCode ResultCode;

		public string Status = "";

		public bool IsTruncated;

		public readonly List<ClanPublicSummary> PublicClans = new List<ClanPublicSummary>();

		public readonly List<ClanDirectoryPlayerSummary> Players = new List<ClanDirectoryPlayerSummary>();

		internal void Write(ZPackage package, int publicClanCount, int playerCount)
		{
			publicClanCount = ClanDataRules.RequireRange(publicClanCount, 0, Math.Min(PublicClans.Count, 1024), "directory clan count");
			playerCount = ClanDataRules.RequireRange(playerCount, 0, Math.Min(Players.Count, 1024), "directory player count");
			package.Write(ClanDataRules.RequireRequestId(RequestId));
			package.Write((int)ClanDataRules.RequireEnum(ResultCode, "directory result"));
			ClanDataRules.WriteText(package, Status, 1024, "directory status", allowEmpty: true, allowLineBreaks: true);
			package.Write(IsTruncated);
			package.Write(publicClanCount);
			for (int i = 0; i < publicClanCount; i++)
			{
				PublicClans[i].Write(package);
			}
			package.Write(playerCount);
			for (int j = 0; j < playerCount; j++)
			{
				Players[j].Write(package);
			}
		}

		public static ClanDirectorySnapshot Read(ZPackage package)
		{
			ClanDirectorySnapshot clanDirectorySnapshot = new ClanDirectorySnapshot
			{
				RequestId = ClanDataRules.RequireRequestId(package.ReadLong()),
				ResultCode = ClanDataRules.ReadEnum<ClanOperationResultCode>(package, "directory result"),
				Status = ClanDataRules.ReadText(package, 1024, "directory status", allowEmpty: true, allowLineBreaks: true),
				IsTruncated = package.ReadBool()
			};
			int num = ClanDataRules.ReadCount(package, 1024, "directory clan");
			for (int i = 0; i < num; i++)
			{
				clanDirectorySnapshot.PublicClans.Add(ClanPublicSummary.Read(package));
			}
			int num2 = ClanDataRules.ReadCount(package, 1024, "directory player");
			for (int j = 0; j < num2; j++)
			{
				clanDirectorySnapshot.Players.Add(ClanDirectoryPlayerSummary.Read(package));
			}
			return clanDirectorySnapshot;
		}
	}
	internal static class ClanLocalizationManager
	{
		private static readonly string[] SupportedExtensions = new string[2] { ".yml", ".json" };

		private const long MaximumExternalTranslationBytes = 1048576L;

		private const int MaximumTranslationValueLength = 4096;

		private static readonly object Sync = new object();

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

		private static BaseUnityPlugin? _plugin;

		private static ManualLogSource? _logger;

		private static Dictionary<string, string> _embeddedEnglish = new Dictionary<string, string>(StringComparer.Ordinal);

		private static Dictionary<string, string> _currentTexts = new Dictionary<string, string>(StringComparer.Ordinal);

		private static string _currentLanguage = "English";

		private static bool _hasLoadedLanguage;

		private static bool _initialized;

		internal static event Action? LanguageChanged;

		internal static void Initialize(BaseUnityPlugin plugin, Harmony harmony)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected O, but got Unknown
			if (!_initialized)
			{
				_plugin = plugin;
				_logger = ClanPlugin.ClanLogger;
				_embeddedEnglish = LoadEmbeddedTranslation("English") ?? throw new InvalidOperationException(plugin.Info.Metadata.Name + " has no embedded translations/English.yml or translations/English.json resource.");
				_currentTexts = new Dictionary<string, string>(_embeddedEnglish, StringComparer.Ordinal);
				_initialized = true;
				harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ClanLocalizationManager), "AfterSetupLanguage", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ClanLocalizationManager), "AfterSetupGui", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Localization instance = Localization.instance;
				if (instance != null)
				{
					Reload(SafeSelectedLanguage(instance));
				}
			}
		}

		internal static void Dispose()
		{
			lock (Sync)
			{
				_plugin = null;
				_logger = null;
				_embeddedEnglish = new Dictionary<string, string>(StringComparer.Ordinal);
				_currentTexts = new Dictionary<string, string>(StringComparer.Ordinal);
				_currentLanguage = "English";
				_hasLoadedLanguage = false;
				LoggedWarnings.Clear();
				ClanLocalizationManager.LanguageChanged = null;
				_initialized = false;
			}
		}

		internal static string GetText(string key)
		{
			string text = NormalizeKey(key);
			lock (Sync)
			{
				if (_currentTexts.TryGetValue(text, out string value))
				{
					return value;
				}
				if (_embeddedEnglish.TryGetValue(text, out string value2))
				{
					return value2;
				}
			}
			WarnOnce("missing:" + text, "Missing Clan localization key '" + text + "'.");
			return text;
		}

		private static void AfterSetupLanguage(string language)
		{
			Reload(language);
		}

		private static void AfterSetupGui()
		{
			Localization instance = Localization.instance;
			if (instance != null && NeedsReload(SafeSelectedLanguage(instance)))
			{
				Reload(SafeSelectedLanguage(instance));
			}
		}

		private static bool NeedsReload(string language)
		{
			lock (Sync)
			{
				return !_hasLoadedLanguage || !_currentLanguage.Equals(language, StringComparison.OrdinalIgnoreCase);
			}
		}

		private static void Reload(string language)
		{
			if (!_initialized || (Object)(object)_plugin == (Object)null)
			{
				return;
			}
			string text = (string.IsNullOrWhiteSpace(language) ? "English" : language.Trim());
			Dictionary<string, List<string>> externalFiles = FindExternalTranslations();
			Dictionary<string, string> dictionary = new Dictionary<string, string>(_embeddedEnglish, StringComparer.Ordinal);
			MergeExternalTranslation(dictionary, externalFiles, "English");
			if (!text.Equals("English", StringComparison.OrdinalIgnoreCase))
			{
				Dictionary<string, string> dictionary2 = LoadEmbeddedTranslation(text);
				if (dictionary2 != null)
				{
					Merge(dictionary, ValidateTranslation("embedded " + text + " translation", dictionary2));
				}
				MergeExternalTranslation(dictionary, externalFiles, text);
			}
			lock (Sync)
			{
				_currentTexts = dictionary;
				_currentLanguage = text;
				_hasLoadedLanguage = true;
			}
			InvokeLanguageChanged();
		}

		private static Dictionary<string, List<string>> FindExternalTranslations()
		{
			BaseUnityPlugin plugin = _plugin;
			if ((Object)(object)plugin == (Object)null)
			{
				return new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
			}
			string name = plugin.Info.Metadata.Name;
			string text = Directory.GetParent(Paths.PluginPath)?.FullName;
			if (string.IsNullOrWhiteSpace(text) || !Directory.Exists(text))
			{
				WarnOnce("missing-bepinex-root", "Could not locate the BepInEx root while searching for Clan translations.");
				return new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
			}
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in EnumerateFilesSafely(text))
			{
				string extension = Path.GetExtension(item);
				if (!SupportedExtensions.Contains<string>(extension, StringComparer.OrdinalIgnoreCase))
				{
					continue;
				}
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item);
				string text2 = name + ".";
				if (fileNameWithoutExtension.StartsWith(text2, StringComparison.OrdinalIgnoreCase) && fileNameWithoutExtension.Length != text2.Length)
				{
					string key = fileNameWithoutExtension.Substring(text2.Length);
					if (!dictionary.TryGetValue(key, out var value))
					{
						value = (dictionary[key] = new List<string>());
					}
					value.Add(item);
				}
			}
			foreach (List<string> value2 in dictionary.Values)
			{
				value2.Sort(CompareExternalCandidates);
			}
			return dictionary;
		}

		private static IEnumerable<string> EnumerateFilesSafely(string root)
		{
			Stack<string> pending = new Stack<string>();
			pending.Push(Path.GetFullPath(root));
			while (pending.Count > 0)
			{
				string directory = pending.Pop();
				string[] files;
				try
				{
					files = Directory.GetFiles(directory);
				}
				catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException) ? 1 : 0) != 0)
				{
					WarnOnce("scan-files:" + directory, "Could not scan '" + directory + "' for Clan translations: " + ex.Message);
					continue;
				}
				Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
				string[] array = files;
				for (int i = 0; i < array.Length; i++)
				{
					yield return array[i];
				}
				string[] directories;
				try
				{
					directories = Directory.GetDirectories(directory);
				}
				catch (Exception ex2) when (((ex2 is IOException || ex2 is UnauthorizedAccessException) ? 1 : 0) != 0)
				{
					WarnOnce("scan-directories:" + directory, "Could not scan subdirectories of '" + directory + "' for Clan translations: " + ex2.Message);
					continue;
				}
				Array.Sort(directories, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
				for (int num = directories.Length - 1; num >= 0; num--)
				{
					string text = directories[num];
					try
					{
						if ((File.GetAttributes(text) & FileAttributes.ReparsePoint) == 0)
						{
							pending.Push(text);
						}
					}
					catch (Exception ex3) when (((ex3 is IOException || ex3 is UnauthorizedAccessException) ? 1 : 0) != 0)
					{
						WarnOnce("scan-attributes:" + text, "Could not inspect '" + text + "' while searching for Clan translations: " + ex3.Message);
					}
				}
			}
		}

		private static int CompareExternalCandidates(string left, string right)
		{
			int num = IsUnderDirectory(left, Paths.ConfigPath).CompareTo(IsUnderDirectory(right, Paths.ConfigPath));
			if (num != 0)
			{
				return -num;
			}
			int num2 = ExtensionPriority(left).CompareTo(ExtensionPriority(right));
			if (num2 == 0)
			{
				return StringComparer.OrdinalIgnoreCase.Compare(Path.GetFullPath(left), Path.GetFullPath(right));
			}
			return num2;
		}

		private static int ExtensionPriority(string path)
		{
			return (!Path.GetExtension(path).Equals(".yml", StringComparison.OrdinalIgnoreCase)) ? 1 : 0;
		}

		private static bool IsUnderDirectory(string path, string directory)
		{
			string fullPath = Path.GetFullPath(path);
			string text = Path.GetFullPath(directory).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			string value = text + directorySeparatorChar;
			return fullPath.StartsWith(value, StringComparison.OrdinalIgnoreCase);
		}

		private static void MergeExternalTranslation(IDictionary<string, string> target, IReadOnlyDictionary<string, List<string>> externalFiles, string language)
		{
			if (!externalFiles.TryGetValue(language, out List<string> value))
			{
				return;
			}
			for (int i = 0; i < value.Count; i++)
			{
				string text = value[i];
				Dictionary<string, string> dictionary = TryLoadExternalTranslation(text);
				if (dictionary != null)
				{
					Merge(target, dictionary);
					for (int j = i + 1; j < value.Count; j++)
					{
						WarnOnce("duplicate:" + language + ":" + value[j], "Ignoring duplicate Clan " + language + " translation '" + value[j] + "'; using '" + text + "'.");
					}
					break;
				}
			}
		}

		private static Dictionary<string, string>? TryLoadExternalTranslation(string path)
		{
			try
			{
				long length = new FileInfo(path).Length;
				if (length <= 0 || length > 1048576)
				{
					throw new InvalidDataException($"translation size must be between 1 and {1048576L} bytes");
				}
				Dictionary<string, string> dictionary = ParseTranslation(File.ReadAllText(path, Encoding.UTF8));
				if (dictionary == null || dictionary.Count == 0)
				{
					throw new InvalidDataException("the translation file is empty");
				}
				dictionary = ValidateTranslation(path, dictionary);
				if (dictionary.Count == 0)
				{
					throw new InvalidDataException("the translation file has no valid Clan keys");
				}
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)("Loaded external Clan translation '" + path + "'."));
				}
				return dictionary;
			}
			catch (Exception ex)
			{
				WarnOnce("external:" + path + ":" + ex.GetType().FullName + ":" + ex.Message, "Could not load external Clan translation '" + path + "': " + ex.Message + ". Falling back to the next valid source.");
				return null;
			}
		}

		private static Dictionary<string, string> ValidateTranslation(string source, IReadOnlyDictionary<string, string> translation)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, string> item in translation)
			{
				if (!_embeddedEnglish.TryGetValue(item.Key, out string value))
				{
					WarnOnce("unknown-key:" + source + ":" + item.Key, "Ignored unknown Clan translation key '" + item.Key + "' in '" + source + "'.");
				}
				else if (item.Value.Length > 4096)
				{
					WarnOnce("long-value:" + source + ":" + item.Key, $"Ignored Clan translation key '{item.Key}' in '{source}' because its value exceeds {4096} characters.");
				}
				else if (!HasCompatiblePlaceholders(value, item.Value))
				{
					WarnOnce("placeholders:" + source + ":" + item.Key, "Ignored Clan translation key '" + item.Key + "' in '" + source + "' because its format placeholders do not match English.");
				}
				else
				{
					dictionary[item.Key] = item.Value;
				}
			}
			return dictionary;
		}

		private static bool HasCompatiblePlaceholders(string english, string candidate)
		{
			if (TryReadPlaceholderIndexes(english, out HashSet<int> indexes) && TryReadPlaceholderIndexes(candidate, out HashSet<int> indexes2))
			{
				return indexes.SetEquals(indexes2);
			}
			return false;
		}

		private static bool TryReadPlaceholderIndexes(string template, out HashSet<int> indexes)
		{
			indexes = new HashSet<int>();
			int num = 0;
			while (num < template.Length)
			{
				switch (template[num])
				{
				case '{':
				{
					if (num + 1 < template.Length && template[num + 1] == '{')
					{
						num += 2;
						break;
					}
					int num2 = template.IndexOf('}', num + 1);
					if (num2 < 0 || !int.TryParse(template.Substring(num + 1, num2 - num - 1), NumberStyles.None, CultureInfo.InvariantCulture, out var result))
					{
						return false;
					}
					indexes.Add(result);
					num = num2 + 1;
					break;
				}
				case '}':
					if (num + 1 >= template.Length || template[num + 1] != '}')
					{
						return false;
					}
					num += 2;
					break;
				default:
					num++;
					break;
				}
			}
			return true;
		}

		private static Dictionary<string, string>? LoadEmbeddedTranslation(string language)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string[] supportedExtensions = SupportedExtensions;
			foreach (string text in supportedExtensions)
			{
				string suffix = "translations." + language + text;
				string text2 = executingAssembly.GetManifestResourceNames().OrderBy<string, string>((string name) => name, StringComparer.Ordinal).FirstOrDefault((string name) => name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase));
				if (text2 == null)
				{
					continue;
				}
				using Stream stream = executingAssembly.GetManifestResourceStream(text2);
				if (stream == null)
				{
					continue;
				}
				using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
				return ParseTranslation(streamReader.ReadToEnd());
			}
			return null;
		}

		private static Dictionary<string, string>? ParseTranslation(string data)
		{
			Dictionary<string, string> dictionary = new DeserializerBuilder().IgnoreFields().WithDuplicateKeyChecking().Build()
				.Deserialize<Dictionary<string, string>>(data);
			if (dictionary == null)
			{
				return null;
			}
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, string> item in dictionary)
			{
				string text = NormalizeKey(item.Key);
				if (!string.IsNullOrWhiteSpace(text) && item.Value != null)
				{
					dictionary2[text] = item.Value;
				}
			}
			return dictionary2;
		}

		private static void Merge(IDictionary<string, string> target, IReadOnlyDictionary<string, string>? source)
		{
			if (source == null)
			{
				return;
			}
			foreach (KeyValuePair<string, string> item in source)
			{
				target[item.Key] = item.Value;
			}
		}

		private static string SafeSelectedLanguage(Localization localization)
		{
			try
			{
				return localization.GetSelectedLanguage();
			}
			catch (Exception ex)
			{
				WarnOnce("selected-language", "Could not read the selected language: " + ex.Message + ". Using English.");
				return _currentLanguage;
			}
		}

		private static string NormalizeKey(string key)
		{
			return (key ?? string.Empty).Trim().TrimStart(new char[1] { '$' });
		}

		private static void InvokeLanguageChanged()
		{
			Delegate[] array = ClanLocalizationManager.LanguageChanged?.GetInvocationList() ?? Array.Empty<Delegate>();
			foreach (Delegate obj in array)
			{
				try
				{
					((Action)obj)();
				}
				catch (Exception ex)
				{
					ManualLogSource? logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Clan language-change subscriber failed: " + ex.GetBaseException().Message));
					}
				}
			}
		}

		private static void WarnOnce(string identity, string message)
		{
			lock (Sync)
			{
				if (!LoggedWarnings.Add(identity))
				{
					return;
				}
			}
			ManualLogSource? logger = _logger;
			if (logger != null)
			{
				logger.LogWarning((object)message);
			}
		}
	}
	internal static class ClanLocalization
	{
		private const string WireStatusPrefix = "@ClanL10n1:";

		private const int MaximumWireStatusLength = 1024;

		private const int MaximumWireArgumentCount = 8;

		private const int MaximumWireArgumentUtf8Bytes = 256;

		private static readonly UTF8Encoding StrictUtf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);

		private static bool _malformedWireStatusLogged;

		internal static event Action? LanguageChanged
		{
			add
			{
				ClanLocalizationManager.LanguageChanged += value;
			}
			remove
			{
				ClanLocalizationManager.LanguageChanged -= value;
			}
		}

		internal static void Initialize(BaseUnityPlugin plugin, Harmony harmony)
		{
			ClanLocalizationManager.Initialize(plugin, harmony);
		}

		internal static void Dispose()
		{
			ClanLocalizationManager.Dispose();
		}

		internal static string Text(string key)
		{
			return ClanLocalizationManager.GetText(key);
		}

		internal static string Format(string key, params object[] arguments)
		{
			string text = Text(key);
			try
			{
				return string.Format(CultureInfo.CurrentCulture, text, arguments);
			}
			catch (FormatException ex)
			{
				ClanPlugin.ClanLogger.LogWarning((object)("Invalid format string for Clan localization key '" + key + "': " + ex.Message));
				return text;
			}
		}

		internal static string EncodeStatus(string key, params object[] arguments)
		{
			string text = (key ?? string.Empty).Trim().TrimStart(new char[1] { '$' });
			if (text.Length == 0)
			{
				return string.Empty;
			}
			if (text.Length > 96 || !IsSafeWireKey(text))
			{
				ClanPlugin.ClanLogger.LogWarning((object)("Refused to encode invalid Clan status localization key '" + text + "'."));
				return text;
			}
			object[] array = arguments ?? Array.Empty<object>();
			int num = Math.Min(array.Length, 8);
			StringBuilder stringBuilder = new StringBuilder("@ClanL10n1:".Length + text.Length + num * 24);
			stringBuilder.Append("@ClanL10n1:").Append(text);
			for (int i = 0; i < num; i++)
			{
				string s = LimitUtf8(Convert.ToString(array[i], CultureInfo.InvariantCulture) ?? string.Empty, 256);
				stringBuilder.Append('|').Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(s)));
			}
			if (stringBuilder.Length <= 1024)
			{
				return stringBuilder.ToString();
			}
			ClanPlugin.ClanLogger.LogWarning((object)("Clan status localization payload for '" + text + "' exceeded its wire limit."));
			return "@ClanL10n1:" + text;
		}

		internal static string ResolveStatus(string value)
		{
			if (string.IsNullOrEmpty(value) || !value.StartsWith("@ClanL10n1:", StringComparison.Ordinal))
			{
				return value ?? string.Empty;
			}
			try
			{
				if (value.Length > 1024)
				{
					throw new FormatException("status payload exceeds its wire limit");
				}
				string[] array = value.Substring("@ClanL10n1:".Length).Split(new char[1] { '|' });
				if (array.Length == 0 || array.Length > 9 || array[0].Length == 0 || array[0].Length > 96 || !IsSafeWireKey(array[0]))
				{
					throw new FormatException("status payload contains an invalid localization key or argument count");
				}
				if (array.Length == 1)
				{
					return Text(array[0]);
				}
				List<object> list = new List<object>(array.Length - 1);
				for (int i = 1; i < array.Length; i++)
				{
					byte[] array2 = Convert.FromBase64String(array[i]);
					if (array2.Length > 256)
					{
						throw new FormatException("status argument exceeds its wire limit");
					}
					list.Add(StrictUtf8.GetString(array2));
				}
				return Format(array[0], list.ToArray());
			}
			catch (Exception ex) when (((ex is FormatException || ex is DecoderFallbackException) ? 1 : 0) != 0)
			{
				if (!_malformedWireStatusLogged)
				{
					_malformedWireStatusLogged = true;
					ClanPlugin.ClanLogger.LogWarning((object)("Ignored a malformed localized Clan status payload: " + ex.Message));
				}
				return Text("clan_status_unavailable");
			}
		}

		internal static string Role(ClanRole role)
		{
			return role switch
			{
				ClanRole.Leader => Text("clan_role_leader"), 
				ClanRole.Officer => Text("clan_role_officer"), 
				ClanRole.Member => Text("clan_role_member"), 
				ClanRole.Guest => Text("clan_role_guest"), 
				_ => role.ToString(), 
			};
		}

		private static bool IsSafeWireKey(string key)
		{
			foreach (char c in key)
			{
				if ((c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '_' && c != '-' && c != '.')
				{
					return false;
				}
			}
			return true;
		}

		private static string LimitUtf8(string value, int maximumBytes)
		{
			if (Encoding.UTF8.GetByteCount(value) <= maximumBytes)
			{
				return value;
			}
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			int num = 0;
			int num2;
			for (num2 = 0; num2 < value.Length; num2++)
			{
				int num3 = ((!char.IsHighSurrogate(value[num2]) || num2 + 1 >= value.Length || !char.IsLowSurrogate(value[num2 + 1])) ? 1 : 2);
				string text = value.Substring(num2, num3);
				int byteCount = Encoding.UTF8.GetByteCount(text);
				if (num + byteCount > maximumBytes)
				{
					break;
				}
				stringBuilder.Append(text);
				num += byteCount;
				num2 += num3 - 1;
			}
			return stringBuilder.ToString();
		}
	}
	public enum ClanWardAuthorizationResolution
	{
		Unavailable,
		ResolvedNoAuthorization,
		Authorized
	}
	public enum ClanMembershipResolution
	{
		Unavailable,
		Resolved
	}
	public sealed class ClanInfo
	{
		public string ClanId { get; }

		public string Name { get; }

		public string Description { get; }

		public string EmblemKey { get; }

		public string LeaderName { get; }

		public int MemberCount { get; }

		internal ClanInfo(string clanId, string name, string description, string emblemKey, string leaderName, int memberCount)
		{
			ClanId = clanId;
			Name = name;
			Description = description;
			EmblemKey = emblemKey;
			LeaderName = leaderName;
			MemberCount = memberCount;
		}
	}
	public sealed class ClanMemberInfo
	{
		public string PlayerKey { get; }

		public string PlatformId { get; }

		public long PlayerId { get; }

		public string Name { get; }

		public ClanRole Role { get; }

		public bool IsSelf { get; }

		public bool IsOnline { get; }

		internal ClanMemberInfo(ClanPlayerSummary source)
		{
			PlayerKey = source.Player.Id;
			PlatformId = source.Player.PlatformId;
			PlayerId = source.Player.CharacterPlayerId;
			Name = source.Player.Name;
			Role = source.Role;
			IsSelf = source.IsSelf;
			IsOnline = source.IsOnline;
		}
	}
	public sealed class ClanLocalSnapshot
	{
		private static readonly IReadOnlyList<ClanMemberInfo> NoMembers = new ReadOnlyCollection<ClanMemberInfo>(Array.Empty<ClanMemberInfo>());

		public bool IsReady { get; }

		public bool HasClan => Clan != null;

		public bool HasPrimaryClan => !string.IsNullOrWhiteSpace(PrimaryClanId);

		public bool HasGuestClan => !string.IsNullOrWhiteSpace(GuestClanId);

		public bool HasAnyClan
		{
			get
			{
				if (!HasPrimaryClan)
				{
					return HasGuestClan;
				}
				return true;
			}
		}

		public string EffectiveClanId => Clan?.ClanId ?? "";

		public string PrimaryClanId { get; }

		public string PrimaryClanName { get; }

		public ClanRole? PrimaryRole { get; }

		public string GuestClanId { get; }

		public string GuestClanName { get; }

		public ClanInfo? Clan { get; }

		public ClanRole? SelfRole { get; }

		public IReadOnlyList<ClanMemberInfo> Members { get; }

		internal static ClanLocalSnapshot Empty { get; } = new ClanLocalSnapshot(isReady: false, "", "", null, "", "", null, null, NoMembers);

		internal ClanLocalSnapshot(bool isReady, string primaryClanId, string primaryClanName, ClanRole? primaryRole, string guestClanId, string guestClanName, ClanInfo? clan, ClanRole? selfRole, IReadOnlyList<ClanMemberInfo> members)
		{
			IsReady = isReady;
			PrimaryClanId = primaryClanId ?? "";
			PrimaryClanName = primaryClanName ?? "";
			PrimaryRole = primaryRole;
			GuestClanId = guestClanId ?? "";
			GuestClanName = guestClanName ?? "";
			Clan = clan;
			SelfRole = selfRole;
			Members = members;
		}
	}
	public enum ClanRenameResultCode
	{
		Unknown,
		Success,
		Unchanged,
		InvalidName,
		NotConnected,
		IdentityUnavailable,
		NotLeader,
		ClanChanged,
		NameAlreadyExists,
		RateLimited,
		TimedOut,
		SessionEnded,
		Failed
	}
	public sealed class ClanRenameResult
	{
		public long RequestId { get; }

		public ClanRenameResultCode Code { get; }

		public bool Succeeded
		{
			get
			{
				ClanRenameResultCode code = Code;
				if ((uint)(code - 1) <= 1u)
				{
					return true;
				}
				return false;
			}
		}

		public string RequestedName { get; }

		public string Message { get; }

		public ClanLocalSnapshot State { get; }

		internal ClanRenameResult(long requestId, ClanRenameResultCode code, string requestedName, string message, ClanLocalSnapshot state)
		{
			RequestId = requestId;
			Code = code;
			RequestedName = requestedName;
			Message = message;
			State = state;
		}
	}
	public sealed class ClanRenameRequestHandle
	{
		private readonly object _gate = new object();

		private Action<ClanRenameResult>? _completed;

		private ClanRenameResult? _result;

		public long RequestId { get; }

		public bool IsCompleted
		{
			get
			{
				lock (_gate)
				{
					return _result != null;
				}
			}
		}

		public ClanRenameResult? Result
		{
			get
			{
				lock (_gate)
				{
					return _result;
				}
			}
		}

		public event Action<ClanRenameResult>? Completed
		{
			add
			{
				if (value == null)
				{
					return;
				}
				ClanRenameResult result;
				lock (_gate)
				{
					result = _result;
					if (result == null)
					{
						_completed = (Action<ClanRenameResult>)Delegate.Combine(_completed, value);
						return;
					}
				}
				InvokeSubscriber(value, result);
			}
			remove
			{
				lock (_gate)
				{
					_completed = (Action<ClanRenameResult>)Delegate.Remove(_completed, value);
				}
			}
		}

		internal ClanRenameRequestHandle(long requestId)
		{
			RequestId = requestId;
		}

		public bool TryGetResult(out ClanRenameResult? result)
		{
			lock (_gate)
			{
				result = _result;
				return result != null;
			}
		}

		internal bool Complete(ClanRenameResult result)
		{
			Action<ClanRenameResult> completed;
			lock (_gate)
			{
				if (_result != null)
				{
					return false;
				}
				_result = result;
				completed = _completed;
				_completed = null;
			}
			if (completed != null)
			{
				Delegate[] invocationList = completed.GetInvocationList();
				for (int i = 0; i < invocationList.Length; i++)
				{
					InvokeSubscriber((Action<ClanRenameResult>)invocationList[i], result);
				}
			}
			return true;
		}

		private static void InvokeSubscriber(Action<ClanRenameResult> subscriber, ClanRenameResult result)
		{
			try
			{
				subscriber(result);
			}
			catch (Exception arg)
			{
				ClanPlugin.ClanLogger.LogWarning((object)$"Clan API rename completion subscriber failed: {arg}");
			}
		}
	}
	public static class ClanApi
	{
		private sealed class PendingRename
		{
			public ClanRenameRequestHandle Handle { get; }

			public string RequestedName { get; }

			public float Deadline { get; }

			public PendingRename(ClanRenameRequestHandle handle, string requestedName, float deadline)
			{
				Handle = handle;
				RequestedName = requestedName;
				Deadline = deadline;
			}
		}

		public const int ApiVersion = 4;

		private const float RenameTimeoutSeconds = 15f;

		private static readonly object PendingLock = new object();

		private static readonly Dictionary<long, PendingRename> PendingRenames = new Dictionary<long, PendingRename>();

		private static readonly IReadOnlyList<ClanInfo> NoClans = new ReadOnlyCollection<ClanInfo>(Array.Empty<ClanInfo>());

		private static bool _initialized;

		private static long _registryRevision;

		public static ClanLocalSnapshot Current { get; private set; } = ClanLocalSnapshot.Empty;

		public static IReadOnlyList<ClanInfo> KnownClans { get; private set; } = NoClans;

		public static long RegistryRevision => Interlocked.Read(in _registryRevision);

		public static event Action<ClanLocalSnapshot>? StateChanged;

		public static event Action<IReadOnlyList<ClanInfo>>? DirectoryChanged;

		public static event Action<ClanRenameResult>? RenameCompleted;

		public static event Action? WardAuthorizationChanged;

		public static event Action? RegistryChanged;

		public static ClanWardAuthorizationResolution ResolveWardAuthorization(string platformId, long characterPlayerId, out string clanId, out string clanName)
		{
			return ClanRegistry.ResolveWardAuthorization(platformId, characterPlayerId, out clanId, out clanName);
		}

		public static ClanMembershipResolution ResolveMemberships(string platformId, long characterPlayerId, out string primaryClanId, out string primaryClanName, out string guestClanId, out string guestClanName)
		{
			return ClanRegistry.ResolveMemberships(platformId, characterPlayerId, out primaryClanId, out primaryClanName, out guestClanId, out guestClanName);
		}

		internal static void Initialize()
		{
			if (!_initialized)
			{
				_initialized = true;
				ClanRpc.SnapshotChanged += OnSnapshotChanged;
				ClanRpc.DirectoryChanged += OnDirectoryChanged;
				Current = CreateLocalSnapshot(ClanRpc.CurrentSnapshot);
				KnownClans = CreateDirectory(ClanRpc.CurrentDirectory);
			}
		}

		internal static void Dispose()
		{
			if (_initialized)
			{
				ClanRpc.SnapshotChanged -= OnSnapshotChanged;
				ClanRpc.DirectoryChanged -= OnDirectoryChanged;
				CompleteAll(ClanRenameResultCode.SessionEnded, "Clan API was shut down.");
				_initialized = false;
				Current = ClanLocalSnapshot.Empty;
				KnownClans = NoClans;
				ClanApi.StateChanged = null;
				ClanApi.DirectoryChanged = null;
				ClanApi.RenameCompleted = null;
				ClanApi.WardAuthorizationChanged = null;
				ClanApi.RegistryChanged = null;
			}
		}

		internal static void NotifyRegistryChanged()
		{
			Interlocked.Increment(ref _registryRevision);
			InvokeInvalidationSubscribers(ClanApi.WardAuthorizationChanged, "ward authorization");
			InvokeInvalidationSubscribers(ClanApi.RegistryChanged, "registry");
		}

		private static void InvokeInvalidationSubscribers(Action? subscribers, string eventName)
		{
			if (subscribers == null)
			{
				return;
			}
			Delegate[] invocationList = subscribers.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception arg)
				{
					ClanPlugin.ClanLogger.LogWarning((object)$"Clan API {eventName} subscriber failed: {arg}");
				}
			}
		}

		internal static void ResetSession()
		{
			ClanLocalSnapshot clanLocalSnapshot = CreateLocalSnapshot(ClanRpc.CurrentSnapshot);
			bool num = !HasSameState(Current, clanLocalSnapshot);
			Current = clanLocalSnapshot;
			if (num)
			{
				InvokeSubscribers(ClanApi.StateChanged, Current, "state");
			}
			bool num2 = KnownClans.Count != 0;
			KnownClans = NoClans;
			if (num2)
			{
				InvokeSubscribers<IReadOnlyList<ClanInfo>>(ClanApi.DirectoryChanged, KnownClans, "directory");
			}
			CompleteAll(ClanRenameResultCode.SessionEnded, "The clan session ended.");
		}

		internal static void Tick()
		{
			List<PendingRename> list = null;
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			lock (PendingLock)
			{
				foreach (PendingRename value in PendingRenames.Values)
				{
					if (realtimeSinceStartup >= value.Deadline)
					{
						if (list == null)
						{
							list = new List<PendingRename>();
						}
						list.Add(value);
					}
				}
				if (list != null)
				{
					foreach (PendingRename item in list)
					{
						PendingRenames.Remove(item.Handle.RequestId);
					}
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (PendingRename item2 in list)
			{
				Complete(item2, ClanRenameResultCode.TimedOut, "The clan rename request timed out.");
			}
		}

		public static ClanInfo? GetClan(string clanId)
		{
			if (Current.Clan != null && StringComparer.Ordinal.Equals(Current.Clan.ClanId, clanId))
			{
				return Current.Clan;
			}
			return KnownClans.FirstOrDefault((ClanInfo clan) => StringComparer.Ordinal.Equals(clan.ClanId, clanId));
		}

		public static IReadOnlyList<ClanMemberInfo>? GetMembers(string clanId)
		{
			if (Current.Clan == null || !StringComparer.Ordinal.Equals(Current.Clan.ClanId, clanId))
			{
				return null;
			}
			return Current.Members;
		}

		public static long RequestDirectoryRefresh()
		{
			return ClanRpc.RequestDirectory();
		}

		public static ClanRenameRequestHandle RequestRenameClan(string newName)
		{
			long num = ClanRpc.NextRequestId();
			ClanRenameRequestHandle clanRenameRequestHandle = new ClanRenameRequestHandle(num);
			string text;
			try
			{
				text = ClanDataRules.RequireClanName(newName);
			}
			catch (InvalidDataException ex)
			{
				CompleteLocal(clanRenameRequestHandle, ClanRenameResultCode.InvalidName, newName ?? "", ex.Message);
				return clanRenameRequestHandle;
			}
			if (!Current.IsReady)
			{
				CompleteLocal(clanRenameRequestHandle, ClanRenameResultCode.IdentityUnavailable, text, "Clan identity is not ready.");
				return clanRenameRequestHandle;
			}
			if (Current.Clan == null)
			{
				CompleteLocal(clanRenameRequestHandle, ClanRenameResultCode.ClanChanged, text, "The local character does not belong to a clan.");
				return clanRenameRequestHandle;
			}
			PendingRename pendingRename = new PendingRename(clanRenameRequestHandle, text, Time.realtimeSinceStartup + 15f);
			lock (PendingLock)
			{
				PendingRenames.Add(num, pendingRename);
			}
			if (!ClanRpc.Send(new ClanRequest
			{
				Type = ClanRequestType.RenameClan,
				RequestId = num,
				ClanId = Current.Clan.ClanId,
				ClanName = text
			}))
			{
				bool flag;
				lock (PendingLock)
				{
					flag = PendingRenames.Remove(num);
				}
				if (flag)
				{
					Complete(pendingRename, ClanRenameResultCode.NotConnected, "Clan server is not connected.");
				}
			}
			return clanRenameRequestHandle;
		}

		private static void OnSnapshotChanged(ClanClientSnapshot snapshot)
		{
			ClanLocalSnapshot clanLocalSnapshot = CreateLocalSnapshot(snapshot);
			bool num = !HasSameState(Current, clanLocalSnapshot);
			Current = clanLocalSnapshot;
			if (num)
			{
				InvokeSubscribers(ClanApi.StateChanged, Current, "state");
			}
			if (snapshot.ResponseRequestId <= 0)
			{
				return;
			}
			PendingRename value;
			lock (PendingLock)
			{
				if (!PendingRenames.TryGetValue(snapshot.ResponseRequestId, out value))
				{
					return;
				}
				PendingRenames.Remove(snapshot.ResponseRequestId);
			}
			Complete(value, MapResult(snapshot), snapshot.Status);
		}

		private static void OnDirectoryChanged(ClanDirectorySnapshot directory)
		{
			IReadOnlyList<ClanInfo> readOnlyList = CreateDirectory(directory);
			bool num = !HasSameDirectory(KnownClans, readOnlyList);
			KnownClans = readOnlyList;
			if (num)
			{
				InvokeSubscribers<IReadOnlyList<ClanInfo>>(ClanApi.DirectoryChanged, KnownClans, "directory");
			}
		}

		private static ClanLocalSnapshot CreateLocalSnapshot(ClanClientSnapshot source)
		{
			IReadOnlyList<ClanMemberInfo> members = new ReadOnlyCollection<ClanMemberInfo>(source.Roster.Select((ClanPlayerSummary member) => new ClanMemberInfo(member)).ToList());
			ClanInfo clan = null;
			if (source.HasClan)
			{
				string leaderName = source.Roster.FirstOrDefault((ClanPlayerSummary member) => member.Role == ClanRole.Leader)?.Name ?? "";
				clan = new ClanInfo(source.ClanId, source.ClanName, source.ClanDescription, source.ClanEmblemKey, leaderName, source.Roster.Count);
			}
			return new ClanLocalSnapshot(ClanRpc.IsIdentityReady, source.PrimaryClanId, source.PrimaryClanName, source.HasPrimaryClan ? new ClanRole?(source.PrimaryRole) : ((ClanRole?)null), source.GuestClanId, source.GuestClanName, clan, source.HasClan ? new ClanRole?(source.SelfRole) : ((ClanRole?)null), members);
		}

		private static IReadOnlyList<ClanInfo> CreateDirectory(ClanDirectorySnapshot source)
		{
			return new ReadOnlyCollection<ClanInfo>(source.PublicClans.Select((ClanPublicSummary clan) => new ClanInfo(clan.ClanId, clan.Name, clan.Description, clan.EmblemKey, clan.LeaderName, clan.MemberCount)).ToList());
		}

		private static ClanRenameResultCode MapResult(ClanClientSnapshot snapshot)
		{
			return snapshot.ResponseResultCode switch
			{
				ClanOperationResultCode.Success => ClanRenameResultCode.Success, 
				ClanOperationResultCode.Unchanged => ClanRenameResultCode.Unchanged, 
				ClanOperationResultCode.InvalidName => ClanRenameResultCode.InvalidName, 
				ClanOperationResultCode.IdentityUnavailable => ClanRenameResultCode.IdentityUnavailable, 
				ClanOperationResultCode.IdentityRejected => ClanRenameResultCode.IdentityUnavailable, 
				ClanOperationResultCode.Unauthorized => ClanRenameResultCode.NotLeader, 
				ClanOperationResultCode.ClanChanged => ClanRenameResultCode.ClanChanged, 
				ClanOperationResultCode.NameTaken => ClanRenameResultCode.NameAlreadyExists, 
				ClanOperationResultCode.RateLimited => ClanRenameResultCode.RateLimited, 
				ClanOperationResultCode.Unavailable => ClanRenameResultCode.Failed, 
				_ => ClanRenameResultCode.Failed, 
			};
		}

		private static void CompleteLocal(ClanRenameRequestHandle handle, ClanRenameResultCode code, string requestedName, string message)
		{
			ClanRenameResult clanRenameResult = new ClanRenameResult(handle.RequestId, code, requestedName, message, Current);
			if (handle.Complete(clanRenameResult))
			{
				InvokeSubscribers(ClanApi.RenameCompleted, clanRenameResult, "rename completion");
			}
		}

		private static void Complete(PendingRename pending, ClanRenameResultCode code, string message)
		{
			CompleteLocal(pending.Handle, code, pending.RequestedName, message);
		}

		private static void CompleteAll(ClanRenameResultCode code, string message)
		{
			PendingRename[] array;
			lock (PendingLock)
			{
				array = PendingRenames.Values.ToArray();
				PendingRenames.Clear();
			}
			PendingRename[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Complete(array2[i], code, message);
			}
		}

		private static bool HasSameState(ClanLocalSnapshot left, ClanLocalSnapshot right)
		{
			if (left.IsReady != right.IsReady || !StringComparer.Ordinal.Equals(left.PrimaryClanId, right.PrimaryClanId) || !StringComparer.Ordinal.Equals(left.PrimaryClanName, right.PrimaryClanName) || left.PrimaryRole != right.PrimaryRole || !StringComparer.Ordinal.Equals(left.GuestClanId, right.GuestClanId) || !StringComparer.Ordinal.Equals(left.GuestClanName, right.GuestClanName) || left.SelfRole != right.SelfRole || !HasSameClan(left.Clan, right.Clan) || left.Members.Count != right.Members.Count)
			{
				return false;
			}
			for (int i = 0; i < left.Members.Count; i++)
			{
				ClanMemberInfo clanMemberInfo = left.Members[i];
				ClanMemberInfo clanMemberInfo2 = right.Members[i];
				if (!StringComparer.Ordinal.Equals(clanMemberInfo.PlayerKey, clanMemberInfo2.PlayerKey) || !StringComparer.Ordinal.Equals(clanMemberInfo.Name, clanMemberInfo2.Name) || clanMemberInfo.Role != clanMemberInfo2.Role || clanMemberInfo.IsSelf != clanMemberInfo2.IsSelf || clanMemberInfo.IsOnline != clanMemberInfo2.IsOnline)
				{
					return false;
				}
			}
			return true;
		}

		private static bool HasSameDirectory(IReadOnlyList<ClanInfo> left, IReadOnlyList<ClanInfo> right)
		{
			if (left.Count != right.Count)
			{
				return false;
			}
			for (int i = 0; i < left.Count; i++)
			{
				if (!HasSameClan(left[i], right[i]))
				{
					return false;
				}
			}
			return true;
		}

		private static bool HasSameClan(ClanInfo? left, ClanInfo? right)
		{
			if (left == right)
			{
				return true;
			}
			if (left != null && right != null && StringComparer.Ordinal.Equals(left.ClanId, right.ClanId) && StringComparer.Ordinal.Equals(left.Name, right.Name) && StringComparer.Ordinal.Equals(left.Description, right.Description) && StringComparer.Ordinal.Equals(left.EmblemKey, right.EmblemKey) && StringComparer.Ordinal.Equals(left.LeaderName, right.LeaderName))
			{
				return left.MemberCount == right.MemberCount;
			}
			return false;
		}

		private static void InvokeSubscribers<T>(Action<T>? subscribers, T value, string eventName)
		{
			if (subscribers == null)
			{
				return;
			}
			Delegate[] invocationList = subscribers.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<T> action = (Action<T>)invocationList[i];
				try
				{
					action(value);
				}
				catch (Exception arg)
				{
					ClanPlugin.ClanLogger.LogWarning((object)$"Clan API {eventName} subscriber failed: {arg}");
				}
			}
		}
	}
	internal static class ClanIdentity
	{
		private static readonly Platform SteamPlatform = new Platform("Steam");

		public static ClanPlayerRef FromPeer(ZNetPeer? peer)
		{
			bool retryable;
			return FromPeer(peer, out retryable);
		}

		public static ClanPlayerRef FromPeer(ZNetPeer? peer, out bool retryable)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			retryable = true;
			if (peer == null)
			{
				ClanPlayerRef result = ClanPlayerRef.Local();
				retryable = !result.IsValid;
				return result;
			}
			if (!peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone() || (Object)(object)ZNet.instance == (Object)null)
			{
				return default(ClanPlayerRef);
			}
			if (((ZDOID)(ref peer.m_characterID)).UserID != peer.m_uid)
			{
				retryable = false;
				return default(ClanPlayerRef);
			}
			string text = ResolvePeerPlatformId(peer);
			if (string.IsNullOrWhiteSpace(text))
			{
				return default(ClanPlayerRef);
			}
			long playerId = ReadCharacterPlayerId(peer.m_characterID);
			ClanPlayerRef result2 = new ClanPlayerRef(text, playerId, peer.m_playerName);
			retryable = !result2.IsValid;
			return result2;
		}

		public static ClanPlayerRef FromPlayer(Player player)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null))
			{
				return FromCharacterId(((Character)player).GetZDOID());
			}
			return default(ClanPlayerRef);
		}

		public unsafe static ClanPlayerRef FromPlayerInfo(PlayerInfo info)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (((ZDOID)(ref info.m_characterID)).IsNone())
			{
				return default(ClanPlayerRef);
			}
			return new ClanPlayerRef(((object)(*(PlatformUserID*)(&info.m_userInfo.m_id))/*cast due to .constrained prefix*/).ToString(), ReadCharacterPlayerId(info.m_characterID), info.m_name);
		}

		public unsafe static bool TryMatchRosterPlayer(PlayerInfo info, IReadOnlyList<ClanPlayerSummary> roster, out ClanPlayerRef player)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			player = default(ClanPlayerRef);
			ClanPlayerRef exact = FromPlayerInfo(info);
			if (exact.IsValid && roster.Any((ClanPlayerSummary member) => member.Id == exact.Id))
			{
				player = exact;
				return true;
			}
			string text = ClanDataRules.NormalizePlatformId(((object)(*(PlatformUserID*)(&info.m_userInfo.m_id))/*cast due to .constrained prefix*/).ToString());
			if (text.Length == 0)
			{
				return false;
			}
			ClanPlayerRef clanPlayerRef = default(ClanPlayerRef);
			int num = 0;
			foreach (ClanPlayerSummary item in roster)
			{
				if (item.Player.IsValid && StringComparer.Ordinal.Equals(item.Player.PlatformId, text) && item.IsOnline)
				{
					num++;
					clanPlayerRef = item.Player;
				}
			}
			if (num == 1)
			{
				player = clanPlayerRef;
				return true;
			}
			return false;
		}

		public static ZNetPeer? FindPeer(ClanPlayerRef player)
		{
			if (!player.IsValid)
			{
				return null;
			}
			foreach (ZNetPeer connectedPeer in GetConnectedPeers())
			{
				ClanPlayerRef player2;
				ClanPlayerRef clanPlayerRef = (ClanRpc.TryGetPinnedPeerIdentity(connectedPeer, out player2) ? player2 : FromPeer(connectedPeer));
				if (clanPlayerRef.IsValid && StringComparer.Ordinal.Equals(clanPlayerRef.Id, player.Id))
				{
					return connectedPeer;
				}
			}
			return null;
		}

		public static IEnumerable<ClanPlayerRef> GetOnlinePlayerRefs()
		{
			HashSet<string> yieldedPlayerIds = new HashSet<string>(StringComparer.Ordinal);
			ClanPlayerRef clanPlayerRef = (((Object)(object)Player.m_localPlayer != (Object)null) ? ClanPlayerRef.Local() : default(ClanPlayerRef));
			if (clanPlayerRef.IsValid && yieldedPlayerIds.Add(clanPlayerRef.Id))
			{
				yield return clanPlayerRef;
			}
			foreach (ZNetPeer connectedPeer in GetConnectedPeers())
			{
				ClanPlayerRef player;
				ClanPlayerRef clanPlayerRef2 = (ClanRpc.TryGetPinnedPeerIdentity(connectedPeer, out player) ? player : FromPeer(connectedPeer));
				if (clanPlayerRef2.IsValid && yieldedPlayerIds.Add(clanPlayerRef2.Id))
				{
					yield return clanPlayerRef2;
				}
			}
		}

		private static ClanPlayerRef FromCharacterId(ZDOID characterId)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (characterId == ZDOID.None)
			{
				return default(ClanPlayerRef);
			}
			foreach (PlayerInfo onlinePlayer in GetOnlinePlayers())
			{
				if (onlinePlayer.m_characterID == characterId)
				{
					return FromPlayerInfo(onlinePlayer);
				}
			}
			return default(ClanPlayerRef);
		}

		private static long ReadCharacterPlayerId(ZDOID characterId)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (((ZDOID)(ref characterId)).IsNone() || ZDOMan.instance == null)
			{
				return 0L;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(characterId);
			if (zDO == null)
			{
				return 0L;
			}
			return zDO.GetLong(ZDOVars.s_playerID, 0L);
		}

		private unsafe static string ResolvePeerPlatformId(ZNetPeer peer)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			foreach (PlayerInfo onlinePlayer in GetOnlinePlayers())
			{
				if (onlinePlayer.m_characterID == peer.m_characterID)
				{
					PlatformUserID id = onlinePlayer.m_userInfo.m_id;
					string text = ((object)(*(PlatformUserID*)(&id))/*cast due to .constrained prefix*/).ToString();
					if (!string.IsNullOrWhiteSpace(text))
					{
						return text;
					}
				}
			}
			ISocket socket = peer.m_socket;
			string text2 = ((socket != null) ? socket.GetHostName() : null) ?? "";
			if (string.IsNullOrWhiteSpace(text2) || (Object)(object)ZNet.instance == (Object)null)
			{
				return "";
			}
			return ((object)(((int)ZNet.m_onlineBackend == 0) ? new PlatformUserID(SteamPlatform, text2) : new PlatformUserID(text2))/*cast due to .constrained prefix*/).ToString();
		}

		private static IEnumerable<ZNetPeer> GetConnectedPeers()
		{
			ZNet instance = ZNet.instance;
			IEnumerable<ZNetPeer> enumerable = ((instance != null) ? instance.GetConnectedPeers() : null);
			return enumerable ?? Enumerable.Empty<ZNetPeer>();
		}

		internal static IEnumerable<PlayerInfo> GetOnlinePlayers()
		{
			ZNet instance = ZNet.instance;
			IEnumerable<PlayerInfo> enumerable = ((instance != null) ? instance.GetPlayerList() : null);
			return enumerable ?? Enumerable.Empty<PlayerInfo>();
		}
	}
	internal static class ClanRegistry
	{
		private readonly struct OperationOutcome
		{
			public readonly ClanOperationResultCode Code;

			public readonly string Status;

			public OperationOutcome(ClanOperation