Decompiled source of ValheimSagas v0.3.36

BepInEx/plugins/ValheimSagas/LiteDB.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using LiteDB.Engine;
using LiteDB.Utils;
using LiteDB.Utils.Extensions;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Maurício David")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("MIT")]
[assembly: AssemblyDescription("LiteDB - A lightweight embedded .NET NoSQL document store in a single datafile")]
[assembly: AssemblyFileVersion("5.0.20")]
[assembly: AssemblyInformationalVersion("5.0.20+9843a4e38b4d46d544a3261f9711dbc559c4c4fc")]
[assembly: AssemblyProduct("LiteDB")]
[assembly: AssemblyTitle("LiteDB")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/mbdavid/LiteDB")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("5.0.20.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace LiteDB
{
	public sealed class LiteCollection<T> : ILiteCollection<T>
	{
		private readonly string _collection;

		private readonly ILiteEngine _engine;

		private readonly List<BsonExpression> _includes;

		private readonly BsonMapper _mapper;

		private readonly EntityMapper _entity;

		private readonly MemberMapper _id;

		private readonly BsonAutoId _autoId;

		public string Name => _collection;

		public BsonAutoId AutoId => _autoId;

		public EntityMapper EntityMapper => _entity;

		public int Count()
		{
			return Query().Count();
		}

		public int Count(BsonExpression predicate)
		{
			if (predicate == null)
			{
				throw new ArgumentNullException("predicate");
			}
			return Query().Where(predicate).Count();
		}

		public int Count(string predicate, BsonDocument parameters)
		{
			return Count(BsonExpression.Create(predicate, parameters));
		}

		public int Count(string predicate, params BsonValue[] args)
		{
			return Count(BsonExpression.Create(predicate, args));
		}

		public int Count(Expression<Func<T, bool>> predicate)
		{
			return Count(_mapper.GetExpression(predicate));
		}

		public int Count(Query query)
		{
			return new LiteQueryable<T>(_engine, _mapper, _collection, query).Count();
		}

		public long LongCount()
		{
			return Query().LongCount();
		}

		public long LongCount(BsonExpression predicate)
		{
			if (predicate == null)
			{
				throw new ArgumentNullException("predicate");
			}
			return Query().Where(predicate).LongCount();
		}

		public long LongCount(string predicate, BsonDocument parameters)
		{
			return LongCount(BsonExpression.Create(predicate, parameters));
		}

		public long LongCount(string predicate, params BsonValue[] args)
		{
			return LongCount(BsonExpression.Create(predicate, args));
		}

		public long LongCount(Expression<Func<T, bool>> predicate)
		{
			return LongCount(_mapper.GetExpression(predicate));
		}

		public long LongCount(Query query)
		{
			return new LiteQueryable<T>(_engine, _mapper, _collection, query).Count();
		}

		public bool Exists(BsonExpression predicate)
		{
			if (predicate == null)
			{
				throw new ArgumentNullException("predicate");
			}
			return Query().Where(predicate).Exists();
		}

		public bool Exists(string predicate, BsonDocument parameters)
		{
			return Exists(BsonExpression.Create(predicate, parameters));
		}

		public bool Exists(string predicate, params BsonValue[] args)
		{
			return Exists(BsonExpression.Create(predicate, args));
		}

		public bool Exists(Expression<Func<T, bool>> predicate)
		{
			return Exists(_mapper.GetExpression(predicate));
		}

		public bool Exists(Query query)
		{
			return new LiteQueryable<T>(_engine, _mapper, _collection, query).Exists();
		}

		public BsonValue Min(BsonExpression keySelector)
		{
			if (string.IsNullOrEmpty(keySelector))
			{
				throw new ArgumentNullException("keySelector");
			}
			BsonDocument bsonDocument = Query().OrderBy(keySelector).Select(keySelector).ToDocuments()
				.First();
			return bsonDocument[bsonDocument.Keys.First()];
		}

		public BsonValue Min()
		{
			return Min("_id");
		}

		public K Min<K>(Expression<Func<T, K>> keySelector)
		{
			if (keySelector == null)
			{
				throw new ArgumentNullException("keySelector");
			}
			BsonExpression expression = _mapper.GetExpression(keySelector);
			BsonValue value = Min(expression);
			return (K)_mapper.Deserialize(typeof(K), value);
		}

		public BsonValue Max(BsonExpression keySelector)
		{
			if (string.IsNullOrEmpty(keySelector))
			{
				throw new ArgumentNullException("keySelector");
			}
			BsonDocument bsonDocument = Query().OrderByDescending(keySelector).Select(keySelector).ToDocuments()
				.First();
			return bsonDocument[bsonDocument.Keys.First()];
		}

		public BsonValue Max()
		{
			return Max("_id");
		}

		public K Max<K>(Expression<Func<T, K>> keySelector)
		{
			if (keySelector == null)
			{
				throw new ArgumentNullException("keySelector");
			}
			BsonExpression expression = _mapper.GetExpression(keySelector);
			BsonValue value = Max(expression);
			return (K)_mapper.Deserialize(typeof(K), value);
		}

		public bool Delete(BsonValue id)
		{
			if (id == null || id.IsNull)
			{
				throw new ArgumentNullException("id");
			}
			return _engine.Delete(_collection, new BsonValue[1] { id }) == 1;
		}

		public int DeleteAll()
		{
			return _engine.DeleteMany(_collection, null);
		}

		public int DeleteMany(BsonExpression predicate)
		{
			if (predicate == null)
			{
				throw new ArgumentNullException("predicate");
			}
			return _engine.DeleteMany(_collection, predicate);
		}

		public int DeleteMany(string predicate, BsonDocument parameters)
		{
			return DeleteMany(BsonExpression.Create(predicate, parameters));
		}

		public int DeleteMany(string predicate, params BsonValue[] args)
		{
			return DeleteMany(BsonExpression.Create(predicate, args));
		}

		public int DeleteMany(Expression<Func<T, bool>> predicate)
		{
			return DeleteMany(_mapper.GetExpression(predicate));
		}

		public ILiteQueryable<T> Query()
		{
			return new LiteQueryable<T>(_engine, _mapper, _collection, new Query()).Include(_includes);
		}

		public IEnumerable<T> Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue)
		{
			if (predicate == null)
			{
				throw new ArgumentNullException("predicate");
			}
			return Query().Include(_includes).Where(predicate).Skip(skip)
				.Limit(limit)
				.ToEnumerable();
		}

		public IEnumerable<T> Find(Query query, int skip = 0, int limit = int.MaxValue)
		{
			if (query == null)
			{
				throw new ArgumentNullException("query");
			}
			if (skip != 0)
			{
				query.Offset = skip;
			}
			if (limit != int.MaxValue)
			{
				query.Limit = limit;
			}
			return new LiteQueryable<T>(_engine, _mapper, _collection, query).ToEnumerable();
		}

		public IEnumerable<T> Find(Expression<Func<T, bool>> predicate, int skip = 0, int limit = int.MaxValue)
		{
			return Find(_mapper.GetExpression(predicate), skip, limit);
		}

		public T FindById(BsonValue id)
		{
			if (id == null || id.IsNull)
			{
				throw new ArgumentNullException("id");
			}
			return Find(BsonExpression.Create("_id = @0", id)).FirstOrDefault();
		}

		public T FindOne(BsonExpression predicate)
		{
			return Find(predicate).FirstOrDefault();
		}

		public T FindOne(string predicate, BsonDocument parameters)
		{
			return FindOne(BsonExpression.Create(predicate, parameters));
		}

		public T FindOne(BsonExpression predicate, params BsonValue[] args)
		{
			return FindOne(BsonExpression.Create(predicate, args));
		}

		public T FindOne(Expression<Func<T, bool>> predicate)
		{
			return FindOne(_mapper.GetExpression(predicate));
		}

		public T FindOne(Query query)
		{
			return Find(query).FirstOrDefault();
		}

		public IEnumerable<T> FindAll()
		{
			return Query().Include(_includes).ToEnumerable();
		}

		public ILiteCollection<T> Include<K>(Expression<Func<T, K>> keySelector)
		{
			if (keySelector == null)
			{
				throw new ArgumentNullException("keySelector");
			}
			BsonExpression expression = _mapper.GetExpression(keySelector);
			return Include(expression);
		}

		public ILiteCollection<T> Include(BsonExpression keySelector)
		{
			if (string.IsNullOrEmpty(keySelector))
			{
				throw new ArgumentNullException("keySelector");
			}
			LiteCollection<T> liteCollection = new LiteCollection<T>(_collection, _autoId, _engine, _mapper);
			liteCollection._includes.AddRange(_includes);
			liteCollection._includes.Add(keySelector);
			return liteCollection;
		}

		public bool EnsureIndex(string name, BsonExpression expression, bool unique = false)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentNullException("name");
			}
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			return _engine.EnsureIndex(_collection, name, expression, unique);
		}

		public bool EnsureIndex(BsonExpression expression, bool unique = false)
		{
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			string name = Regex.Replace(expression.Source, "[^a-z0-9]", "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
			return EnsureIndex(name, expression, unique);
		}

		public bool EnsureIndex<K>(Expression<Func<T, K>> keySelector, bool unique = false)
		{
			BsonExpression indexExpression = GetIndexExpression(keySelector);
			return EnsureIndex(indexExpression, unique);
		}

		public bool EnsureIndex<K>(string name, Expression<Func<T, K>> keySelector, bool unique = false)
		{
			BsonExpression indexExpression = GetIndexExpression(keySelector);
			return EnsureIndex(name, indexExpression, unique);
		}

		private BsonExpression GetIndexExpression<K>(Expression<Func<T, K>> keySelector)
		{
			BsonExpression bsonExpression = _mapper.GetIndexExpression(keySelector);
			if (typeof(K).IsEnumerable() && bsonExpression.IsScalar)
			{
				if (bsonExpression.Type != BsonExpressionType.Path)
				{
					throw new LiteException(0, "Expression `" + bsonExpression.Source + "` must return a enumerable expression");
				}
				bsonExpression = bsonExpression.Source + "[*]";
			}
			return bsonExpression;
		}

		public bool DropIndex(string name)
		{
			return _engine.DropIndex(_collection, name);
		}

		public BsonValue Insert(T entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			BsonDocument bsonDocument = _mapper.ToDocument(entity);
			bool flag = RemoveDocId(bsonDocument);
			_engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId);
			BsonValue bsonValue = bsonDocument["_id"];
			if (flag)
			{
				_id.Setter(entity, bsonValue.RawValue);
			}
			return bsonValue;
		}

		public void Insert(BsonValue id, T entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			if (id == null || id.IsNull)
			{
				throw new ArgumentNullException("id");
			}
			BsonDocument bsonDocument = _mapper.ToDocument(entity);
			bsonDocument["_id"] = id;
			_engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId);
		}

		public int Insert(IEnumerable<T> entities)
		{
			if (entities == null)
			{
				throw new ArgumentNullException("entities");
			}
			return _engine.Insert(_collection, GetBsonDocs(entities), _autoId);
		}

		[Obsolete("Use normal Insert()")]
		public int InsertBulk(IEnumerable<T> entities, int batchSize = 5000)
		{
			if (entities == null)
			{
				throw new ArgumentNullException("entities");
			}
			return _engine.Insert(_collection, GetBsonDocs(entities), _autoId);
		}

		private IEnumerable<BsonDocument> GetBsonDocs(IEnumerable<T> documents)
		{
			foreach (T document in documents)
			{
				BsonDocument doc = _mapper.ToDocument(document);
				bool removed = RemoveDocId(doc);
				yield return doc;
				if (removed && _id != null)
				{
					_id.Setter(document, doc["_id"].RawValue);
				}
			}
		}

		private bool RemoveDocId(BsonDocument doc)
		{
			if (_id != null && doc.TryGetValue("_id", out var value) && ((_autoId == BsonAutoId.Int32 && value.IsInt32 && value.AsInt32 == 0) || (_autoId == BsonAutoId.ObjectId && (value.IsNull || (value.IsObjectId && value.AsObjectId == ObjectId.Empty))) || (_autoId == BsonAutoId.Guid && value.IsGuid && value.AsGuid == Guid.Empty) || (_autoId == BsonAutoId.Int64 && value.IsInt64 && value.AsInt64 == 0L)))
			{
				doc.Remove("_id");
				return true;
			}
			return false;
		}

		public bool Update(T entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			BsonDocument bsonDocument = _mapper.ToDocument(entity);
			return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0;
		}

		public bool Update(BsonValue id, T entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			if (id == null || id.IsNull)
			{
				throw new ArgumentNullException("id");
			}
			BsonDocument bsonDocument = _mapper.ToDocument(entity);
			bsonDocument["_id"] = id;
			return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0;
		}

		public int Update(IEnumerable<T> entities)
		{
			if (entities == null)
			{
				throw new ArgumentNullException("entities");
			}
			return _engine.Update(_collection, entities.Select((T x) => _mapper.ToDocument(x)));
		}

		public int UpdateMany(BsonExpression transform, BsonExpression predicate)
		{
			if (transform == null)
			{
				throw new ArgumentNullException("transform");
			}
			if (predicate == null)
			{
				throw new ArgumentNullException("predicate");
			}
			if (transform.Type != BsonExpressionType.Document)
			{
				throw new ArgumentException("Extend expression must return a document. Eg: `col.UpdateMany('{ Name: UPPER(Name) }', 'Age > 10')`");
			}
			return _engine.UpdateMany(_collection, transform, predicate);
		}

		public int UpdateMany(Expression<Func<T, T>> extend, Expression<Func<T, bool>> predicate)
		{
			if (extend == null)
			{
				throw new ArgumentNullException("extend");
			}
			if (predicate == null)
			{
				throw new ArgumentNullException("predicate");
			}
			BsonExpression expression = _mapper.GetExpression(extend);
			BsonExpression expression2 = _mapper.GetExpression(predicate);
			if (expression.Type != BsonExpressionType.Document)
			{
				throw new ArgumentException("Extend expression must return an anonymous class to be merge with entities. Eg: `col.UpdateMany(x => new { Name = x.Name.ToUpper() }, x => x.Age > 10)`");
			}
			return _engine.UpdateMany(_collection, expression, expression2);
		}

		public bool Upsert(T entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			return Upsert(new T[1] { entity }) == 1;
		}

		public int Upsert(IEnumerable<T> entities)
		{
			if (entities == null)
			{
				throw new ArgumentNullException("entities");
			}
			return _engine.Upsert(_collection, GetBsonDocs(entities), _autoId);
		}

		public bool Upsert(BsonValue id, T entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			if (id == null || id.IsNull)
			{
				throw new ArgumentNullException("id");
			}
			BsonDocument bsonDocument = _mapper.ToDocument(entity);
			bsonDocument["_id"] = id;
			return _engine.Upsert(_collection, new BsonDocument[1] { bsonDocument }, _autoId) > 0;
		}

		internal LiteCollection(string name, BsonAutoId autoId, ILiteEngine engine, BsonMapper mapper)
		{
			_collection = name ?? mapper.ResolveCollectionName(typeof(T));
			_engine = engine;
			_mapper = mapper;
			_includes = new List<BsonExpression>();
			if (typeof(T) == typeof(BsonDocument))
			{
				_entity = null;
				_id = null;
				_autoId = autoId;
				return;
			}
			_entity = mapper.GetEntityMapper(typeof(T));
			_id = _entity.Id;
			if (_id != null && _id.AutoId)
			{
				_autoId = ((_id.DataType == typeof(int) || _id.DataType == typeof(int?)) ? BsonAutoId.Int32 : ((_id.DataType == typeof(long) || _id.DataType == typeof(long?)) ? BsonAutoId.Int64 : ((_id.DataType == typeof(Guid) || _id.DataType == typeof(Guid?)) ? BsonAutoId.Guid : BsonAutoId.ObjectId)));
			}
			else
			{
				_autoId = autoId;
			}
		}
	}
	public interface ILiteCollection<T>
	{
		string Name { get; }

		BsonAutoId AutoId { get; }

		EntityMapper EntityMapper { get; }

		ILiteCollection<T> Include<K>(Expression<Func<T, K>> keySelector);

		ILiteCollection<T> Include(BsonExpression keySelector);

		bool Upsert(T entity);

		int Upsert(IEnumerable<T> entities);

		bool Upsert(BsonValue id, T entity);

		bool Update(T entity);

		bool Update(BsonValue id, T entity);

		int Update(IEnumerable<T> entities);

		int UpdateMany(BsonExpression transform, BsonExpression predicate);

		int UpdateMany(Expression<Func<T, T>> extend, Expression<Func<T, bool>> predicate);

		BsonValue Insert(T entity);

		void Insert(BsonValue id, T entity);

		int Insert(IEnumerable<T> entities);

		int InsertBulk(IEnumerable<T> entities, int batchSize = 5000);

		bool EnsureIndex(string name, BsonExpression expression, bool unique = false);

		bool EnsureIndex(BsonExpression expression, bool unique = false);

		bool EnsureIndex<K>(Expression<Func<T, K>> keySelector, bool unique = false);

		bool EnsureIndex<K>(string name, Expression<Func<T, K>> keySelector, bool unique = false);

		bool DropIndex(string name);

		ILiteQueryable<T> Query();

		IEnumerable<T> Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue);

		IEnumerable<T> Find(Query query, int skip = 0, int limit = int.MaxValue);

		IEnumerable<T> Find(Expression<Func<T, bool>> predicate, int skip = 0, int limit = int.MaxValue);

		T FindById(BsonValue id);

		T FindOne(BsonExpression predicate);

		T FindOne(string predicate, BsonDocument parameters);

		T FindOne(BsonExpression predicate, params BsonValue[] args);

		T FindOne(Expression<Func<T, bool>> predicate);

		T FindOne(Query query);

		IEnumerable<T> FindAll();

		bool Delete(BsonValue id);

		int DeleteAll();

		int DeleteMany(BsonExpression predicate);

		int DeleteMany(string predicate, BsonDocument parameters);

		int DeleteMany(string predicate, params BsonValue[] args);

		int DeleteMany(Expression<Func<T, bool>> predicate);

		int Count();

		int Count(BsonExpression predicate);

		int Count(string predicate, BsonDocument parameters);

		int Count(string predicate, params BsonValue[] args);

		int Count(Expression<Func<T, bool>> predicate);

		int Count(Query query);

		long LongCount();

		long LongCount(BsonExpression predicate);

		long LongCount(string predicate, BsonDocument parameters);

		long LongCount(string predicate, params BsonValue[] args);

		long LongCount(Expression<Func<T, bool>> predicate);

		long LongCount(Query query);

		bool Exists(BsonExpression predicate);

		bool Exists(string predicate, BsonDocument parameters);

		bool Exists(string predicate, params BsonValue[] args);

		bool Exists(Expression<Func<T, bool>> predicate);

		bool Exists(Query query);

		BsonValue Min(BsonExpression keySelector);

		BsonValue Min();

		K Min<K>(Expression<Func<T, K>> keySelector);

		BsonValue Max(BsonExpression keySelector);

		BsonValue Max();

		K Max<K>(Expression<Func<T, K>> keySelector);
	}
	public interface ILiteDatabase : IDisposable
	{
		BsonMapper Mapper { get; }

		ILiteStorage<string> FileStorage { get; }

		int UserVersion { get; set; }

		TimeSpan Timeout { get; set; }

		bool UtcDate { get; set; }

		long LimitSize { get; set; }

		int CheckpointSize { get; set; }

		Collation Collation { get; }

		ILiteCollection<T> GetCollection<T>(string name, BsonAutoId autoId = BsonAutoId.ObjectId);

		ILiteCollection<T> GetCollection<T>();

		ILiteCollection<T> GetCollection<T>(BsonAutoId autoId);

		ILiteCollection<BsonDocument> GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId);

		bool BeginTrans();

		bool Commit();

		bool Rollback();

		ILiteStorage<TFileId> GetStorage<TFileId>(string filesCollection = "_files", string chunksCollection = "_chunks");

		IEnumerable<string> GetCollectionNames();

		bool CollectionExists(string name);

		bool DropCollection(string name);

		bool RenameCollection(string oldName, string newName);

		IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null);

		IBsonDataReader Execute(string command, BsonDocument parameters = null);

		IBsonDataReader Execute(string command, params BsonValue[] args);

		void Checkpoint();

		long Rebuild(RebuildOptions options = null);

		BsonValue Pragma(string name);

		BsonValue Pragma(string name, BsonValue value);
	}
	public interface ILiteQueryable<T> : ILiteQueryableResult<T>
	{
		ILiteQueryable<T> Include(BsonExpression path);

		ILiteQueryable<T> Include(List<BsonExpression> paths);

		ILiteQueryable<T> Include<K>(Expression<Func<T, K>> path);

		ILiteQueryable<T> Where(BsonExpression predicate);

		ILiteQueryable<T> Where(string predicate, BsonDocument parameters);

		ILiteQueryable<T> Where(string predicate, params BsonValue[] args);

		ILiteQueryable<T> Where(Expression<Func<T, bool>> predicate);

		ILiteQueryable<T> OrderBy(BsonExpression keySelector, int order = 1);

		ILiteQueryable<T> OrderBy<K>(Expression<Func<T, K>> keySelector, int order = 1);

		ILiteQueryable<T> OrderByDescending(BsonExpression keySelector);

		ILiteQueryable<T> OrderByDescending<K>(Expression<Func<T, K>> keySelector);

		ILiteQueryable<T> GroupBy(BsonExpression keySelector);

		ILiteQueryable<T> Having(BsonExpression predicate);

		ILiteQueryableResult<BsonDocument> Select(BsonExpression selector);

		ILiteQueryableResult<K> Select<K>(Expression<Func<T, K>> selector);
	}
	public interface ILiteQueryableResult<T>
	{
		ILiteQueryableResult<T> Limit(int limit);

		ILiteQueryableResult<T> Skip(int offset);

		ILiteQueryableResult<T> Offset(int offset);

		ILiteQueryableResult<T> ForUpdate();

		BsonDocument GetPlan();

		IBsonDataReader ExecuteReader();

		IEnumerable<BsonDocument> ToDocuments();

		IEnumerable<T> ToEnumerable();

		List<T> ToList();

		T[] ToArray();

		int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId);

		T First();

		T FirstOrDefault();

		T Single();

		T SingleOrDefault();

		int Count();

		long LongCount();

		bool Exists();
	}
	public interface ILiteRepository : IDisposable
	{
		ILiteDatabase Database { get; }

		BsonValue Insert<T>(T entity, string collectionName = null);

		int Insert<T>(IEnumerable<T> entities, string collectionName = null);

		bool Update<T>(T entity, string collectionName = null);

		int Update<T>(IEnumerable<T> entities, string collectionName = null);

		bool Upsert<T>(T entity, string collectionName = null);

		int Upsert<T>(IEnumerable<T> entities, string collectionName = null);

		bool Delete<T>(BsonValue id, string collectionName = null);

		int DeleteMany<T>(BsonExpression predicate, string collectionName = null);

		int DeleteMany<T>(Expression<Func<T, bool>> predicate, string collectionName = null);

		ILiteQueryable<T> Query<T>(string collectionName = null);

		bool EnsureIndex<T>(string name, BsonExpression expression, bool unique = false, string collectionName = null);

		bool EnsureIndex<T>(BsonExpression expression, bool unique = false, string collectionName = null);

		bool EnsureIndex<T, K>(Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null);

		bool EnsureIndex<T, K>(string name, Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null);

		T SingleById<T>(BsonValue id, string collectionName = null);

		List<T> Fetch<T>(BsonExpression predicate, string collectionName = null);

		List<T> Fetch<T>(Expression<Func<T, bool>> predicate, string collectionName = null);

		T First<T>(BsonExpression predicate, string collectionName = null);

		T First<T>(Expression<Func<T, bool>> predicate, string collectionName = null);

		T FirstOrDefault<T>(BsonExpression predicate, string collectionName = null);

		T FirstOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null);

		T Single<T>(BsonExpression predicate, string collectionName = null);

		T Single<T>(Expression<Func<T, bool>> predicate, string collectionName = null);

		T SingleOrDefault<T>(BsonExpression predicate, string collectionName = null);

		T SingleOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null);
	}
	public class LiteDatabase : ILiteDatabase, IDisposable
	{
		private readonly ILiteEngine _engine;

		private readonly BsonMapper _mapper;

		private readonly bool _disposeOnClose;

		private ILiteStorage<string> _fs;

		public BsonMapper Mapper => _mapper;

		public ILiteStorage<string> FileStorage => _fs ?? (_fs = GetStorage<string>());

		public int UserVersion
		{
			get
			{
				return _engine.Pragma("USER_VERSION");
			}
			set
			{
				_engine.Pragma("USER_VERSION", value);
			}
		}

		public TimeSpan Timeout
		{
			get
			{
				return TimeSpan.FromSeconds(_engine.Pragma("TIMEOUT").AsInt32);
			}
			set
			{
				_engine.Pragma("TIMEOUT", (int)value.TotalSeconds);
			}
		}

		public bool UtcDate
		{
			get
			{
				return _engine.Pragma("UTC_DATE");
			}
			set
			{
				_engine.Pragma("UTC_DATE", value);
			}
		}

		public long LimitSize
		{
			get
			{
				return _engine.Pragma("LIMIT_SIZE");
			}
			set
			{
				_engine.Pragma("LIMIT_SIZE", value);
			}
		}

		public int CheckpointSize
		{
			get
			{
				return _engine.Pragma("CHECKPOINT");
			}
			set
			{
				_engine.Pragma("CHECKPOINT", value);
			}
		}

		public Collation Collation => new Collation(_engine.Pragma("COLLATION").AsString);

		public LiteDatabase(string connectionString, BsonMapper mapper = null)
			: this(new ConnectionString(connectionString), mapper)
		{
		}

		public LiteDatabase(ConnectionString connectionString, BsonMapper mapper = null)
		{
			if (connectionString == null)
			{
				throw new ArgumentNullException("connectionString");
			}
			_engine = connectionString.CreateEngine();
			_mapper = mapper ?? BsonMapper.Global;
			_disposeOnClose = true;
		}

		public LiteDatabase(Stream stream, BsonMapper mapper = null, Stream logStream = null)
		{
			EngineSettings settings = new EngineSettings
			{
				DataStream = (stream ?? throw new ArgumentNullException("stream")),
				LogStream = logStream
			};
			_engine = new LiteEngine(settings);
			_mapper = mapper ?? BsonMapper.Global;
			_disposeOnClose = true;
		}

		public LiteDatabase(ILiteEngine engine, BsonMapper mapper = null, bool disposeOnClose = true)
		{
			_engine = engine ?? throw new ArgumentNullException("engine");
			_mapper = mapper ?? BsonMapper.Global;
			_disposeOnClose = disposeOnClose;
		}

		public ILiteCollection<T> GetCollection<T>(string name, BsonAutoId autoId = BsonAutoId.ObjectId)
		{
			return new LiteCollection<T>(name, autoId, _engine, _mapper);
		}

		public ILiteCollection<T> GetCollection<T>()
		{
			return GetCollection<T>(null);
		}

		public ILiteCollection<T> GetCollection<T>(BsonAutoId autoId)
		{
			return GetCollection<T>(null, autoId);
		}

		public ILiteCollection<BsonDocument> GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId)
		{
			if (name.IsNullOrWhiteSpace())
			{
				throw new ArgumentNullException("name");
			}
			return new LiteCollection<BsonDocument>(name, autoId, _engine, _mapper);
		}

		public bool BeginTrans()
		{
			return _engine.BeginTrans();
		}

		public bool Commit()
		{
			return _engine.Commit();
		}

		public bool Rollback()
		{
			return _engine.Rollback();
		}

		public ILiteStorage<TFileId> GetStorage<TFileId>(string filesCollection = "_files", string chunksCollection = "_chunks")
		{
			return new LiteStorage<TFileId>(this, filesCollection, chunksCollection);
		}

		public IEnumerable<string> GetCollectionNames()
		{
			return (from x in GetCollection("$cols").Query().Where("type = 'user'").ToDocuments()
				select x["name"].AsString).ToArray();
		}

		public bool CollectionExists(string name)
		{
			if (name.IsNullOrWhiteSpace())
			{
				throw new ArgumentNullException("name");
			}
			return GetCollectionNames().Contains<string>(name, StringComparer.OrdinalIgnoreCase);
		}

		public bool DropCollection(string name)
		{
			if (name.IsNullOrWhiteSpace())
			{
				throw new ArgumentNullException("name");
			}
			return _engine.DropCollection(name);
		}

		public bool RenameCollection(string oldName, string newName)
		{
			if (oldName.IsNullOrWhiteSpace())
			{
				throw new ArgumentNullException("oldName");
			}
			if (newName.IsNullOrWhiteSpace())
			{
				throw new ArgumentNullException("newName");
			}
			return _engine.RenameCollection(oldName, newName);
		}

		public IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null)
		{
			if (commandReader == null)
			{
				throw new ArgumentNullException("commandReader");
			}
			Tokenizer tokenizer = new Tokenizer(commandReader);
			return new SqlParser(_engine, tokenizer, parameters).Execute();
		}

		public IBsonDataReader Execute(string command, BsonDocument parameters = null)
		{
			if (command == null)
			{
				throw new ArgumentNullException("command");
			}
			Tokenizer tokenizer = new Tokenizer(command);
			return new SqlParser(_engine, tokenizer, parameters).Execute();
		}

		public IBsonDataReader Execute(string command, params BsonValue[] args)
		{
			BsonDocument bsonDocument = new BsonDocument();
			int num = 0;
			foreach (BsonValue value in args)
			{
				bsonDocument[num.ToString()] = value;
				num++;
			}
			return Execute(command, bsonDocument);
		}

		public void Checkpoint()
		{
			_engine.Checkpoint();
		}

		public long Rebuild(RebuildOptions options = null)
		{
			return _engine.Rebuild(options ?? new RebuildOptions());
		}

		public BsonValue Pragma(string name)
		{
			return _engine.Pragma(name);
		}

		public BsonValue Pragma(string name, BsonValue value)
		{
			return _engine.Pragma(name, value);
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		~LiteDatabase()
		{
			Dispose(disposing: false);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (disposing && _disposeOnClose)
			{
				_engine.Dispose();
			}
		}
	}
	public class LiteQueryable<T> : ILiteQueryable<T>, ILiteQueryableResult<T>
	{
		protected readonly ILiteEngine _engine;

		protected readonly BsonMapper _mapper;

		protected readonly string _collection;

		protected readonly Query _query;

		private readonly bool _isSimpleType = Reflection.IsSimpleType(typeof(T));

		internal LiteQueryable(ILiteEngine engine, BsonMapper mapper, string collection, Query query)
		{
			_engine = engine;
			_mapper = mapper;
			_collection = collection;
			_query = query;
		}

		public ILiteQueryable<T> Include<K>(Expression<Func<T, K>> path)
		{
			_query.Includes.Add(_mapper.GetExpression(path));
			return this;
		}

		public ILiteQueryable<T> Include(BsonExpression path)
		{
			_query.Includes.Add(path);
			return this;
		}

		public ILiteQueryable<T> Include(List<BsonExpression> paths)
		{
			_query.Includes.AddRange(paths);
			return this;
		}

		public ILiteQueryable<T> Where(BsonExpression predicate)
		{
			_query.Where.Add(predicate);
			return this;
		}

		public ILiteQueryable<T> Where(string predicate, BsonDocument parameters)
		{
			_query.Where.Add(BsonExpression.Create(predicate, parameters));
			return this;
		}

		public ILiteQueryable<T> Where(string predicate, params BsonValue[] args)
		{
			_query.Where.Add(BsonExpression.Create(predicate, args));
			return this;
		}

		public ILiteQueryable<T> Where(Expression<Func<T, bool>> predicate)
		{
			return Where(_mapper.GetExpression(predicate));
		}

		public ILiteQueryable<T> OrderBy(BsonExpression keySelector, int order = 1)
		{
			if (_query.OrderBy != null)
			{
				throw new ArgumentException("ORDER BY already defined in this query builder");
			}
			_query.OrderBy = keySelector;
			_query.Order = order;
			return this;
		}

		public ILiteQueryable<T> OrderBy<K>(Expression<Func<T, K>> keySelector, int order = 1)
		{
			return OrderBy(_mapper.GetExpression(keySelector), order);
		}

		public ILiteQueryable<T> OrderByDescending(BsonExpression keySelector)
		{
			return OrderBy(keySelector, -1);
		}

		public ILiteQueryable<T> OrderByDescending<K>(Expression<Func<T, K>> keySelector)
		{
			return OrderBy(keySelector, -1);
		}

		public ILiteQueryable<T> GroupBy(BsonExpression keySelector)
		{
			if (_query.GroupBy != null)
			{
				throw new ArgumentException("GROUP BY already defined in this query");
			}
			_query.GroupBy = keySelector;
			return this;
		}

		public ILiteQueryable<T> Having(BsonExpression predicate)
		{
			if (_query.Having != null)
			{
				throw new ArgumentException("HAVING already defined in this query");
			}
			_query.Having = predicate;
			return this;
		}

		public ILiteQueryableResult<BsonDocument> Select(BsonExpression selector)
		{
			_query.Select = selector;
			return new LiteQueryable<BsonDocument>(_engine, _mapper, _collection, _query);
		}

		public ILiteQueryableResult<K> Select<K>(Expression<Func<T, K>> selector)
		{
			if (_query.GroupBy != null)
			{
				throw new ArgumentException("Use Select(BsonExpression selector) when using GroupBy query");
			}
			_query.Select = _mapper.GetExpression(selector);
			return new LiteQueryable<K>(_engine, _mapper, _collection, _query);
		}

		public ILiteQueryableResult<T> ForUpdate()
		{
			_query.ForUpdate = true;
			return this;
		}

		public ILiteQueryableResult<T> Offset(int offset)
		{
			_query.Offset = offset;
			return this;
		}

		public ILiteQueryableResult<T> Skip(int offset)
		{
			return Offset(offset);
		}

		public ILiteQueryableResult<T> Limit(int limit)
		{
			_query.Limit = limit;
			return this;
		}

		public IBsonDataReader ExecuteReader()
		{
			_query.ExplainPlan = false;
			return _engine.Query(_collection, _query);
		}

		public IEnumerable<BsonDocument> ToDocuments()
		{
			using IBsonDataReader reader = ExecuteReader();
			while (reader.Read())
			{
				yield return reader.Current as BsonDocument;
			}
		}

		public IEnumerable<T> ToEnumerable()
		{
			if (_isSimpleType)
			{
				return from x in ToDocuments()
					select x[x.Keys.First()] into x
					select (T)_mapper.Deserialize(typeof(T), x);
			}
			return from x in ToDocuments()
				select (T)_mapper.Deserialize(typeof(T), x);
		}

		public List<T> ToList()
		{
			return ToEnumerable().ToList();
		}

		public T[] ToArray()
		{
			return ToEnumerable().ToArray();
		}

		public BsonDocument GetPlan()
		{
			_query.ExplainPlan = true;
			return _engine.Query(_collection, _query).ToEnumerable().FirstOrDefault()?.AsDocument;
		}

		public T Single()
		{
			return ToEnumerable().Single();
		}

		public T SingleOrDefault()
		{
			return ToEnumerable().SingleOrDefault();
		}

		public T First()
		{
			return ToEnumerable().First();
		}

		public T FirstOrDefault()
		{
			return ToEnumerable().FirstOrDefault();
		}

		public int Count()
		{
			BsonExpression bsonExpression = _query.Select;
			try
			{
				Select("{ count: COUNT(*._id) }");
				return ToDocuments().Single()["count"].AsInt32;
			}
			finally
			{
				_query.Select = bsonExpression;
			}
		}

		public long LongCount()
		{
			BsonExpression bsonExpression = _query.Select;
			try
			{
				Select("{ count: COUNT(*._id) }");
				return ToDocuments().Single()["count"].AsInt64;
			}
			finally
			{
				_query.Select = bsonExpression;
			}
		}

		public bool Exists()
		{
			BsonExpression bsonExpression = _query.Select;
			try
			{
				Select("{ exists: ANY(*._id) }");
				return ToDocuments().Single()["exists"].AsBoolean;
			}
			finally
			{
				_query.Select = bsonExpression;
			}
		}

		public int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId)
		{
			_query.Into = newCollection;
			_query.IntoAutoId = autoId;
			using IBsonDataReader bsonDataReader = ExecuteReader();
			return bsonDataReader.Current.AsInt32;
		}
	}
	public class LiteRepository : ILiteRepository, IDisposable
	{
		private readonly ILiteDatabase _db;

		public ILiteDatabase Database => _db;

		public LiteRepository(ILiteDatabase database)
		{
			_db = database;
		}

		public LiteRepository(string connectionString, BsonMapper mapper = null)
		{
			_db = new LiteDatabase(connectionString, mapper);
		}

		public LiteRepository(ConnectionString connectionString, BsonMapper mapper = null)
		{
			_db = new LiteDatabase(connectionString, mapper);
		}

		public LiteRepository(Stream stream, BsonMapper mapper = null, Stream logStream = null)
		{
			_db = new LiteDatabase(stream, mapper, logStream);
		}

		public BsonValue Insert<T>(T entity, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Insert(entity);
		}

		public int Insert<T>(IEnumerable<T> entities, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Insert(entities);
		}

		public bool Update<T>(T entity, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Update(entity);
		}

		public int Update<T>(IEnumerable<T> entities, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Update(entities);
		}

		public bool Upsert<T>(T entity, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Upsert(entity);
		}

		public int Upsert<T>(IEnumerable<T> entities, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Upsert(entities);
		}

		public bool Delete<T>(BsonValue id, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Delete(id);
		}

		public int DeleteMany<T>(BsonExpression predicate, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).DeleteMany(predicate);
		}

		public int DeleteMany<T>(Expression<Func<T, bool>> predicate, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).DeleteMany(predicate);
		}

		public ILiteQueryable<T> Query<T>(string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Query();
		}

		public bool EnsureIndex<T>(string name, BsonExpression expression, bool unique = false, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).EnsureIndex(name, expression, unique);
		}

		public bool EnsureIndex<T>(BsonExpression expression, bool unique = false, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).EnsureIndex(expression, unique);
		}

		public bool EnsureIndex<T, K>(Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).EnsureIndex(keySelector, unique);
		}

		public bool EnsureIndex<T, K>(string name, Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).EnsureIndex(name, keySelector, unique);
		}

		public T SingleById<T>(BsonValue id, string collectionName = null)
		{
			return _db.GetCollection<T>(collectionName).Query().Where("_id = @0", id)
				.Single();
		}

		public List<T> Fetch<T>(BsonExpression predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).ToList();
		}

		public List<T> Fetch<T>(Expression<Func<T, bool>> predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).ToList();
		}

		public T First<T>(BsonExpression predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).First();
		}

		public T First<T>(Expression<Func<T, bool>> predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).First();
		}

		public T FirstOrDefault<T>(BsonExpression predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).FirstOrDefault();
		}

		public T FirstOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).FirstOrDefault();
		}

		public T Single<T>(BsonExpression predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).Single();
		}

		public T Single<T>(Expression<Func<T, bool>> predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).Single();
		}

		public T SingleOrDefault<T>(BsonExpression predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).SingleOrDefault();
		}

		public T SingleOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null)
		{
			return Query<T>(collectionName).Where(predicate).SingleOrDefault();
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		~LiteRepository()
		{
			Dispose(disposing: false);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				_db.Dispose();
			}
		}
	}
	public class BsonCtorAttribute : Attribute
	{
	}
	public class BsonFieldAttribute : Attribute
	{
		public string Name { get; set; }

		public BsonFieldAttribute(string name)
		{
			Name = name;
		}

		public BsonFieldAttribute()
		{
		}
	}
	public class BsonIdAttribute : Attribute
	{
		public bool AutoId { get; private set; }

		public BsonIdAttribute()
		{
			AutoId = true;
		}

		public BsonIdAttribute(bool autoId)
		{
			AutoId = autoId;
		}
	}
	public class BsonIgnoreAttribute : Attribute
	{
	}
	public class BsonRefAttribute : Attribute
	{
		public string Collection { get; set; }

		public BsonRefAttribute(string collection)
		{
			Collection = collection;
		}

		public BsonRefAttribute()
		{
			Collection = null;
		}
	}
	public class BsonMapper
	{
		private readonly Dictionary<Type, EntityMapper> _entities = new Dictionary<Type, EntityMapper>();

		private readonly ConcurrentDictionary<Type, Func<object, BsonValue>> _customSerializer = new ConcurrentDictionary<Type, Func<object, BsonValue>>();

		private readonly ConcurrentDictionary<Type, Func<BsonValue, object>> _customDeserializer = new ConcurrentDictionary<Type, Func<BsonValue, object>>();

		private readonly Func<Type, object> _typeInstantiator;

		private readonly ITypeNameBinder _typeNameBinder;

		public static BsonMapper Global = new BsonMapper();

		public Func<string, string> ResolveFieldName;

		public Action<Type, MemberInfo, MemberMapper> ResolveMember;

		public Func<Type, string> ResolveCollectionName;

		private readonly Regex _lowerCaseDelimiter = new Regex("(?!(^[A-Z]))([A-Z])", RegexOptions.Compiled);

		private readonly HashSet<Type> _bsonTypes = new HashSet<Type>
		{
			typeof(string),
			typeof(int),
			typeof(long),
			typeof(bool),
			typeof(Guid),
			typeof(DateTime),
			typeof(byte[]),
			typeof(ObjectId),
			typeof(double),
			typeof(decimal)
		};

		private readonly HashSet<Type> _basicTypes = new HashSet<Type>
		{
			typeof(short),
			typeof(ushort),
			typeof(uint),
			typeof(float),
			typeof(char),
			typeof(byte),
			typeof(sbyte)
		};

		public bool SerializeNullValues { get; set; }

		public bool TrimWhitespace { get; set; }

		public bool EmptyStringToNull { get; set; }

		public bool EnumAsInteger { get; set; }

		public bool IncludeFields { get; set; }

		public bool IncludeNonPublic { get; set; }

		public int MaxDepth { get; set; }

		public BsonMapper(Func<Type, object> customTypeInstantiator = null, ITypeNameBinder typeNameBinder = null)
		{
			SerializeNullValues = false;
			TrimWhitespace = true;
			EmptyStringToNull = true;
			EnumAsInteger = false;
			ResolveFieldName = (string s) => s;
			ResolveMember = delegate
			{
			};
			ResolveCollectionName = (Type t) => (!Reflection.IsEnumerable(t)) ? t.Name : Reflection.GetListItemType(t).Name;
			IncludeFields = false;
			MaxDepth = 20;
			_typeInstantiator = customTypeInstantiator ?? ((Func<Type, object>)((Type t) => (object)null));
			_typeNameBinder = typeNameBinder ?? DefaultTypeNameBinder.Instance;
			RegisterType((Uri uri) => uri.IsAbsoluteUri ? uri.AbsoluteUri : uri.ToString(), (BsonValue bson) => new Uri(bson.AsString));
			RegisterType((DateTimeOffset value) => new BsonValue(value.UtcDateTime), (BsonValue bson) => bson.AsDateTime.ToUniversalTime());
			RegisterType((TimeSpan value) => new BsonValue(value.Ticks), (BsonValue bson) => new TimeSpan(bson.AsInt64));
			RegisterType((Regex r) => (r.Options != RegexOptions.None) ? new BsonDocument
			{
				{
					"p",
					r.ToString()
				},
				{
					"o",
					(int)r.Options
				}
			} : new BsonValue(r.ToString()), (BsonValue value) => (!value.IsString) ? new Regex(value.AsDocument["p"].AsString, (RegexOptions)value.AsDocument["o"].AsInt32) : new Regex(value));
		}

		public void RegisterType<T>(Func<T, BsonValue> serialize, Func<BsonValue, T> deserialize)
		{
			_customSerializer[typeof(T)] = (object o) => serialize((T)o);
			_customDeserializer[typeof(T)] = (BsonValue b) => deserialize(b);
		}

		public void RegisterType(Type type, Func<object, BsonValue> serialize, Func<BsonValue, object> deserialize)
		{
			_customSerializer[type] = (object o) => serialize(o);
			_customDeserializer[type] = (BsonValue b) => deserialize(b);
		}

		public EntityBuilder<T> Entity<T>()
		{
			return new EntityBuilder<T>(this, _typeNameBinder);
		}

		public BsonExpression GetExpression<T, K>(Expression<Func<T, K>> predicate)
		{
			return new LinqExpressionVisitor(this, predicate).Resolve(typeof(K) == typeof(bool));
		}

		public BsonExpression GetIndexExpression<T, K>(Expression<Func<T, K>> predicate)
		{
			return new LinqExpressionVisitor(this, predicate).Resolve(predicate: false);
		}

		public BsonMapper UseCamelCase()
		{
			ResolveFieldName = (string s) => char.ToLower(s[0]) + s.Substring(1);
			return this;
		}

		public BsonMapper UseLowerCaseDelimiter(char delimiter = '_')
		{
			ResolveFieldName = (string s) => _lowerCaseDelimiter.Replace(s, delimiter + "$2").ToLower();
			return this;
		}

		internal EntityMapper GetEntityMapper(Type type)
		{
			if (!_entities.TryGetValue(type, out var value))
			{
				lock (_entities)
				{
					if (!_entities.TryGetValue(type, out value))
					{
						return _entities[type] = BuildEntityMapper(type);
					}
				}
			}
			return value;
		}

		protected virtual EntityMapper BuildEntityMapper(Type type)
		{
			EntityMapper entityMapper = new EntityMapper(type);
			Type typeFromHandle = typeof(BsonIdAttribute);
			Type typeFromHandle2 = typeof(BsonIgnoreAttribute);
			Type typeFromHandle3 = typeof(BsonFieldAttribute);
			Type typeFromHandle4 = typeof(BsonRefAttribute);
			IEnumerable<MemberInfo> typeMembers = GetTypeMembers(type);
			MemberInfo idMember = GetIdMember(typeMembers);
			foreach (MemberInfo item in typeMembers)
			{
				if (!CustomAttributeExtensions.IsDefined(item, typeFromHandle2, inherit: true))
				{
					string name = ResolveFieldName(item.Name);
					BsonFieldAttribute bsonFieldAttribute = (BsonFieldAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle3, inherit: true).FirstOrDefault();
					if (bsonFieldAttribute != null && bsonFieldAttribute.Name != null)
					{
						name = bsonFieldAttribute.Name;
					}
					if (item == idMember)
					{
						name = "_id";
					}
					GenericGetter getter = Reflection.CreateGenericGetter(type, item);
					GenericSetter setter = Reflection.CreateGenericSetter(type, item);
					BsonIdAttribute bsonIdAttribute = (BsonIdAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle, inherit: true).FirstOrDefault();
					Type type2 = ((item is PropertyInfo) ? (item as PropertyInfo).PropertyType : (item as FieldInfo).FieldType);
					bool flag = Reflection.IsEnumerable(type2);
					MemberMapper memberMapper = new MemberMapper
					{
						AutoId = (bsonIdAttribute?.AutoId ?? true),
						FieldName = name,
						MemberName = item.Name,
						DataType = type2,
						IsEnumerable = flag,
						UnderlyingType = (flag ? Reflection.GetListItemType(type2) : type2),
						Getter = getter,
						Setter = setter
					};
					BsonRefAttribute bsonRefAttribute = (BsonRefAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle4, inherit: false).FirstOrDefault();
					if (bsonRefAttribute != null && item is PropertyInfo)
					{
						RegisterDbRef(this, memberMapper, _typeNameBinder, bsonRefAttribute.Collection ?? ResolveCollectionName((item as PropertyInfo).PropertyType));
					}
					ResolveMember?.Invoke(type, item, memberMapper);
					if (memberMapper.FieldName != null && !entityMapper.Members.Any((MemberMapper x) => x.FieldName.Equals(name, StringComparison.OrdinalIgnoreCase)) && !memberMapper.IsIgnore)
					{
						entityMapper.Members.Add(memberMapper);
					}
				}
			}
			return entityMapper;
		}

		protected virtual MemberInfo GetIdMember(IEnumerable<MemberInfo> members)
		{
			return Reflection.SelectMember(members, (MemberInfo x) => CustomAttributeExtensions.IsDefined(x, typeof(BsonIdAttribute), inherit: true), (MemberInfo x) => x.Name.Equals("Id", StringComparison.OrdinalIgnoreCase), (MemberInfo x) => x.Name.Equals(x.DeclaringType.Name + "Id", StringComparison.OrdinalIgnoreCase));
		}

		protected virtual IEnumerable<MemberInfo> GetTypeMembers(Type type)
		{
			List<MemberInfo> list = new List<MemberInfo>();
			BindingFlags bindingAttr = (IncludeNonPublic ? (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public));
			list.AddRange((from x in type.GetProperties(bindingAttr)
				where x.CanRead && x.GetIndexParameters().Length == 0
				select x).Select((Func<PropertyInfo, MemberInfo>)((PropertyInfo x) => x)));
			if (IncludeFields)
			{
				list.AddRange((from x in type.GetFields(bindingAttr)
					where !x.Name.EndsWith("k__BackingField") && !x.IsStatic
					select x).Select((Func<FieldInfo, MemberInfo>)((FieldInfo x) => x)));
			}
			return list;
		}

		protected virtual CreateObject GetTypeCtor(EntityMapper mapper)
		{
			Type type = mapper.ForType;
			List<CreateObject> list = new List<CreateObject>();
			bool flag = false;
			ConstructorInfo[] constructors = type.GetConstructors();
			foreach (ConstructorInfo constructorInfo in constructors)
			{
				ParameterInfo[] parameters = constructorInfo.GetParameters();
				if (parameters.Length == 0)
				{
					flag = true;
					continue;
				}
				KeyValuePair<string, Type>[] paramMap = new KeyValuePair<string, Type>[parameters.Length];
				int j;
				for (j = 0; j < parameters.Length; j++)
				{
					ParameterInfo parameterInfo = parameters[j];
					MemberMapper memberMapper = null;
					foreach (MemberMapper member in mapper.Members)
					{
						if (member.MemberName.ToLower() == parameterInfo.Name.ToLower() && member.DataType == parameterInfo.ParameterType)
						{
							memberMapper = member;
							break;
						}
					}
					if (memberMapper == null)
					{
						break;
					}
					paramMap[j] = new KeyValuePair<string, Type>(memberMapper.FieldName, memberMapper.DataType);
				}
				if (j < parameters.Length)
				{
					continue;
				}
				CreateObject createObject = (BsonDocument value) => Activator.CreateInstance(type, paramMap.Select((KeyValuePair<string, Type> x) => Deserialize(x.Value, value[x.Key])).ToArray());
				if (constructorInfo.GetCustomAttribute<BsonCtorAttribute>() != null)
				{
					return createObject;
				}
				list.Add(createObject);
			}
			if (flag)
			{
				return null;
			}
			return list.FirstOrDefault();
		}

		internal static void RegisterDbRef(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection)
		{
			member.IsDbRef = true;
			if (member.IsEnumerable)
			{
				RegisterDbRefList(mapper, member, typeNameBinder, collection);
			}
			else
			{
				RegisterDbRefItem(mapper, member, typeNameBinder, collection);
			}
		}

		private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection)
		{
			EntityMapper entity = mapper.GetEntityMapper(member.DataType);
			member.Serialize = delegate(object obj, BsonMapper m)
			{
				if (obj == null)
				{
					return BsonValue.Null;
				}
				object obj2 = (entity.Id ?? throw new LiteException(0, "There is no _id field mapped in your type: " + member.DataType.FullName)).Getter(obj);
				BsonDocument bsonDocument = new BsonDocument
				{
					["$id"] = m.Serialize(obj2.GetType(), obj2, 0),
					["$ref"] = collection
				};
				if (member.DataType != obj.GetType())
				{
					bsonDocument["$type"] = typeNameBinder.GetName(obj.GetType());
				}
				return bsonDocument;
			};
			member.Deserialize = delegate(BsonValue bson, BsonMapper m)
			{
				if (bson == null || !bson.IsDocument)
				{
					return (object)null;
				}
				BsonDocument asDocument = bson.AsDocument;
				BsonValue value = asDocument["$id"];
				bool num = asDocument["$missing"] == true;
				bool flag = !asDocument.ContainsKey("$ref");
				if (num)
				{
					return (object)null;
				}
				if (flag)
				{
					asDocument["_id"] = value;
					if (asDocument.ContainsKey("$type"))
					{
						asDocument["_type"] = bson["$type"];
					}
					return m.Deserialize(entity.ForType, asDocument);
				}
				return m.Deserialize(entity.ForType, asDocument.ContainsKey("$type") ? new BsonDocument
				{
					["_id"] = value,
					["_type"] = bson["$type"]
				} : new BsonDocument { ["_id"] = value });
			};
		}

		private static void RegisterDbRefList(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection)
		{
			EntityMapper entity = mapper.GetEntityMapper(member.UnderlyingType);
			member.Serialize = delegate(object list, BsonMapper m)
			{
				if (list == null)
				{
					return BsonValue.Null;
				}
				BsonArray bsonArray = new BsonArray();
				MemberMapper id = entity.Id;
				foreach (object item in (IEnumerable)list)
				{
					if (item != null)
					{
						object obj = id.Getter(item);
						BsonDocument bsonDocument = new BsonDocument
						{
							["$id"] = m.Serialize(obj.GetType(), obj, 0),
							["$ref"] = collection
						};
						if (member.UnderlyingType != item.GetType())
						{
							bsonDocument["$type"] = typeNameBinder.GetName(item.GetType());
						}
						bsonArray.Add(bsonDocument);
					}
				}
				return bsonArray;
			};
			member.Deserialize = delegate(BsonValue bson, BsonMapper m)
			{
				if (!bson.IsArray)
				{
					return (object)null;
				}
				BsonArray asArray = bson.AsArray;
				if (asArray.Count == 0)
				{
					return m.Deserialize(member.DataType, asArray);
				}
				BsonArray bsonArray = new BsonArray();
				foreach (BsonValue item2 in asArray)
				{
					if (item2.IsDocument)
					{
						BsonDocument asDocument = item2.AsDocument;
						BsonValue value = asDocument["$id"];
						bool flag = asDocument["$missing"] == true;
						bool flag2 = !asDocument.ContainsKey("$ref");
						if (!flag)
						{
							if (flag2)
							{
								item2["_id"] = value;
								if (item2.AsDocument.ContainsKey("$type"))
								{
									item2["_type"] = item2["$type"];
								}
								bsonArray.Add(item2);
							}
							else
							{
								BsonDocument bsonDocument = new BsonDocument { ["_id"] = value };
								if (item2.AsDocument.ContainsKey("$type"))
								{
									bsonDocument["_type"] = item2["$type"];
								}
								bsonArray.Add(bsonDocument);
							}
						}
					}
				}
				return m.Deserialize(member.DataType, bsonArray);
			};
		}

		public virtual object ToObject(Type type, BsonDocument doc)
		{
			if (doc == null)
			{
				throw new ArgumentNullException("doc");
			}
			if (type == typeof(BsonDocument))
			{
				return doc;
			}
			return Deserialize(type, doc);
		}

		public virtual T ToObject<T>(BsonDocument doc)
		{
			return (T)ToObject(typeof(T), doc);
		}

		public T Deserialize<T>(BsonValue value)
		{
			if (value == null)
			{
				return default(T);
			}
			return (T)Deserialize(typeof(T), value);
		}

		public object Deserialize(Type type, BsonValue value)
		{
			if (value.IsNull)
			{
				return null;
			}
			if (Reflection.IsNullable(type))
			{
				type = Reflection.UnderlyingTypeOf(type);
			}
			if (_customDeserializer.TryGetValue(type, out var value2))
			{
				return value2(value);
			}
			TypeInfo typeInfo = type.GetTypeInfo();
			if (type == typeof(BsonValue))
			{
				return value;
			}
			if (type == typeof(BsonDocument))
			{
				return value.AsDocument;
			}
			if (type == typeof(BsonArray))
			{
				return value.AsArray;
			}
			if (_bsonTypes.Contains(type))
			{
				return value.RawValue;
			}
			if (_basicTypes.Contains(type))
			{
				return Convert.ChangeType(value.RawValue, type);
			}
			if (type == typeof(ulong))
			{
				return (ulong)value.AsInt64;
			}
			if (typeInfo.IsEnum)
			{
				if (value.IsString)
				{
					return Enum.Parse(type, value.AsString);
				}
				if (value.IsNumber)
				{
					return Enum.ToObject(type, value.AsInt32);
				}
			}
			else
			{
				if (value.IsArray)
				{
					if (type == typeof(object))
					{
						return DeserializeArray(typeof(object), value.AsArray);
					}
					if (type.IsArray)
					{
						return DeserializeArray(type.GetElementType(), value.AsArray);
					}
					return DeserializeList(type, value.AsArray);
				}
				if (value.IsDocument)
				{
					if (type.IsAnonymousType())
					{
						return DeserializeAnonymousType(type, value.AsDocument);
					}
					BsonDocument asDocument = value.AsDocument;
					if (asDocument.TryGetValue("_type", out var value3) && value3.IsString)
					{
						Type type2 = _typeNameBinder.GetType(value3.AsString);
						if (type2 == null)
						{
							throw LiteException.InvalidTypedName(value3.AsString);
						}
						if (!type.IsAssignableFrom(type2))
						{
							throw LiteException.DataTypeNotAssignable(type.FullName, type2.FullName);
						}
						if (type2.FullName.Equals("System.Diagnostics.Process", StringComparison.OrdinalIgnoreCase))
						{
							throw LiteException.AvoidUseOfProcess();
						}
						type = type2;
					}
					else if (type == typeof(object))
					{
						type = typeof(Dictionary<string, object>);
					}
					EntityMapper entity = GetEntityMapper(type);
					if (entity.CreateInstance == null)
					{
						entity.CreateInstance = GetTypeCtor(entity) ?? ((CreateObject)((BsonDocument v) => Reflection.CreateInstance(entity.ForType)));
					}
					object obj = _typeInstantiator(type) ?? entity.CreateInstance(asDocument);
					if (obj is IDictionary dict)
					{
						if (obj.GetType().GetTypeInfo().IsGenericType)
						{
							Type k = type.GetGenericArguments()[0];
							Type t = type.GetGenericArguments()[1];
							DeserializeDictionary(k, t, dict, value.AsDocument);
						}
						else
						{
							DeserializeDictionary(typeof(object), typeof(object), dict, value.AsDocument);
						}
					}
					else
					{
						DeserializeObject(entity, obj, asDocument);
					}
					return obj;
				}
			}
			return value.RawValue;
		}

		private object DeserializeArray(Type type, BsonArray array)
		{
			Array array2 = Array.CreateInstance(type, array.Count);
			int num = 0;
			foreach (BsonValue item in array)
			{
				array2.SetValue(Deserialize(type, item), num++);
			}
			return array2;
		}

		private object DeserializeList(Type type, BsonArray value)
		{
			Type listItemType = Reflection.GetListItemType(type);
			IEnumerable enumerable = (IEnumerable)Reflection.CreateInstance(type);
			if (enumerable is IList list)
			{
				foreach (BsonValue item in value)
				{
					list.Add(Deserialize(listItemType, item));
				}
			}
			else
			{
				MethodInfo method = type.GetMethod("Add", new Type[1] { listItemType });
				foreach (BsonValue item2 in value)
				{
					method.Invoke(enumerable, new object[1] { Deserialize(listItemType, item2) });
				}
			}
			return enumerable;
		}

		private void DeserializeDictionary(Type K, Type T, IDictionary dict, BsonDocument value)
		{
			bool isEnum = K.GetTypeInfo().IsEnum;
			foreach (KeyValuePair<string, BsonValue> element in value.GetElements())
			{
				object key = (isEnum ? Enum.Parse(K, element.Key) : ((K == typeof(Uri)) ? new Uri(element.Key) : Convert.ChangeType(element.Key, K)));
				object value2 = Deserialize(T, element.Value);
				dict.Add(key, value2);
			}
		}

		private void DeserializeObject(EntityMapper entity, object obj, BsonDocument value)
		{
			foreach (MemberMapper item in entity.Members.Where((MemberMapper x) => x.Setter != null))
			{
				if (value.TryGetValue(item.FieldName, out var value2))
				{
					if (item.Deserialize != null)
					{
						item.Setter(obj, item.Deserialize(value2, this));
					}
					else
					{
						item.Setter(obj, Deserialize(item.DataType, value2));
					}
				}
			}
		}

		private object DeserializeAnonymousType(Type type, BsonDocument value)
		{
			List<object> list = new List<object>();
			ParameterInfo[] parameters = type.GetConstructors()[0].GetParameters();
			foreach (ParameterInfo parameterInfo in parameters)
			{
				object obj = Deserialize(parameterInfo.ParameterType, value[parameterInfo.Name]);
				if (obj == null && StringComparer.OrdinalIgnoreCase.Equals(parameterInfo.Name, "Id") && value.TryGetValue("_id", out var value2))
				{
					obj = Deserialize(parameterInfo.ParameterType, value2);
				}
				list.Add(obj);
			}
			return Activator.CreateInstance(type, list.ToArray());
		}

		public virtual BsonDocument ToDocument(Type type, object entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			if (entity is BsonDocument)
			{
				return (BsonDocument)entity;
			}
			return Serialize(type, entity, 0).AsDocument;
		}

		public virtual BsonDocument ToDocument<T>(T entity)
		{
			return ToDocument(typeof(T), entity)?.AsDocument;
		}

		public BsonValue Serialize<T>(T obj)
		{
			return Serialize(typeof(T), obj, 0);
		}

		public BsonValue Serialize(Type type, object obj)
		{
			return Serialize(type, obj, 0);
		}

		internal BsonValue Serialize(Type type, object obj, int depth)
		{
			if (++depth > MaxDepth)
			{
				throw LiteException.DocumentMaxDepth(MaxDepth, type);
			}
			if (obj == null)
			{
				return BsonValue.Null;
			}
			if (obj is BsonValue result)
			{
				return result;
			}
			if (_customSerializer.TryGetValue(type, out var value) || _customSerializer.TryGetValue(obj.GetType(), out value))
			{
				return value(obj);
			}
			if (obj is string)
			{
				string text = (TrimWhitespace ? (obj as string).Trim() : ((string)obj));
				if (EmptyStringToNull && text.Length == 0)
				{
					return BsonValue.Null;
				}
				return new BsonValue(text);
			}
			if (obj is int)
			{
				return new BsonValue((int)obj);
			}
			if (obj is long)
			{
				return new BsonValue((long)obj);
			}
			if (obj is double)
			{
				return new BsonValue((double)obj);
			}
			if (obj is decimal)
			{
				return new BsonValue((decimal)obj);
			}
			if (obj is byte[])
			{
				return new BsonValue((byte[])obj);
			}
			if (obj is ObjectId)
			{
				return new BsonValue((ObjectId)obj);
			}
			if (obj is Guid)
			{
				return new BsonValue((Guid)obj);
			}
			if (obj is bool)
			{
				return new BsonValue((bool)obj);
			}
			if (obj is DateTime)
			{
				return new BsonValue((DateTime)obj);
			}
			if (obj is short || obj is ushort || obj is byte || obj is sbyte)
			{
				return new BsonValue(Convert.ToInt32(obj));
			}
			if (obj is uint)
			{
				return new BsonValue(Convert.ToInt64(obj));
			}
			if (obj is ulong)
			{
				return new BsonValue((long)(ulong)obj);
			}
			if (obj is float)
			{
				return new BsonValue(Convert.ToDouble(obj));
			}
			if (obj is char)
			{
				return new BsonValue(obj.ToString());
			}
			if (obj is Enum)
			{
				if (EnumAsInteger)
				{
					return new BsonValue((int)obj);
				}
				return new BsonValue(obj.ToString());
			}
			if (obj is IDictionary dict)
			{
				if (type == typeof(object))
				{
					type = obj.GetType();
				}
				Type type2 = (type.GetTypeInfo().IsGenericType ? type.GetGenericArguments()[1] : typeof(object));
				return SerializeDictionary(type2, dict, depth);
			}
			if (obj is IEnumerable)
			{
				return SerializeArray(Reflection.GetListItemType(type), obj as IEnumerable, depth);
			}
			return SerializeObject(type, obj, depth);
		}

		private BsonArray SerializeArray(Type type, IEnumerable array, int depth)
		{
			BsonArray bsonArray = new BsonArray();
			foreach (object item in array)
			{
				bsonArray.Add(Serialize(type, item, depth));
			}
			return bsonArray;
		}

		private BsonDocument SerializeDictionary(Type type, IDictionary dict, int depth)
		{
			BsonDocument bsonDocument = new BsonDocument();
			foreach (object key in dict.Keys)
			{
				object obj = dict[key];
				string name = key.ToString();
				if (key is DateTime dateTime)
				{
					name = dateTime.ToString("o");
				}
				bsonDocument[name] = Serialize(type, obj, depth);
			}
			return bsonDocument;
		}

		private BsonDocument SerializeObject(Type type, object obj, int depth)
		{
			Type type2 = obj.GetType();
			BsonDocument bsonDocument = new BsonDocument();
			EntityMapper entityMapper = GetEntityMapper(type2);
			if (type != type2)
			{
				bsonDocument["_type"] = new BsonValue(_typeNameBinder.GetName(type2));
			}
			foreach (MemberMapper item in entityMapper.Members.Where((MemberMapper x) => x.Getter != null))
			{
				object obj2 = item.Getter(obj);
				if (obj2 != null || SerializeNullValues || !(item.FieldName != "_id"))
				{
					if (item.Serialize != null)
					{
						bsonDocument[item.FieldName] = item.Serialize(obj2, this);
					}
					else
					{
						bsonDocument[item.FieldName] = Serialize(item.DataType, obj2, depth);
					}
				}
			}
			return bsonDocument;
		}
	}
	public class EntityBuilder<T>
	{
		private readonly BsonMapper _mapper;

		private readonly EntityMapper _entity;

		private readonly ITypeNameBinder _typeNameBinder;

		internal EntityBuilder(BsonMapper mapper, ITypeNameBinder typeNameBinder)
		{
			_mapper = mapper;
			_typeNameBinder = typeNameBinder;
			_entity = mapper.GetEntityMapper(typeof(T));
		}

		public EntityBuilder<T> Ignore<K>(Expression<Func<T, K>> member)
		{
			return GetMember(member, delegate(MemberMapper p)
			{
				_entity.Members.Remove(p);
			});
		}

		public EntityBuilder<T> Field<K>(Expression<Func<T, K>> member, string field)
		{
			if (field.IsNullOrWhiteSpace())
			{
				throw new ArgumentNullException("field");
			}
			return GetMember(member, delegate(MemberMapper p)
			{
				p.FieldName = field;
			});
		}

		public EntityBuilder<T> Id<K>(Expression<Func<T, K>> member, bool autoId = true)
		{
			return GetMember(member, delegate(MemberMapper p)
			{
				MemberMapper memberMapper = _entity.Members.FirstOrDefault((MemberMapper x) => x.FieldName == "_id");
				if (memberMapper != null)
				{
					memberMapper.FieldName = _mapper.ResolveFieldName(memberMapper.MemberName);
					memberMapper.AutoId = false;
				}
				p.FieldName = "_id";
				p.AutoId = autoId;
			});
		}

		public EntityBuilder<T> Ctor(Func<BsonDocument, T> createInstance)
		{
			_entity.CreateInstance = (BsonDocument v) => createInstance(v);
			return this;
		}

		public EntityBuilder<T> DbRef<K>(Expression<Func<T, K>> member, string collection = null)
		{
			return GetMember(member, delegate(MemberMapper p)
			{
				BsonMapper.RegisterDbRef(_mapper, p, _typeNameBinder, collection ?? _mapper.ResolveCollectionName(typeof(K)));
			});
		}

		private EntityBuilder<T> GetMember<TK, K>(Expression<Func<TK, K>> member, Action<MemberMapper> action)
		{
			if (member == null)
			{
				throw new ArgumentNullException("member");
			}
			MemberMapper member2 = _entity.GetMember(member);
			if (member2 == null)
			{
				throw new ArgumentNullException("Member '" + member.GetPath() + "' not found in type '" + _entity.ForType.Name + "' (use IncludeFields in BsonMapper)");
			}
			action(member2);
			return this;
		}
	}
	public class EntityMapper
	{
		public Type ForType { get; }

		public List<MemberMapper> Members { get; } = new List<MemberMapper>();

		public MemberMapper Id => Members.SingleOrDefault((MemberMapper x) => x.FieldName == "_id");

		public CreateObject CreateInstance { get; set; }

		public EntityMapper(Type forType)
		{
			ForType = forType;
		}

		public MemberMapper GetMember(Expression expr)
		{
			return Members.FirstOrDefault((MemberMapper x) => x.MemberName == expr.GetPath());
		}
	}
	internal class LinqExpressionVisitor : ExpressionVisitor
	{
		private static readonly Dictionary<Type, ITypeResolver> _resolver = new Dictionary<Type, ITypeResolver>
		{
			[typeof(BsonValue)] = new BsonValueResolver(),
			[typeof(BsonArray)] = new BsonValueResolver(),
			[typeof(BsonDocument)] = new BsonValueResolver(),
			[typeof(Convert)] = new ConvertResolver(),
			[typeof(DateTime)] = new DateTimeResolver(),
			[typeof(int)] = new NumberResolver("INT32"),
			[typeof(long)] = new NumberResolver("INT64"),
			[typeof(decimal)] = new NumberResolver("DECIMAL"),
			[typeof(double)] = new NumberResolver("DOUBLE"),
			[typeof(ICollection)] = new ICollectionResolver(),
			[typeof(Enumerable)] = new EnumerableResolver(),
			[typeof(Guid)] = new GuidResolver(),
			[typeof(Math)] = new MathResolver(),
			[typeof(Regex)] = new RegexResolver(),
			[typeof(ObjectId)] = new ObjectIdResolver(),
			[typeof(string)] = new StringResolver(),
			[typeof(Nullable)] = new NullableResolver()
		};

		private readonly BsonMapper _mapper;

		private readonly Expression _expr;

		private readonly ParameterExpression _rootParameter;

		private readonly BsonDocument _parameters = new BsonDocument();

		private int _paramIndex;

		private Type _dbRefType;

		private readonly StringBuilder _builder = new StringBuilder();

		private readonly Stack<Expression> _nodes = new Stack<Expression>();

		public LinqExpressionVisitor(BsonMapper mapper, Expression expr)
		{
			_mapper = mapper;
			_expr = expr;
			if (expr is LambdaExpression lambdaExpression)
			{
				_rootParameter = lambdaExpression.Parameters.First();
				return;
			}
			throw new NotSupportedException("Expression " + expr.ToString() + " must be a lambda expression");
		}

		public BsonExpression Resolve(bool predicate)
		{
			Visit(_expr);
			Constants.ENSURE(_nodes.Count == 0, "node stack must be empty when finish expression resolve");
			string text = _builder.ToString();
			try
			{
				BsonExpression bsonExpression = BsonExpression.Create(text, _parameters);
				if (predicate && (bsonExpression.Type == BsonExpressionType.Path || bsonExpression.Type == BsonExpressionType.Call || bsonExpression.Type == BsonExpressionType.Parameter))
				{
					text = "(" + text + " = true)";
					bsonExpression = BsonExpression.Create(text, _parameters);
				}
				return bsonExpression;
			}
			catch (Exception innerException)
			{
				throw new NotSupportedException("Invalid BsonExpression when converted from Linq expression: " + _expr.ToString() + " - `" + text + "`", innerException);
			}
		}

		protected override Expression VisitLambda<T>(Expression<T> node)
		{
			Expression result = base.VisitLambda(node);
			_builder.Length--;
			return result;
		}

		protected override Expression VisitInvocation(InvocationExpression node)
		{
			Expression result = base.VisitInvocation(node);
			_builder.Length--;
			return result;
		}

		protected override Expression VisitParameter(ParameterExpression node)
		{
			_builder.Append(_rootParameter.Equals(node) ? "$" : "@");
			return base.VisitParameter(node);
		}

		protected override Expression VisitMember(MemberExpression node)
		{
			bool flag = ParameterExpressionVisitor.Test(node);
			MemberInfo member = node.Member;
			if (TryGetResolver(member.DeclaringType, out var typeResolver))
			{
				string text = typeResolver.ResolveMember(member);
				if (text == null)
				{
					throw new NotSupportedException("Member " + member.Name + " are not support in " + member.DeclaringType.Name + " when convert to BsonExpression (" + node.ToString() + ").");
				}
				ResolvePattern(text, node.Expression, new Expression[0]);
			}
			else if (node.Expression != null)
			{
				_nodes.Push(node);
				base.Visit(node.Expression);
				if (flag)
				{
					string value = ResolveMember(member);
					_builder.Append(value);
				}
			}
			else
			{
				object value2 = Evaluate(node);
				base.Visit(Expression.Constant(value2));
			}
			if (_nodes.Count > 0)
			{
				_nodes.Pop();
			}
			return node;
		}

		protected override Expression VisitMethodCall(MethodCallExpression node)
		{
			if (IsMethodIndexEval(node, out var obj, out var idx))
			{
				Visit(obj);
				object obj2 = Evaluate(idx, typeof(string), typeof(int));
				if (obj2 is string)
				{
					_builder.Append(".");
					_builder.Append($"['{obj2}']");
				}
				else
				{
					_builder.Append($"[{obj2}]");
				}
				return node;
			}
			if (!TryGetResolver(node.Method.DeclaringType, out var typeResolver))
			{
				if (ParameterExpressionVisitor.Test(node))
				{
					throw new NotSupportedException("Method " + node.Method.Name + " not available to convert to BsonExpression (" + node.ToString() + ").");
				}
				object value = Evaluate(node);
				base.Visit(Expression.Constant(value));
				return node;
			}
			string text = typeResolver.ResolveMethod(node.Method);
			if (text == null)
			{
				throw new NotSupportedException("Method " + Reflection.MethodName(node.Method) + " in " + node.Method.DeclaringType.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ").");
			}
			ResolvePattern(text, node.Object, node.Arguments);
			return node;
		}

		protected override Expression VisitConstant(ConstantExpression node)
		{
			object value = node.Value;
			while (_nodes.Count > 0 && _nodes.Peek() is MemberExpression memberExpression)
			{
				if (memberExpression.Member is FieldInfo fieldInfo)
				{
					value = fieldInfo.GetValue(value);
				}
				else if (memberExpression.Member is PropertyInfo propertyInfo)
				{
					value = propertyInfo.GetValue(value);
				}
				_nodes.Pop();
			}
			Constants.ENSURE(_nodes.Count == 0, "counter stack must be zero to eval all properties/field over object");
			string text = "p" + _paramIndex++;
			_builder.AppendFormat("@" + text);
			Type type = value?.GetType();
			BsonValue value2 = ((type == null) ? BsonValue.Null : ((type == typeof(string)) ? new BsonValue((string)value) : _mapper.Serialize(value.GetType(), value)));
			_parameters[text] = value2;
			return node;
		}

		protected override Expression VisitUnary(UnaryExpression node)
		{
			if (node.NodeType == ExpressionType.Not)
			{
				if (node.Operand.NodeType == ExpressionType.MemberAccess)
				{
					_builder.Append("(");
					Visit(node.Operand);
					_builder.Append(" = false)");
				}
				else
				{
					_builder.Append("(");
					Visit(node.Operand);
					_builder.Append(")");
					_builder.Append(" = false");
				}
			}
			else if (node.NodeType == ExpressionType.Convert)
			{
				Type fromType = node.Operand.Type;
				Type type = node.Type;
				if ((fromType == typeof(double) || fromType == typeof(decimal)) && (type == typeof(int) || type == typeof(long)))
				{
					string methodName = "To" + type.Name.ToString();
					MethodInfo methodInfo = (from x in typeof(Convert).GetMethods()
						where x.Name == methodName
						where x.GetParameters().Length == 1 && x.GetParameters().Any((ParameterInfo z) => z.ParameterType == fromType)
						select x).FirstOrDefault();
					if (methodInfo == null)
					{
						throw new NotSupportedException("Cast from " + fromType.Name + " are not supported when convert to BsonExpression");
					}
					MethodCallExpression node2 = Expression.Call(null, methodInfo, node.Operand);
					VisitMethodCall(node2);
				}
				else
				{
					base.VisitUnary(node);
				}
			}
			else if (node.NodeType == ExpressionType.ArrayLength)
			{
				_builder.Append("LENGTH(");
				Visit(node.Operand);
				_builder.Append(")");
			}
			else
			{
				base.VisitUnary(node);
			}
			return node;
		}

		protected override Expression VisitNew(NewExpression node)
		{
			if (node.Members == null)
			{
				if (!TryGetResolver(node.Type, out var typeResolver))
				{
					throw new NotSupportedException($"New instance are not supported for {node.Type} when convert to BsonExpression ({node.ToString()}).");
				}
				string text = typeResolver.ResolveCtor(node.Constructor);
				if (text == null)
				{
					throw new NotSupportedException("Constructor for " + node.Type.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ").");
				}
				ResolvePattern(text, null, node.Arguments);
			}
			else
			{
				_builder.Append("{ ");
				for (int i = 0; i < node.Members.Count; i++)
				{
					MemberInfo memberInfo = node.Members[i];
					_builder.Append((i > 0) ? ", " : "");
					_builder.AppendFormat("'{0}': ", memberInfo.Name);
					Visit(node.Arguments[i]);
				}
				_builder.Append(" }");
			}
			return node;
		}

		protected override Expression VisitMemberInit(MemberInitExpression node)
		{
			if (node.NewExpression.Constructor.GetParameters().Length != 0)
			{
				throw new NotSupportedException($"New instance of {node.Type} are not supported because contains ctor with parameter. Try use only property initializers: `new {node.Type.Name} {{ PropA = 1, PropB == \"John\" }}`.");
			}
			_builder.Append("{");
			for (int i = 0; i < node.Bindings.Count; i++)
			{
				MemberAssignment memberAssignment = node.Bindings[i] as MemberAssignment;
				string text = ResolveMember(memberAssignment.Member);
				_builder.Append((i > 0) ? ", " : "");
				_builder.Append(text.Substring(1));
				_builder.Append(":");
				Visit(memberAssignment.Expression);
			}
			_builder.Append("}");
			return node;
		}

		protected override Expression VisitNewArray(NewArrayExpression node)
		{
			_builder.Append("[ ");
			for (int i = 0; i < node.Expressions.Count; i++)
			{
				_builder.Append((i > 0) ? ", " : "");
				Visit(node.Expressions[i]);
			}
			_builder.Append(" ]");
			return node;
		}

		protected override Expression VisitBinary(BinaryExpression node)
		{
			bool ensurePredicate = node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.OrElse;
			if (node.NodeType == ExpressionType.Coalesce)
			{
				return VisitCoalesce(node);
			}
			if (node.NodeType == ExpressionType.ArrayIndex)
			{
				return VisitArrayIndex(node);
			}
			string value = GetOperator(node.NodeType);
			_builder.Append("(");
			VisitAsPredicate(node.Left, ensurePredicate);
			_builder.Append(value);
			if (!_mapper.EnumAsInteger && node.Left.NodeType == ExpressionType.Convert && node.Left is UnaryExpression unaryExpression && unaryExpression.Operand.Type.GetTypeInfo().IsEnum && unaryExpression.Type == typeof(int))
			{
				VisitAsPredicate(Expression.Constant(Enum.GetName(unaryExpression.Operand.Type, Evaluate(node.Right))), ensurePredicate);
			}
			else
			{
				VisitAsPredicate(node.Right, ensurePredicate);
			}
			_builder.Append(")");
			return node;
		}

		protected override Expression VisitConditional(ConditionalExpression node)
		{
			_builder.Append("IIF(");
			Visit(node.Test);
			_builder.Append(", ");
			Visit(node.IfTrue);
			_builder.Append(", ");
			Visit(node.IfFalse);
			_builder.Append(")");
			return node;
		}

		private Expression VisitCoalesce(BinaryExpression node)
		{
			_builder.Append("COALESCE(");
			Visit(node.Left);
			_builder.Append(", ");
			Visit(node.Right);
			_builder.Append(")");
			return node;
		}

		private Expression VisitArrayIndex(BinaryExpression node)
		{
			Visit(node.Left);
			_builder.Append("[");
			object value = Evaluate(node.Right, typeof(int));
			_builder.Append(value);
			_builder.Append("]");
			return node;
		}

		private void ResolvePattern(string pattern, Expression obj, IList<Expression> args)
		{
			Tokenizer tokenizer = new Tokenizer(pattern);
			while (!tokenizer.EOF)
			{
				Token token = tokenizer.ReadToken(eatWhitespace: false);
				if (token.Type == TokenType.Hashtag)
				{
					Visit(obj);
				}
				else if (token.Type == TokenType.At && tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Int)
				{
					int index = Convert.ToInt32(tokenizer.ReadToken(eatWhitespace: false).Expect(TokenType.Int).Value);
					Visit(args[index]);
				}
				else if (token.Type == TokenType.Percent)
				{
					VisitEnumerablePredicate(args[1] as LambdaExpression);
				}
				else
				{
					_builder.Append((token.Type == TokenType.String) ? ("'" + token.Value + "'") : token.Value);
				}
			}
		}

		private void VisitEnumerablePredicate(LambdaExpression lambda)
		{
			Expression body = lambda.Body;
			if (body is BinaryExpression binaryExpression)
			{
				if (binaryExpression.Left.NodeType != ExpressionType.Parameter)
				{
					throw new LiteException(0, "Any/All requires simple parameter on left side. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`");
				}
				string value = GetOperator(binaryExpression.NodeType);
				_builder.Append(value);
				VisitAsPredicate(binaryExpression.Right, ensurePredicate: false);
				return;
			}
			if (body is MethodCallExpression methodCallExpression)
			{
				if (methodCallExpression.Object.NodeType != ExpressionType.Parameter)
				{
					throw new NotSupportedException("Any/All requires simple parameter on left side. Eg: `x.Customers.Select(c => c.Name).Any(n => n.StartsWith('J'))`");
				}
				if (!TryGetResolver(methodCallExpression.Method.DeclaringType, out var typeResolver))
				{
					throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call.");
				}
				string text = typeResolver.ResolveMethod(methodCallExpression.Method);
				if (text == null || !text.StartsWith("#"))
				{
					throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call.");
				}
				ResolvePattern(text.Substring(1), methodCallExpression.Object, methodCallExpression.Arguments);
				return;
			}
			throw new LiteException(0, "When using Any/All method test do only simple predicate variable. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`");
		}

		private string GetOperator(ExpressionType nodeType)
		{
			return nodeType switch
			{
				ExpressionType.Add => " + ", 
				ExpressionType.Multiply => " * ", 
				ExpressionType.Subtract => " - ", 
				ExpressionType.Divide => " / ", 
				ExpressionType.Equal => " = ", 
				ExpressionType.NotEqual => " != ", 
				ExpressionType.GreaterThan => " > ", 
				ExpressionType.GreaterThanOrEqual => " >= ", 
				ExpressionType.LessThan => " < ", 
				ExpressionType.LessThanOrEqual => " <= ", 
				ExpressionType.And => " AND ", 
				ExpressionType.AndAlso => " AND ", 
				ExpressionType.Or => " OR ", 
				ExpressionType.OrElse => " OR ", 
				_ => throw new NotSupportedException($"Operator not supported {nodeType}"), 
			};
		}

		private string ResolveMember(MemberInfo member)
		{
			string name = member.Name;
			bool flag = _dbRefType != null && member.DeclaringType.IsAssignableFrom(_dbRefType);
			MemberMapper memberMapper = _mapper.GetEntityMapper(member.DeclaringType).Members.FirstOrDefault((MemberMapper x) => x.MemberName == name);
			if (memberMapper == null)
			{
				throw new NotSupportedException($"Member {name} not found on BsonMapper for type {member.DeclaringType}.");
			}
			_dbRefType = (memberMapper.IsDbRef ? memberMapper.UnderlyingType : null);
			return "." + ((flag && memberMapper.FieldName == "_id") ? "$id" : memberMapper.FieldName);
		}

		private bool IsMethodIndexEval(MethodCallExpression node, out Expression obj, out Expression idx)
		{
			MethodInfo method = node.Method;
			_ = method.DeclaringType;
			ParameterInfo[] parameters = method.GetParameters();
			if (method.Name == "get_Item" && parameters.Length == 1 && (parameters[0].ParameterType == typeof(int) || parameters[0].ParameterType == typeof(string)))
			{
				obj = node.Object;
				idx = node.Arguments[0];
				return true;
			}
			obj = null;
			idx = null;
			return false;
		}

		private void VisitAsPredicate(Expression expr, bool ensurePredicate)
		{
			ensurePredicate = ensurePredicate && (expr.NodeType == ExpressionType.MemberAccess || expr.NodeType == ExpressionType.Call || expr.NodeType == ExpressionType.Invoke || expr.NodeType == ExpressionType.Constant);
			if (ensurePredicate)
			{
				_builder.Append("(");
				_builder.Append("(");
				base.Visit(expr);
				_builder.Append(")");
				_builder.Append(" = true)");
			}
			else
			{
				base.Visit(expr);
			}
		}

		private object Evaluate(Expression expr, params Type[] validTypes)
		{
			object value = null;
			if (expr.NodeType == ExpressionType.Constant)
			{
				ConstantExpression constantExpression = (ConstantExpression)expr;
				value = constantExpression.Value;
			}
			else
			{
				Delegate obj = Expression.Lambda(expr).Compile();
				value = obj.DynamicInvoke();
			}
			if (validTypes.Length != 0 && value == null)
			{
				throw new NotSupportedException($"Expression {expr} can't return null value");
			}
			if (validTypes.Length != 0 && !validTypes.Any((Type x) => x == value.GetType()))
			{
				throw new NotSupportedException(string.Format("Expression {0} must return on of this types: {1}", expr, string.Join(", ", validTypes.Select((Type x) => "`" + x.Name + "`"))));
			}
			return value;
		}

		private bool TryGetResolver(Type declaringType, out ITypeResolver typeResolver)
		{
			bool num = Reflection.IsCollection(declaringType);
			bool flag = Reflection.IsEnumerable(declaringType);
			bool flag2 = Reflection.IsNullable(declaringType);
			Type key = (num ? typeof(ICollection) : (flag ? typeof(Enumerable) : (flag2 ? typeof(Nullable) : declaringType)));
			return _resolver.TryGetValue(key, out typeResolver);
		}
	}
	internal class ParameterExpressionVisitor : ExpressionVisitor
	{
		public bool IsParameter { get; private set; }

		protected override Expression VisitParameter(ParameterExpression node)
		{
			IsParameter = true;
			return base.VisitParameter(node);
		}

		public static bool Test(Expression node)
		{
			ParameterExpressionVisitor parameterExpressionVisitor = new ParameterExpressionVisitor();
			parameterExpressionVisitor.Visit(node);
			return parameterExpressionVisitor.IsParameter;
		}
	}
	internal class BsonValueResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			return null;
		}

		public string ResolveMember(MemberInfo member)
		{
			switch (member.Name)
			{
			case "AsInt32":
			case "AsInt64":
			case "AsArray":
			case "AsDateTime":
			case "AsDocument":
			case "AsObjectId":
			case "AsString":
			case "AsBinary":
			case "AsDouble":
			case "AsBoolean":
			case "AsDecimal":
			case "AsGuid":
				return "#";
			case "IsNull":
				return "IS_NULL(#)";
			case "IsArray":
				return "IS_ARRAY(#)";
			case "IsDocument":
				return "IS_DOCUMENT(#)";
			case "IsInt32":
				return "IS_INT32(#)";
			case "IsInt64":
				return "IS_INT64(#)";
			case "IsDouble":
				return "IS_DOUBLE(#)";
			case "IsDecimal":
				return "IS_DECIMAL(#)";
			case "IsNumber":
				return "IS_NUMBER(#)";
			case "IsBinary":
				return "IS_BINARY(#)";
			case "IsBoolean":
				return "IS_BOOLEAN(#)";
			case "IsString":
				return "IS_STRING(#)";
			case "IsObjectId":
				return "IS_OBJECTID(#)";
			case "IsGuid":
				return "IS_GUID(#)";
			case "IsDateTime":
				return "IS_DATETIME(#)";
			case "IsMinValue":
				return "IS_MINVALUE(#)";
			case "IsMaxValue":
				return "IS_MAXVALUE(#)";
			default:
				return null;
			}
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	internal class ConvertResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			return method.Name switch
			{
				"ToInt32" => "INT32(@0)", 
				"ToInt64" => "INT64(@0)", 
				"ToDouble" => "DOUBLE(@0)", 
				"ToDecimal" => "DECIMAL(@0)", 
				"ToDateTime" => "DATE(@0)", 
				"FromBase64String" => "BINARY(@0)", 
				"ToBoolean" => "BOOL(@0)", 
				"ToString" => "STRING(@0)", 
				_ => null, 
			};
		}

		public string ResolveMember(MemberInfo member)
		{
			return null;
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	internal class DateTimeResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			switch (method.Name)
			{
			case "AddYears":
				return "DATEADD('y', @0, #)";
			case "AddMonths":
				return "DATEADD('M', @0, #)";
			case "AddDays":
				return "DATEADD('d', @0, #)";
			case "AddHours":
				return "DATEADD('h', @0, #)";
			case "AddMinutes":
				return "DATEADD('m', @0, #)";
			case "AddSeconds":
				return "DATEADD('s', @0, #)";
			case "ToString":
			{
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length == 0)
				{
					return "STRING(#)";
				}
				if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
				{
					return "FORMAT(#, @0)";
				}
				break;
			}
			case "ToUniversalTime":
				return "TO_UTC(#)";
			case "Parse":
				return "DATETIME(@0)";
			case "Equals":
				return "# = @0";
			}
			return null;
		}

		public string ResolveMember(MemberInfo member)
		{
			return member.Name switch
			{
				"Now" => "NOW()", 
				"UtcNow" => "NOW_UTC()", 
				"Today" => "TODAY()", 
				"Year" => "YEAR(#)", 
				"Month" => "MONTH(#)", 
				"Day" => "DAY(#)", 
				"Hour" => "HOUR(#)", 
				"Minute" => "MINUTE(#)", 
				"Second" => "SECOND(#)", 
				"Date" => "DATETIME(YEAR(#), MONTH(#), DAY(#))", 
				"ToLocalTime" => "TO_LOCAL(#)", 
				"ToUniversalTime" => "TO_UTC(#)", 
				_ => null, 
			};
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			ParameterInfo[] parameters = ctor.GetParameters();
			if (parameters.Length == 3 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(int))
			{
				return "DATETIME(@0, @1, @2)";
			}
			return null;
		}
	}
	internal class EnumerableResolver : ITypeResolver
	{
		public virtual string ResolveMethod(MethodInfo method)
		{
			switch (Reflection.MethodName(method, 1))
			{
			case "AsEnumerable()":
				return "@0[*]";
			case "get_Item(int)":
				return "#[@0]";
			case "ElementAt(int)":
				return "@0[@1]";
			case "Single()":
			case "First()":
			case "SingleOrDefault()":
			case "FirstOrDefault()":
				return "@0[0]";
			case "Last()":
			case "LastOrDefault()":
				return "@0[-1]";
			case "Single(Func<T,TResult>)":
			case "First(Func<T,TResult>)":
			case "SingleOrDefault(Func<T,TResult>)":
			case "FirstOrDefault(Func<T,TResult>)":
				return "FIRST(FILTER(@0 => @1))";
			case "Last(Func<T,TResult>)":
			case "LastOrDefault(Func<T,TResult>)":
				return "LAST(FILTER(@0 => @1))";
			case "Where(Func<T,TResult>)":
				return "FILTER(@0 => @1)";
			case "Select(Func<T,TResult>)":
				return "MAP(@0 => @1)";
			case "Count()":
				return "COUNT(@0)";
			case "Sum()":
				return "SUM(@0)";
			case "Average()":
				return "AVG(@0)";
			case "Max()":
				return "MAX(@0)";
			case "Min()":
				return "MIN(@0)";
			case "Count(Func<T,TResult>)":
				return "COUNT(FILTER(@0 => @1))";
			case "Sum(Func<T,TResult>)":
				return "SUM(MAP(@0 => @1))";
			case "Average(Func<T,TResult>)":
				return "AVG(MAP(@0 => @1))";
			case "Max(Func<T,TResult>)":
				return "MAX(MAP(@0 => @1))";
			case "Min(Func<T,TResult>)":
				return "MIN(MAP(@0 => @1))";
			case "ToList()":
			case "ToArray()":
				return "ARRAY(@0)";
			case "Any(Func<T,TResult>)":
				return "@0 ANY %";
			case "All(Func<T,TResult>)":
				return "@0 ALL %";
			case "Any()":
				return "COUNT(@0) > 0";
			default:
				if (method.Name == "Contains")
				{
					return "@0 ANY = @1";
				}
				return null;
			}
		}

		public virtual string ResolveMember(MemberInfo member)
		{
			string name = member.Name;
			if (!(name == "Length"))
			{
				if (name == "Count")
				{
					return "COUNT(#)";
				}
				return null;
			}
			return "LENGTH(#)";
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	internal class GuidResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			return method.Name switch
			{
				"ToString" => "STRING(#)", 
				"NewGuid" => "GUID()", 
				"Parse" => "GUID(@0)", 
				"TryParse" => throw new NotSupportedException("There is no TryParse translate. Use Guid.Parse()"), 
				"Equals" => "# = @0", 
				_ => null, 
			};
		}

		public string ResolveMember(MemberInfo member)
		{
			if (member.Name == "Empty")
			{
				return "GUID('00000000-0000-0000-0000-000000000000')";
			}
			return null;
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			ParameterInfo[] parameters = ctor.GetParameters();
			if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
			{
				return "GUID(@0)";
			}
			return null;
		}
	}
	internal class ICollectionResolver : EnumerableResolver
	{
		public override string ResolveMethod(MethodInfo method)
		{
			if (method.Name == "Contains")
			{
				return "# ANY = @0";
			}
			return base.ResolveMethod(method);
		}
	}
	internal interface ITypeResolver
	{
		string ResolveMethod(MethodInfo method);

		string ResolveMember(MemberInfo member);

		string ResolveCtor(ConstructorInfo ctor);
	}
	internal class MathResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			int num = method.GetParameters().Length;
			switch (method.Name)
			{
			case "Abs":
				return "ABS(@0)";
			case "Pow":
				return "POW(@0, @1)";
			case "Round":
				if (num != 2)
				{
					throw new ArgumentOutOfRangeException("Method Round need 2 arguments when convert to BsonExpression");
				}
				return "ROUND(@0, @1)";
			default:
				return null;
			}
		}

		public string ResolveMember(MemberInfo member)
		{
			return null;
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	internal class NullableResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			return null;
		}

		public string ResolveMember(MemberInfo member)
		{
			string name = member.Name;
			if (!(name == "HasValue"))
			{
				if (name == "Value")
				{
					return "#";
				}
				return null;
			}
			return "(IS_NULL(#) = false)";
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	internal class NumberResolver : ITypeResolver
	{
		private readonly string _parseMethod;

		public NumberResolver(string parseMethod)
		{
			_parseMethod = parseMethod;
		}

		public string ResolveMethod(MethodInfo method)
		{
			switch (method.Name)
			{
			case "ToString":
			{
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length == 0)
				{
					return "STRING(#)";
				}
				if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
				{
					return "FORMAT(#, @0)";
				}
				break;
			}
			case "Parse":
				return _parseMethod + "(@0)";
			case "Equals":
				return "# = @0";
			}
			return null;
		}

		public string ResolveMember(MemberInfo member)
		{
			return null;
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	internal class ObjectIdResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			string name = method.Name;
			if (!(name == "ToString"))
			{
				if (name == "Equals")
				{
					return "# = @0";
				}
				return null;
			}
			return "STRING(#)";
		}

		public string ResolveMember(MemberInfo member)
		{
			string name = member.Name;
			if (!(name == "Empty"))
			{
				if (name == "CreationTime")
				{
					return "OID_CREATIONTIME(#)";
				}
				return null;
			}
			return "OBJECTID('000000000000000000000000')";
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			ParameterInfo[] parameters = ctor.GetParameters();
			if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
			{
				return "OBJECTID(@0)";
			}
			return null;
		}
	}
	internal class RegexResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			string name = method.Name;
			if (!(name == "Split"))
			{
				if (name == "IsMatch")
				{
					return "IS_MATCH(@0, @1)";
				}
				return null;
			}
			return "SPLIT(@0, @1, true)";
		}

		public string ResolveMember(MemberInfo member)
		{
			return null;
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	internal class StringResolver : ITypeResolver
	{
		public string ResolveMethod(MethodInfo method)
		{
			int num = method.GetParameters().Length;
			switch (method.Name)
			{
			case "Count":
				return "LENGTH(#)";
			case "Trim":
				return "TRIM(#)";
			case "TrimStart":
				return "LTRIM(#)";
			case "TrimEnd":
				return "RTRIM(#)";
			case "ToUpper":
				return "UPPER(#)";
			case "ToUpperInvariant":
				return "UPPER(#)";
			case "ToLower":
				return "LOWER(#)";
			case "ToLowerInvariant":
				return "LOWER(#)";
			case "Replace":
				return "REPLACE(#, @0, @1)";
			case "PadLeft":
				return "LPAD(#, @0, @1)";
			case "RightLeft":
				return "RPAD(#, @0, @1)";
			case "IndexOf":
				if (num != 1)
				{
					return "INDEXOF(#, @0, @1)";
				}
				return "INDEXOF(#, @0)";
			case "Substring":
				if (num != 1)
				{
					return "SUBSTRING(#, @0, @1)";
				}
				return "SUBSTRING(#, @0)";
			case "StartsWith":
				return "# LIKE (@0 + '%')";
			case "Contains":
				return "# LIKE ('%' + @0 + '%')";
			case "EndsWith":
				return "# LIKE ('%' + @0)";
			case "ToString":
				return "#";
			case "Equals":
				return "# = @0";
			case "IsNullOrEmpty":
				return "(LENGTH(@0) = 0)";
			case "IsNullOrWhiteSpace":
				return "(LENGTH(TRIM(@0)) = 0)";
			case "Format":
				throw new NotImplementedException();
			case "Join":
				throw new NotImplementedException();
			default:
				return null;
			}
		}

		public string ResolveMember(MemberInfo member)
		{
			string name = member.Name;
			if (!(name == "Length"))
			{
				if (name == "Empty")
				{
					return "''";
				}
				return null;
			}
			return "LENGTH(#)";
		}

		public string ResolveCtor(ConstructorInfo ctor)
		{
			return null;
		}
	}
	public class MemberMapper
	{
		public bool AutoId { get; set; }

		public string MemberName { get; set; }

		public Type DataType { get; set; }

		public string FieldName { get; set; }

		public GenericGetter Getter { get; set; }

		public GenericSetter Setter { get; set; }

		public Func<object, BsonMapper, BsonValue> Serialize { get; set; }

		public Func<BsonValue, BsonMapper, object> Deserialize { get; set; }

		public bool IsDbRef { get; set; }

		public bool IsEnumerable { get; set; }

		public Type UnderlyingType { get; set; }

		public bool IsIgnore { get; set; }
	}
	public delegate object CreateObject(BsonDocument value);
	public delegate void GenericSetter(object target, object value);
	public delegate object GenericGetter(object obj);
	internal class Reflection
	{
		private static readonly Dictionary<Type, CreateObject> _cacheCtor = new Dictionary<Type, CreateObject>();

		public static readonly Dictionary<Type, PropertyInfo> ConvertType = new Dictionary<Type, PropertyInfo>
		{
			[typeof(DateTime)] = typeof(BsonValue).GetProperty("AsDateTime"),
			[typeof(decimal)] = typeof(BsonValue).GetProperty("AsDecimal"),
			[typeof(double)] = typeof(BsonValue).GetProperty("AsDouble"),
			[typeof(long)] = typeof(BsonValue).GetProperty("AsInt64"),
			[typeof(int)] = typeof(BsonValue).GetProperty("AsInt32"),
			[typeof(bool)] = typeof(BsonValue).GetProperty("AsBoolean"),
			[typeof(byte[])] = typeof(BsonValue).GetProperty("AsBinary"),
			[typeof(BsonDocument)] = typeof(BsonValue).GetProperty("AsDocument"),
			[typeof(BsonArray)] = typeof(BsonValue).GetProperty("AsArray"),
			[typeof(ObjectId)] = typeof(BsonValue).GetProperty("AsObjectId"),
			[typeof(string)] = typeof(BsonValue).GetProperty("AsString"),
			[typeof(Guid)] = typeof(BsonValue).GetProperty("AsGuid")
		};

		public static readonly PropertyInfo DocumentItemProperty = (from x in typeof(BsonDocument).GetProperties()
			where x.Name == "Item" && x.GetGetMethod().GetParameters().First()
				.ParameterType == typeof(string)
			select x).First();

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

		public static object CreateInstance(Type type)
		{
			try
			{
				if (_cacheCtor.TryGetValue(type, out var value))
				{
					return value(null);
				}
			}
			catch (Exception inner)
			{
				throw LiteException.InvalidCtor(type, inner);
			}
			lock (_cacheCtor)
			{
				try
				{
					if (_cacheCtor.TryGetValue(type, out var value2))
					{
						return value2(null);
					}
					TypeInfo typeInfo = type.GetTypeInfo();
					if (typeInfo.IsClass)
					{
						_cacheCtor.Add(type, value2 = CreateClass(type));
					}
					else
					{
						if (typeInfo.IsInterface)
						{
							if (typeInfo.IsGenericType)
							{
								Type genericTypeDefinition = type.GetGenericTypeDefinition();
								if (genericTypeDefinition == typeof(ISet<>))
								{
									return CreateInstance(GetGenericSetOfType(UnderlyingTypeOf(type)));
								}
								if (genericTypeDefinition == typeof(IDictionary<, >))
								{
									Type k = type.GetGenericArguments()[0];
									Type v = type.GetGenericArguments()[1];
									return CreateInstance(GetGenericDictionaryOfType(k, v));
								}
								if (genericTypeDefinition == typeof(IList<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>) || typeof(IEnumerable).IsAssignableFrom(genericTypeDefiniti

BepInEx/plugins/ValheimSagas/Newtonsoft.Json.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Versioning;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq.JsonPath;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AllowPartiallyTrustedCallers]
[assembly: InternalsVisibleTo("Newtonsoft.Json.Schema, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")]
[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")]
[assembly: InternalsVisibleTo("Newtonsoft.Json.Dynamic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cbd8d53b9d7de30f1f1278f636ec462cf9c254991291e66ebb157a885638a517887633b898ccbcf0d5c5ff7be85a6abe9e765d0ac7cd33c68dac67e7e64530e8222101109f154ab14a941c490ac155cd1d4fcba0fabb49016b4ef28593b015cab5937da31172f03f67d09edda404b88a60023f062ae71d0b2e4438b74cc11dc9")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("9ca358aa-317b-4925-8ada-4a29e943a363")]
[assembly: CLSCompliant(true)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Newtonsoft")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © James Newton-King 2008")]
[assembly: AssemblyDescription("Json.NET is a popular high-performance JSON framework for .NET")]
[assembly: AssemblyFileVersion("13.0.3.27908")]
[assembly: AssemblyInformationalVersion("13.0.3+0a2e291c0d9c0c7675d445703e51750363a549ef")]
[assembly: AssemblyProduct("Json.NET")]
[assembly: AssemblyTitle("Json.NET .NET Standard 2.0")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/JamesNK/Newtonsoft.Json")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("13.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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;
		}
	}
}
namespace System.Diagnostics.CodeAnalysis
{
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)]
	internal sealed class NotNullAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
	internal sealed class NotNullWhenAttribute : Attribute
	{
		public bool ReturnValue { get; }

		public NotNullWhenAttribute(bool returnValue)
		{
			ReturnValue = returnValue;
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)]
	internal sealed class MaybeNullAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
	internal sealed class AllowNullAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	internal class DoesNotReturnIfAttribute : Attribute
	{
		public bool ParameterValue { get; }

		public DoesNotReturnIfAttribute(bool parameterValue)
		{
			ParameterValue = parameterValue;
		}
	}
}
namespace Newtonsoft.Json
{
	public enum ConstructorHandling
	{
		Default,
		AllowNonPublicDefaultConstructor
	}
	public enum DateFormatHandling
	{
		IsoDateFormat,
		MicrosoftDateFormat
	}
	public enum DateParseHandling
	{
		None,
		DateTime,
		DateTimeOffset
	}
	public enum DateTimeZoneHandling
	{
		Local,
		Utc,
		Unspecified,
		RoundtripKind
	}
	public class DefaultJsonNameTable : JsonNameTable
	{
		private class Entry
		{
			internal readonly string Value;

			internal readonly int HashCode;

			internal Entry Next;

			internal Entry(string value, int hashCode, Entry next)
			{
				Value = value;
				HashCode = hashCode;
				Next = next;
			}
		}

		private static readonly int HashCodeRandomizer;

		private int _count;

		private Entry[] _entries;

		private int _mask = 31;

		static DefaultJsonNameTable()
		{
			HashCodeRandomizer = Environment.TickCount;
		}

		public DefaultJsonNameTable()
		{
			_entries = new Entry[_mask + 1];
		}

		public override string? Get(char[] key, int start, int length)
		{
			if (length == 0)
			{
				return string.Empty;
			}
			int num = length + HashCodeRandomizer;
			num += (num << 7) ^ key[start];
			int num2 = start + length;
			for (int i = start + 1; i < num2; i++)
			{
				num += (num << 7) ^ key[i];
			}
			num -= num >> 17;
			num -= num >> 11;
			num -= num >> 5;
			int num3 = Volatile.Read(ref _mask);
			int num4 = num & num3;
			for (Entry entry = _entries[num4]; entry != null; entry = entry.Next)
			{
				if (entry.HashCode == num && TextEquals(entry.Value, key, start, length))
				{
					return entry.Value;
				}
			}
			return null;
		}

		public string Add(string key)
		{
			if (key == null)
			{
				throw new ArgumentNullException("key");
			}
			int length = key.Length;
			if (length == 0)
			{
				return string.Empty;
			}
			int num = length + HashCodeRandomizer;
			for (int i = 0; i < key.Length; i++)
			{
				num += (num << 7) ^ key[i];
			}
			num -= num >> 17;
			num -= num >> 11;
			num -= num >> 5;
			for (Entry entry = _entries[num & _mask]; entry != null; entry = entry.Next)
			{
				if (entry.HashCode == num && entry.Value.Equals(key, StringComparison.Ordinal))
				{
					return entry.Value;
				}
			}
			return AddEntry(key, num);
		}

		private string AddEntry(string str, int hashCode)
		{
			int num = hashCode & _mask;
			Entry entry = new Entry(str, hashCode, _entries[num]);
			_entries[num] = entry;
			if (_count++ == _mask)
			{
				Grow();
			}
			return entry.Value;
		}

		private void Grow()
		{
			Entry[] entries = _entries;
			int num = _mask * 2 + 1;
			Entry[] array = new Entry[num + 1];
			for (int i = 0; i < entries.Length; i++)
			{
				Entry entry = entries[i];
				while (entry != null)
				{
					int num2 = entry.HashCode & num;
					Entry next = entry.Next;
					entry.Next = array[num2];
					array[num2] = entry;
					entry = next;
				}
			}
			_entries = array;
			Volatile.Write(ref _mask, num);
		}

		private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length)
		{
			if (str1.Length != str2Length)
			{
				return false;
			}
			for (int i = 0; i < str1.Length; i++)
			{
				if (str1[i] != str2[str2Start + i])
				{
					return false;
				}
			}
			return true;
		}
	}
	[Flags]
	public enum DefaultValueHandling
	{
		Include = 0,
		Ignore = 1,
		Populate = 2,
		IgnoreAndPopulate = 3
	}
	public enum FloatFormatHandling
	{
		String,
		Symbol,
		DefaultValue
	}
	public enum FloatParseHandling
	{
		Double,
		Decimal
	}
	public enum Formatting
	{
		None,
		Indented
	}
	public interface IArrayPool<T>
	{
		T[] Rent(int minimumLength);

		void Return(T[]? array);
	}
	public interface IJsonLineInfo
	{
		int LineNumber { get; }

		int LinePosition { get; }

		bool HasLineInfo();
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
	public sealed class JsonArrayAttribute : JsonContainerAttribute
	{
		private bool _allowNullItems;

		public bool AllowNullItems
		{
			get
			{
				return _allowNullItems;
			}
			set
			{
				_allowNullItems = value;
			}
		}

		public JsonArrayAttribute()
		{
		}

		public JsonArrayAttribute(bool allowNullItems)
		{
			_allowNullItems = allowNullItems;
		}

		public JsonArrayAttribute(string id)
			: base(id)
		{
		}
	}
	[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)]
	public sealed class JsonConstructorAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
	public abstract class JsonContainerAttribute : Attribute
	{
		internal bool? _isReference;

		internal bool? _itemIsReference;

		internal ReferenceLoopHandling? _itemReferenceLoopHandling;

		internal TypeNameHandling? _itemTypeNameHandling;

		private Type? _namingStrategyType;

		private object[]? _namingStrategyParameters;

		public string? Id { get; set; }

		public string? Title { get; set; }

		public string? Description { get; set; }

		public Type? ItemConverterType { get; set; }

		public object[]? ItemConverterParameters { get; set; }

		public Type? NamingStrategyType
		{
			get
			{
				return _namingStrategyType;
			}
			set
			{
				_namingStrategyType = value;
				NamingStrategyInstance = null;
			}
		}

		public object[]? NamingStrategyParameters
		{
			get
			{
				return _namingStrategyParameters;
			}
			set
			{
				_namingStrategyParameters = value;
				NamingStrategyInstance = null;
			}
		}

		internal NamingStrategy? NamingStrategyInstance { get; set; }

		public bool IsReference
		{
			get
			{
				return _isReference.GetValueOrDefault();
			}
			set
			{
				_isReference = value;
			}
		}

		public bool ItemIsReference
		{
			get
			{
				return _itemIsReference.GetValueOrDefault();
			}
			set
			{
				_itemIsReference = value;
			}
		}

		public ReferenceLoopHandling ItemReferenceLoopHandling
		{
			get
			{
				return _itemReferenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_itemReferenceLoopHandling = value;
			}
		}

		public TypeNameHandling ItemTypeNameHandling
		{
			get
			{
				return _itemTypeNameHandling.GetValueOrDefault();
			}
			set
			{
				_itemTypeNameHandling = value;
			}
		}

		protected JsonContainerAttribute()
		{
		}

		protected JsonContainerAttribute(string id)
		{
			Id = id;
		}
	}
	public static class JsonConvert
	{
		public static readonly string True = "true";

		public static readonly string False = "false";

		public static readonly string Null = "null";

		public static readonly string Undefined = "undefined";

		public static readonly string PositiveInfinity = "Infinity";

		public static readonly string NegativeInfinity = "-Infinity";

		public static readonly string NaN = "NaN";

		public static Func<JsonSerializerSettings>? DefaultSettings { get; set; }

		public static string ToString(DateTime value)
		{
			return ToString(value, DateFormatHandling.IsoDateFormat, DateTimeZoneHandling.RoundtripKind);
		}

		public static string ToString(DateTime value, DateFormatHandling format, DateTimeZoneHandling timeZoneHandling)
		{
			DateTime value2 = DateTimeUtils.EnsureDateTime(value, timeZoneHandling);
			using StringWriter stringWriter = StringUtils.CreateStringWriter(64);
			stringWriter.Write('"');
			DateTimeUtils.WriteDateTimeString(stringWriter, value2, format, null, CultureInfo.InvariantCulture);
			stringWriter.Write('"');
			return stringWriter.ToString();
		}

		public static string ToString(DateTimeOffset value)
		{
			return ToString(value, DateFormatHandling.IsoDateFormat);
		}

		public static string ToString(DateTimeOffset value, DateFormatHandling format)
		{
			using StringWriter stringWriter = StringUtils.CreateStringWriter(64);
			stringWriter.Write('"');
			DateTimeUtils.WriteDateTimeOffsetString(stringWriter, value, format, null, CultureInfo.InvariantCulture);
			stringWriter.Write('"');
			return stringWriter.ToString();
		}

		public static string ToString(bool value)
		{
			if (!value)
			{
				return False;
			}
			return True;
		}

		public static string ToString(char value)
		{
			return ToString(char.ToString(value));
		}

		public static string ToString(Enum value)
		{
			return value.ToString("D");
		}

		public static string ToString(int value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(short value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(ushort value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(uint value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(long value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		private static string ToStringInternal(BigInteger value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(ulong value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(float value)
		{
			return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture));
		}

		internal static string ToString(float value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
		{
			return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable);
		}

		private static string EnsureFloatFormat(double value, string text, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
		{
			if (floatFormatHandling == FloatFormatHandling.Symbol || (!double.IsInfinity(value) && !double.IsNaN(value)))
			{
				return text;
			}
			if (floatFormatHandling == FloatFormatHandling.DefaultValue)
			{
				if (nullable)
				{
					return Null;
				}
				return "0.0";
			}
			return quoteChar + text + quoteChar;
		}

		public static string ToString(double value)
		{
			return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture));
		}

		internal static string ToString(double value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
		{
			return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable);
		}

		private static string EnsureDecimalPlace(double value, string text)
		{
			if (double.IsNaN(value) || double.IsInfinity(value) || StringUtils.IndexOf(text, '.') != -1 || StringUtils.IndexOf(text, 'E') != -1 || StringUtils.IndexOf(text, 'e') != -1)
			{
				return text;
			}
			return text + ".0";
		}

		private static string EnsureDecimalPlace(string text)
		{
			if (StringUtils.IndexOf(text, '.') != -1)
			{
				return text;
			}
			return text + ".0";
		}

		public static string ToString(byte value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(sbyte value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(decimal value)
		{
			return EnsureDecimalPlace(value.ToString(null, CultureInfo.InvariantCulture));
		}

		public static string ToString(Guid value)
		{
			return ToString(value, '"');
		}

		internal static string ToString(Guid value, char quoteChar)
		{
			string text = value.ToString("D", CultureInfo.InvariantCulture);
			string text2 = quoteChar.ToString(CultureInfo.InvariantCulture);
			return text2 + text + text2;
		}

		public static string ToString(TimeSpan value)
		{
			return ToString(value, '"');
		}

		internal static string ToString(TimeSpan value, char quoteChar)
		{
			return ToString(value.ToString(), quoteChar);
		}

		public static string ToString(Uri? value)
		{
			if (value == null)
			{
				return Null;
			}
			return ToString(value, '"');
		}

		internal static string ToString(Uri value, char quoteChar)
		{
			return ToString(value.OriginalString, quoteChar);
		}

		public static string ToString(string? value)
		{
			return ToString(value, '"');
		}

		public static string ToString(string? value, char delimiter)
		{
			return ToString(value, delimiter, StringEscapeHandling.Default);
		}

		public static string ToString(string? value, char delimiter, StringEscapeHandling stringEscapeHandling)
		{
			if (delimiter != '"' && delimiter != '\'')
			{
				throw new ArgumentException("Delimiter must be a single or double quote.", "delimiter");
			}
			return JavaScriptUtils.ToEscapedJavaScriptString(value, delimiter, appendDelimiters: true, stringEscapeHandling);
		}

		public static string ToString(object? value)
		{
			if (value == null)
			{
				return Null;
			}
			return ConvertUtils.GetTypeCode(value.GetType()) switch
			{
				PrimitiveTypeCode.String => ToString((string)value), 
				PrimitiveTypeCode.Char => ToString((char)value), 
				PrimitiveTypeCode.Boolean => ToString((bool)value), 
				PrimitiveTypeCode.SByte => ToString((sbyte)value), 
				PrimitiveTypeCode.Int16 => ToString((short)value), 
				PrimitiveTypeCode.UInt16 => ToString((ushort)value), 
				PrimitiveTypeCode.Int32 => ToString((int)value), 
				PrimitiveTypeCode.Byte => ToString((byte)value), 
				PrimitiveTypeCode.UInt32 => ToString((uint)value), 
				PrimitiveTypeCode.Int64 => ToString((long)value), 
				PrimitiveTypeCode.UInt64 => ToString((ulong)value), 
				PrimitiveTypeCode.Single => ToString((float)value), 
				PrimitiveTypeCode.Double => ToString((double)value), 
				PrimitiveTypeCode.DateTime => ToString((DateTime)value), 
				PrimitiveTypeCode.Decimal => ToString((decimal)value), 
				PrimitiveTypeCode.DBNull => Null, 
				PrimitiveTypeCode.DateTimeOffset => ToString((DateTimeOffset)value), 
				PrimitiveTypeCode.Guid => ToString((Guid)value), 
				PrimitiveTypeCode.Uri => ToString((Uri)value), 
				PrimitiveTypeCode.TimeSpan => ToString((TimeSpan)value), 
				PrimitiveTypeCode.BigInteger => ToStringInternal((BigInteger)value), 
				_ => throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())), 
			};
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value)
		{
			return SerializeObject(value, (Type?)null, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Formatting formatting)
		{
			return SerializeObject(value, formatting, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, params JsonConverter[] converters)
		{
			JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings
			{
				Converters = converters
			} : null);
			return SerializeObject(value, null, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Formatting formatting, params JsonConverter[] converters)
		{
			JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings
			{
				Converters = converters
			} : null);
			return SerializeObject(value, null, formatting, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, JsonSerializerSettings? settings)
		{
			return SerializeObject(value, null, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Type? type, JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			return SerializeObjectInternal(value, type, jsonSerializer);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Formatting formatting, JsonSerializerSettings? settings)
		{
			return SerializeObject(value, null, formatting, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Type? type, Formatting formatting, JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			jsonSerializer.Formatting = formatting;
			return SerializeObjectInternal(value, type, jsonSerializer);
		}

		private static string SerializeObjectInternal(object? value, Type? type, JsonSerializer jsonSerializer)
		{
			StringWriter stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture);
			using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter))
			{
				jsonTextWriter.Formatting = jsonSerializer.Formatting;
				jsonSerializer.Serialize(jsonTextWriter, value, type);
			}
			return stringWriter.ToString();
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value)
		{
			return DeserializeObject(value, (Type?)null, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value, JsonSerializerSettings settings)
		{
			return DeserializeObject(value, null, settings);
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value, Type type)
		{
			return DeserializeObject(value, type, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static T? DeserializeObject<T>(string value)
		{
			return JsonConvert.DeserializeObject<T>(value, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject)
		{
			return DeserializeObject<T>(value);
		}

		[DebuggerStepThrough]
		public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject, JsonSerializerSettings settings)
		{
			return DeserializeObject<T>(value, settings);
		}

		[DebuggerStepThrough]
		public static T? DeserializeObject<T>(string value, params JsonConverter[] converters)
		{
			return (T)DeserializeObject(value, typeof(T), converters);
		}

		[DebuggerStepThrough]
		public static T? DeserializeObject<T>(string value, JsonSerializerSettings? settings)
		{
			return (T)DeserializeObject(value, typeof(T), settings);
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value, Type type, params JsonConverter[] converters)
		{
			JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings
			{
				Converters = converters
			} : null);
			return DeserializeObject(value, type, settings);
		}

		public static object? DeserializeObject(string value, Type? type, JsonSerializerSettings? settings)
		{
			ValidationUtils.ArgumentNotNull(value, "value");
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			if (!jsonSerializer.IsCheckAdditionalContentSet())
			{
				jsonSerializer.CheckAdditionalContent = true;
			}
			using JsonTextReader reader = new JsonTextReader(new StringReader(value));
			return jsonSerializer.Deserialize(reader, type);
		}

		[DebuggerStepThrough]
		public static void PopulateObject(string value, object target)
		{
			PopulateObject(value, target, null);
		}

		public static void PopulateObject(string value, object target, JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			using JsonReader jsonReader = new JsonTextReader(new StringReader(value));
			jsonSerializer.Populate(jsonReader, target);
			if (settings == null || !settings.CheckAdditionalContent)
			{
				return;
			}
			while (jsonReader.Read())
			{
				if (jsonReader.TokenType != JsonToken.Comment)
				{
					throw JsonSerializationException.Create(jsonReader, "Additional text found in JSON string after finishing deserializing object.");
				}
			}
		}

		public static string SerializeXmlNode(XmlNode? node)
		{
			return SerializeXmlNode(node, Formatting.None);
		}

		public static string SerializeXmlNode(XmlNode? node, Formatting formatting)
		{
			XmlNodeConverter xmlNodeConverter = new XmlNodeConverter();
			return SerializeObject(node, formatting, xmlNodeConverter);
		}

		public static string SerializeXmlNode(XmlNode? node, Formatting formatting, bool omitRootObject)
		{
			XmlNodeConverter xmlNodeConverter = new XmlNodeConverter
			{
				OmitRootObject = omitRootObject
			};
			return SerializeObject(node, formatting, xmlNodeConverter);
		}

		public static XmlDocument? DeserializeXmlNode(string value)
		{
			return DeserializeXmlNode(value, null);
		}

		public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName)
		{
			return DeserializeXmlNode(value, deserializeRootElementName, writeArrayAttribute: false);
		}

		public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName, bool writeArrayAttribute)
		{
			return DeserializeXmlNode(value, deserializeRootElementName, writeArrayAttribute, encodeSpecialCharacters: false);
		}

		public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName, bool writeArrayAttribute, bool encodeSpecialCharacters)
		{
			XmlNodeConverter xmlNodeConverter = new XmlNodeConverter();
			xmlNodeConverter.DeserializeRootElementName = deserializeRootElementName;
			xmlNodeConverter.WriteArrayAttribute = writeArrayAttribute;
			xmlNodeConverter.EncodeSpecialCharacters = encodeSpecialCharacters;
			return (XmlDocument)DeserializeObject(value, typeof(XmlDocument), xmlNodeConverter);
		}

		public static string SerializeXNode(XObject? node)
		{
			return SerializeXNode(node, Formatting.None);
		}

		public static string SerializeXNode(XObject? node, Formatting formatting)
		{
			return SerializeXNode(node, formatting, omitRootObject: false);
		}

		public static string SerializeXNode(XObject? node, Formatting formatting, bool omitRootObject)
		{
			XmlNodeConverter xmlNodeConverter = new XmlNodeConverter
			{
				OmitRootObject = omitRootObject
			};
			return SerializeObject(node, formatting, xmlNodeConverter);
		}

		public static XDocument? DeserializeXNode(string value)
		{
			return DeserializeXNode(value, null);
		}

		public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName)
		{
			return DeserializeXNode(value, deserializeRootElementName, writeArrayAttribute: false);
		}

		public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName, bool writeArrayAttribute)
		{
			return DeserializeXNode(value, deserializeRootElementName, writeArrayAttribute, encodeSpecialCharacters: false);
		}

		public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName, bool writeArrayAttribute, bool encodeSpecialCharacters)
		{
			XmlNodeConverter xmlNodeConverter = new XmlNodeConverter();
			xmlNodeConverter.DeserializeRootElementName = deserializeRootElementName;
			xmlNodeConverter.WriteArrayAttribute = writeArrayAttribute;
			xmlNodeConverter.EncodeSpecialCharacters = encodeSpecialCharacters;
			return (XDocument)DeserializeObject(value, typeof(XDocument), xmlNodeConverter);
		}
	}
	public abstract class JsonConverter
	{
		public virtual bool CanRead => true;

		public virtual bool CanWrite => true;

		public abstract void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer);

		public abstract object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer);

		public abstract bool CanConvert(Type objectType);
	}
	public abstract class JsonConverter<T> : JsonConverter
	{
		public sealed override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
		{
			if (!((value != null) ? (value is T) : ReflectionUtils.IsNullable(typeof(T))))
			{
				throw new JsonSerializationException("Converter cannot write specified value to JSON. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T)));
			}
			WriteJson(writer, (T)value, serializer);
		}

		public abstract void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer);

		public sealed override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
		{
			bool flag = existingValue == null;
			if (!flag && !(existingValue is T))
			{
				throw new JsonSerializationException("Converter cannot read JSON with the specified existing value. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T)));
			}
			return ReadJson(reader, objectType, flag ? default(T) : ((T)existingValue), !flag, serializer);
		}

		public abstract T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer);

		public sealed override bool CanConvert(Type objectType)
		{
			return typeof(T).IsAssignableFrom(objectType);
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false)]
	public sealed class JsonConverterAttribute : Attribute
	{
		private readonly Type _converterType;

		public Type ConverterType => _converterType;

		public object[]? ConverterParameters { get; }

		public JsonConverterAttribute(Type converterType)
		{
			if (converterType == null)
			{
				throw new ArgumentNullException("converterType");
			}
			_converterType = converterType;
		}

		public JsonConverterAttribute(Type converterType, params object[] converterParameters)
			: this(converterType)
		{
			ConverterParameters = converterParameters;
		}
	}
	public class JsonConverterCollection : Collection<JsonConverter>
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
	public sealed class JsonDictionaryAttribute : JsonContainerAttribute
	{
		public JsonDictionaryAttribute()
		{
		}

		public JsonDictionaryAttribute(string id)
			: base(id)
		{
		}
	}
	[Serializable]
	public class JsonException : Exception
	{
		public JsonException()
		{
		}

		public JsonException(string message)
			: base(message)
		{
		}

		public JsonException(string message, Exception? innerException)
			: base(message, innerException)
		{
		}

		public JsonException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		internal static JsonException Create(IJsonLineInfo lineInfo, string path, string message)
		{
			message = JsonPosition.FormatMessage(lineInfo, path, message);
			return new JsonException(message);
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public class JsonExtensionDataAttribute : Attribute
	{
		public bool WriteData { get; set; }

		public bool ReadData { get; set; }

		public JsonExtensionDataAttribute()
		{
			WriteData = true;
			ReadData = true;
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public sealed class JsonIgnoreAttribute : Attribute
	{
	}
	public abstract class JsonNameTable
	{
		public abstract string? Get(char[] key, int start, int length);
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false)]
	public sealed class JsonObjectAttribute : JsonContainerAttribute
	{
		private MemberSerialization _memberSerialization;

		internal MissingMemberHandling? _missingMemberHandling;

		internal Required? _itemRequired;

		internal NullValueHandling? _itemNullValueHandling;

		public MemberSerialization MemberSerialization
		{
			get
			{
				return _memberSerialization;
			}
			set
			{
				_memberSerialization = value;
			}
		}

		public MissingMemberHandling MissingMemberHandling
		{
			get
			{
				return _missingMemberHandling.GetValueOrDefault();
			}
			set
			{
				_missingMemberHandling = value;
			}
		}

		public NullValueHandling ItemNullValueHandling
		{
			get
			{
				return _itemNullValueHandling.GetValueOrDefault();
			}
			set
			{
				_itemNullValueHandling = value;
			}
		}

		public Required ItemRequired
		{
			get
			{
				return _itemRequired.GetValueOrDefault();
			}
			set
			{
				_itemRequired = value;
			}
		}

		public JsonObjectAttribute()
		{
		}

		public JsonObjectAttribute(MemberSerialization memberSerialization)
		{
			MemberSerialization = memberSerialization;
		}

		public JsonObjectAttribute(string id)
			: base(id)
		{
		}
	}
	internal enum JsonContainerType
	{
		None,
		Object,
		Array,
		Constructor
	}
	internal struct JsonPosition
	{
		private static readonly char[] SpecialCharacters = new char[18]
		{
			'.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t',
			'\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029'
		};

		internal JsonContainerType Type;

		internal int Position;

		internal string? PropertyName;

		internal bool HasIndex;

		public JsonPosition(JsonContainerType type)
		{
			Type = type;
			HasIndex = TypeHasIndex(type);
			Position = -1;
			PropertyName = null;
		}

		internal int CalculateLength()
		{
			switch (Type)
			{
			case JsonContainerType.Object:
				return PropertyName.Length + 5;
			case JsonContainerType.Array:
			case JsonContainerType.Constructor:
				return MathUtils.IntLength((ulong)Position) + 2;
			default:
				throw new ArgumentOutOfRangeException("Type");
			}
		}

		internal void WriteTo(StringBuilder sb, ref StringWriter? writer, ref char[]? buffer)
		{
			switch (Type)
			{
			case JsonContainerType.Object:
			{
				string propertyName = PropertyName;
				if (propertyName.IndexOfAny(SpecialCharacters) != -1)
				{
					sb.Append("['");
					if (writer == null)
					{
						writer = new StringWriter(sb);
					}
					JavaScriptUtils.WriteEscapedJavaScriptString(writer, propertyName, '\'', appendDelimiters: false, JavaScriptUtils.SingleQuoteCharEscapeFlags, StringEscapeHandling.Default, null, ref buffer);
					sb.Append("']");
				}
				else
				{
					if (sb.Length > 0)
					{
						sb.Append('.');
					}
					sb.Append(propertyName);
				}
				break;
			}
			case JsonContainerType.Array:
			case JsonContainerType.Constructor:
				sb.Append('[');
				sb.Append(Position);
				sb.Append(']');
				break;
			}
		}

		internal static bool TypeHasIndex(JsonContainerType type)
		{
			if (type != JsonContainerType.Array)
			{
				return type == JsonContainerType.Constructor;
			}
			return true;
		}

		internal static string BuildPath(List<JsonPosition> positions, JsonPosition? currentPosition)
		{
			int num = 0;
			if (positions != null)
			{
				for (int i = 0; i < positions.Count; i++)
				{
					num += positions[i].CalculateLength();
				}
			}
			if (currentPosition.HasValue)
			{
				num += currentPosition.GetValueOrDefault().CalculateLength();
			}
			StringBuilder stringBuilder = new StringBuilder(num);
			StringWriter writer = null;
			char[] buffer = null;
			if (positions != null)
			{
				foreach (JsonPosition position in positions)
				{
					position.WriteTo(stringBuilder, ref writer, ref buffer);
				}
			}
			currentPosition?.WriteTo(stringBuilder, ref writer, ref buffer);
			return stringBuilder.ToString();
		}

		internal static string FormatMessage(IJsonLineInfo? lineInfo, string path, string message)
		{
			if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal))
			{
				message = message.Trim();
				if (!StringUtils.EndsWith(message, '.'))
				{
					message += ".";
				}
				message += " ";
			}
			message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path);
			if (lineInfo != null && lineInfo.HasLineInfo())
			{
				message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition);
			}
			message += ".";
			return message;
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
	public sealed class JsonPropertyAttribute : Attribute
	{
		internal NullValueHandling? _nullValueHandling;

		internal DefaultValueHandling? _defaultValueHandling;

		internal ReferenceLoopHandling? _referenceLoopHandling;

		internal ObjectCreationHandling? _objectCreationHandling;

		internal TypeNameHandling? _typeNameHandling;

		internal bool? _isReference;

		internal int? _order;

		internal Required? _required;

		internal bool? _itemIsReference;

		internal ReferenceLoopHandling? _itemReferenceLoopHandling;

		internal TypeNameHandling? _itemTypeNameHandling;

		public Type? ItemConverterType { get; set; }

		public object[]? ItemConverterParameters { get; set; }

		public Type? NamingStrategyType { get; set; }

		public object[]? NamingStrategyParameters { get; set; }

		public NullValueHandling NullValueHandling
		{
			get
			{
				return _nullValueHandling.GetValueOrDefault();
			}
			set
			{
				_nullValueHandling = value;
			}
		}

		public DefaultValueHandling DefaultValueHandling
		{
			get
			{
				return _defaultValueHandling.GetValueOrDefault();
			}
			set
			{
				_defaultValueHandling = value;
			}
		}

		public ReferenceLoopHandling ReferenceLoopHandling
		{
			get
			{
				return _referenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_referenceLoopHandling = value;
			}
		}

		public ObjectCreationHandling ObjectCreationHandling
		{
			get
			{
				return _objectCreationHandling.GetValueOrDefault();
			}
			set
			{
				_objectCreationHandling = value;
			}
		}

		public TypeNameHandling TypeNameHandling
		{
			get
			{
				return _typeNameHandling.GetValueOrDefault();
			}
			set
			{
				_typeNameHandling = value;
			}
		}

		public bool IsReference
		{
			get
			{
				return _isReference.GetValueOrDefault();
			}
			set
			{
				_isReference = value;
			}
		}

		public int Order
		{
			get
			{
				return _order.GetValueOrDefault();
			}
			set
			{
				_order = value;
			}
		}

		public Required Required
		{
			get
			{
				return _required.GetValueOrDefault();
			}
			set
			{
				_required = value;
			}
		}

		public string? PropertyName { get; set; }

		public ReferenceLoopHandling ItemReferenceLoopHandling
		{
			get
			{
				return _itemReferenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_itemReferenceLoopHandling = value;
			}
		}

		public TypeNameHandling ItemTypeNameHandling
		{
			get
			{
				return _itemTypeNameHandling.GetValueOrDefault();
			}
			set
			{
				_itemTypeNameHandling = value;
			}
		}

		public bool ItemIsReference
		{
			get
			{
				return _itemIsReference.GetValueOrDefault();
			}
			set
			{
				_itemIsReference = value;
			}
		}

		public JsonPropertyAttribute()
		{
		}

		public JsonPropertyAttribute(string propertyName)
		{
			PropertyName = propertyName;
		}
	}
	public abstract class JsonReader : IDisposable
	{
		protected internal enum State
		{
			Start,
			Complete,
			Property,
			ObjectStart,
			Object,
			ArrayStart,
			Array,
			Closed,
			PostValue,
			ConstructorStart,
			Constructor,
			Error,
			Finished
		}

		private JsonToken _tokenType;

		private object? _value;

		internal char _quoteChar;

		internal State _currentState;

		private JsonPosition _currentPosition;

		private CultureInfo? _culture;

		private DateTimeZoneHandling _dateTimeZoneHandling;

		private int? _maxDepth;

		private bool _hasExceededMaxDepth;

		internal DateParseHandling _dateParseHandling;

		internal FloatParseHandling _floatParseHandling;

		private string? _dateFormatString;

		private List<JsonPosition>? _stack;

		protected State CurrentState => _currentState;

		public bool CloseInput { get; set; }

		public bool SupportMultipleContent { get; set; }

		public virtual char QuoteChar
		{
			get
			{
				return _quoteChar;
			}
			protected internal set
			{
				_quoteChar = value;
			}
		}

		public DateTimeZoneHandling DateTimeZoneHandling
		{
			get
			{
				return _dateTimeZoneHandling;
			}
			set
			{
				if (value < DateTimeZoneHandling.Local || value > DateTimeZoneHandling.RoundtripKind)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_dateTimeZoneHandling = value;
			}
		}

		public DateParseHandling DateParseHandling
		{
			get
			{
				return _dateParseHandling;
			}
			set
			{
				if (value < DateParseHandling.None || value > DateParseHandling.DateTimeOffset)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_dateParseHandling = value;
			}
		}

		public FloatParseHandling FloatParseHandling
		{
			get
			{
				return _floatParseHandling;
			}
			set
			{
				if (value < FloatParseHandling.Double || value > FloatParseHandling.Decimal)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_floatParseHandling = value;
			}
		}

		public string? DateFormatString
		{
			get
			{
				return _dateFormatString;
			}
			set
			{
				_dateFormatString = value;
			}
		}

		public int? MaxDepth
		{
			get
			{
				return _maxDepth;
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentException("Value must be positive.", "value");
				}
				_maxDepth = value;
			}
		}

		public virtual JsonToken TokenType => _tokenType;

		public virtual object? Value => _value;

		public virtual Type? ValueType => _value?.GetType();

		public virtual int Depth
		{
			get
			{
				int num = _stack?.Count ?? 0;
				if (JsonTokenUtils.IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None)
				{
					return num;
				}
				return num + 1;
			}
		}

		public virtual string Path
		{
			get
			{
				if (_currentPosition.Type == JsonContainerType.None)
				{
					return string.Empty;
				}
				JsonPosition? currentPosition = ((_currentState != State.ArrayStart && _currentState != State.ConstructorStart && _currentState != State.ObjectStart) ? new JsonPosition?(_currentPosition) : null);
				return JsonPosition.BuildPath(_stack, currentPosition);
			}
		}

		public CultureInfo Culture
		{
			get
			{
				return _culture ?? CultureInfo.InvariantCulture;
			}
			set
			{
				_culture = value;
			}
		}

		public virtual Task<bool> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<bool>() ?? Read().ToAsync();
		}

		public async Task SkipAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			if (TokenType == JsonToken.PropertyName)
			{
				await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			if (JsonTokenUtils.IsStartToken(TokenType))
			{
				int depth = Depth;
				while (await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false) && depth < Depth)
				{
				}
			}
		}

		internal async Task ReaderReadAndAssertAsync(CancellationToken cancellationToken)
		{
			if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
			{
				throw CreateUnexpectedEndException();
			}
		}

		public virtual Task<bool?> ReadAsBooleanAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<bool?>() ?? Task.FromResult(ReadAsBoolean());
		}

		public virtual Task<byte[]?> ReadAsBytesAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<byte[]>() ?? Task.FromResult(ReadAsBytes());
		}

		internal async Task<byte[]?> ReadArrayIntoByteArrayAsync(CancellationToken cancellationToken)
		{
			List<byte> buffer = new List<byte>();
			do
			{
				if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
				{
					SetToken(JsonToken.None);
				}
			}
			while (!ReadArrayElementIntoByteArrayReportDone(buffer));
			byte[] array = buffer.ToArray();
			SetToken(JsonToken.Bytes, array, updateIndex: false);
			return array;
		}

		public virtual Task<DateTime?> ReadAsDateTimeAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<DateTime?>() ?? Task.FromResult(ReadAsDateTime());
		}

		public virtual Task<DateTimeOffset?> ReadAsDateTimeOffsetAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<DateTimeOffset?>() ?? Task.FromResult(ReadAsDateTimeOffset());
		}

		public virtual Task<decimal?> ReadAsDecimalAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<decimal?>() ?? Task.FromResult(ReadAsDecimal());
		}

		public virtual Task<double?> ReadAsDoubleAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return Task.FromResult(ReadAsDouble());
		}

		public virtual Task<int?> ReadAsInt32Async(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<int?>() ?? Task.FromResult(ReadAsInt32());
		}

		public virtual Task<string?> ReadAsStringAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<string>() ?? Task.FromResult(ReadAsString());
		}

		internal async Task<bool> ReadAndMoveToContentAsync(CancellationToken cancellationToken)
		{
			bool flag = await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			if (flag)
			{
				flag = await MoveToContentAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			return flag;
		}

		internal Task<bool> MoveToContentAsync(CancellationToken cancellationToken)
		{
			JsonToken tokenType = TokenType;
			if (tokenType == JsonToken.None || tokenType == JsonToken.Comment)
			{
				return MoveToContentFromNonContentAsync(cancellationToken);
			}
			return AsyncUtils.True;
		}

		private async Task<bool> MoveToContentFromNonContentAsync(CancellationToken cancellationToken)
		{
			JsonToken tokenType;
			do
			{
				if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
				{
					return false;
				}
				tokenType = TokenType;
			}
			while (tokenType == JsonToken.None || tokenType == JsonToken.Comment);
			return true;
		}

		internal JsonPosition GetPosition(int depth)
		{
			if (_stack != null && depth < _stack.Count)
			{
				return _stack[depth];
			}
			return _currentPosition;
		}

		protected JsonReader()
		{
			_currentState = State.Start;
			_dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
			_dateParseHandling = DateParseHandling.DateTime;
			_floatParseHandling = FloatParseHandling.Double;
			_maxDepth = 64;
			CloseInput = true;
		}

		private void Push(JsonContainerType value)
		{
			UpdateScopeWithFinishedValue();
			if (_currentPosition.Type == JsonContainerType.None)
			{
				_currentPosition = new JsonPosition(value);
				return;
			}
			if (_stack == null)
			{
				_stack = new List<JsonPosition>();
			}
			_stack.Add(_currentPosition);
			_currentPosition = new JsonPosition(value);
			if (!_maxDepth.HasValue || !(Depth + 1 > _maxDepth) || _hasExceededMaxDepth)
			{
				return;
			}
			_hasExceededMaxDepth = true;
			throw JsonReaderException.Create(this, "The reader's MaxDepth of {0} has been exceeded.".FormatWith(CultureInfo.InvariantCulture, _maxDepth));
		}

		private JsonContainerType Pop()
		{
			JsonPosition currentPosition;
			if (_stack != null && _stack.Count > 0)
			{
				currentPosition = _currentPosition;
				_currentPosition = _stack[_stack.Count - 1];
				_stack.RemoveAt(_stack.Count - 1);
			}
			else
			{
				currentPosition = _currentPosition;
				_currentPosition = default(JsonPosition);
			}
			if (_maxDepth.HasValue && Depth <= _maxDepth)
			{
				_hasExceededMaxDepth = false;
			}
			return currentPosition.Type;
		}

		private JsonContainerType Peek()
		{
			return _currentPosition.Type;
		}

		public abstract bool Read();

		public virtual int? ReadAsInt32()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				object value = Value;
				if (value is int)
				{
					return (int)value;
				}
				int num;
				if (value is BigInteger bigInteger)
				{
					num = (int)bigInteger;
				}
				else
				{
					try
					{
						num = Convert.ToInt32(value, CultureInfo.InvariantCulture);
					}
					catch (Exception ex)
					{
						throw JsonReaderException.Create(this, "Could not convert to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, value), ex);
					}
				}
				SetToken(JsonToken.Integer, num, updateIndex: false);
				return num;
			}
			case JsonToken.String:
			{
				string s = (string)Value;
				return ReadInt32String(s);
			}
			default:
				throw JsonReaderException.Create(this, "Error reading integer. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal int? ReadInt32String(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (int.TryParse(s, NumberStyles.Integer, Culture, out var result))
			{
				SetToken(JsonToken.Integer, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual string? ReadAsString()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.String:
				return (string)Value;
			default:
				if (JsonTokenUtils.IsPrimitiveToken(contentToken))
				{
					object value = Value;
					if (value != null)
					{
						string text = ((!(value is IFormattable formattable)) ? ((value is Uri uri) ? uri.OriginalString : value.ToString()) : formattable.ToString(null, Culture));
						SetToken(JsonToken.String, text, updateIndex: false);
						return text;
					}
				}
				throw JsonReaderException.Create(this, "Error reading string. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		public virtual byte[]? ReadAsBytes()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.StartObject:
			{
				ReadIntoWrappedTypeObject();
				byte[] array2 = ReadAsBytes();
				ReaderReadAndAssert();
				if (TokenType != JsonToken.EndObject)
				{
					throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
				}
				SetToken(JsonToken.Bytes, array2, updateIndex: false);
				return array2;
			}
			case JsonToken.String:
			{
				string text = (string)Value;
				Guid g;
				byte[] array3 = ((text.Length == 0) ? CollectionUtils.ArrayEmpty<byte>() : ((!ConvertUtils.TryConvertGuid(text, out g)) ? Convert.FromBase64String(text) : g.ToByteArray()));
				SetToken(JsonToken.Bytes, array3, updateIndex: false);
				return array3;
			}
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Bytes:
				if (Value is Guid guid)
				{
					byte[] array = guid.ToByteArray();
					SetToken(JsonToken.Bytes, array, updateIndex: false);
					return array;
				}
				return (byte[])Value;
			case JsonToken.StartArray:
				return ReadArrayIntoByteArray();
			default:
				throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal byte[] ReadArrayIntoByteArray()
		{
			List<byte> list = new List<byte>();
			do
			{
				if (!Read())
				{
					SetToken(JsonToken.None);
				}
			}
			while (!ReadArrayElementIntoByteArrayReportDone(list));
			byte[] array = list.ToArray();
			SetToken(JsonToken.Bytes, array, updateIndex: false);
			return array;
		}

		private bool ReadArrayElementIntoByteArrayReportDone(List<byte> buffer)
		{
			switch (TokenType)
			{
			case JsonToken.None:
				throw JsonReaderException.Create(this, "Unexpected end when reading bytes.");
			case JsonToken.Integer:
				buffer.Add(Convert.ToByte(Value, CultureInfo.InvariantCulture));
				return false;
			case JsonToken.EndArray:
				return true;
			case JsonToken.Comment:
				return false;
			default:
				throw JsonReaderException.Create(this, "Unexpected token when reading bytes: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
			}
		}

		public virtual double? ReadAsDouble()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				object value = Value;
				if (value is double)
				{
					return (double)value;
				}
				double num = ((!(value is BigInteger bigInteger)) ? Convert.ToDouble(value, CultureInfo.InvariantCulture) : ((double)bigInteger));
				SetToken(JsonToken.Float, num, updateIndex: false);
				return num;
			}
			case JsonToken.String:
				return ReadDoubleString((string)Value);
			default:
				throw JsonReaderException.Create(this, "Error reading double. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal double? ReadDoubleString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (double.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, Culture, out var result))
			{
				SetToken(JsonToken.Float, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to double: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual bool? ReadAsBoolean()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				bool flag = ((!(Value is BigInteger bigInteger)) ? Convert.ToBoolean(Value, CultureInfo.InvariantCulture) : (bigInteger != 0L));
				SetToken(JsonToken.Boolean, flag, updateIndex: false);
				return flag;
			}
			case JsonToken.String:
				return ReadBooleanString((string)Value);
			case JsonToken.Boolean:
				return (bool)Value;
			default:
				throw JsonReaderException.Create(this, "Error reading boolean. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal bool? ReadBooleanString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (bool.TryParse(s, out var result))
			{
				SetToken(JsonToken.Boolean, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to boolean: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual decimal? ReadAsDecimal()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				object value = Value;
				if (value is decimal)
				{
					return (decimal)value;
				}
				decimal num;
				if (value is BigInteger bigInteger)
				{
					num = (decimal)bigInteger;
				}
				else
				{
					try
					{
						num = Convert.ToDecimal(value, CultureInfo.InvariantCulture);
					}
					catch (Exception ex)
					{
						throw JsonReaderException.Create(this, "Could not convert to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, value), ex);
					}
				}
				SetToken(JsonToken.Float, num, updateIndex: false);
				return num;
			}
			case JsonToken.String:
				return ReadDecimalString((string)Value);
			default:
				throw JsonReaderException.Create(this, "Error reading decimal. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal decimal? ReadDecimalString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (decimal.TryParse(s, NumberStyles.Number, Culture, out var result))
			{
				SetToken(JsonToken.Float, result, updateIndex: false);
				return result;
			}
			if (ConvertUtils.DecimalTryParse(s.ToCharArray(), 0, s.Length, out result) == ParseResult.Success)
			{
				SetToken(JsonToken.Float, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual DateTime? ReadAsDateTime()
		{
			switch (GetContentToken())
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Date:
				if (Value is DateTimeOffset dateTimeOffset)
				{
					SetToken(JsonToken.Date, dateTimeOffset.DateTime, updateIndex: false);
				}
				return (DateTime)Value;
			case JsonToken.String:
				return ReadDateTimeString((string)Value);
			default:
				throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
			}
		}

		internal DateTime? ReadDateTimeString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (DateTimeUtils.TryParseDateTime(s, DateTimeZoneHandling, _dateFormatString, Culture, out var dt))
			{
				dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
			{
				dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual DateTimeOffset? ReadAsDateTimeOffset()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Date:
				if (Value is DateTime dateTime)
				{
					SetToken(JsonToken.Date, new DateTimeOffset(dateTime), updateIndex: false);
				}
				return (DateTimeOffset)Value;
			case JsonToken.String:
			{
				string s = (string)Value;
				return ReadDateTimeOffsetString(s);
			}
			default:
				throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal DateTimeOffset? ReadDateTimeOffsetString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (DateTimeUtils.TryParseDateTimeOffset(s, _dateFormatString, Culture, out var dt))
			{
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
			{
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		internal void ReaderReadAndAssert()
		{
			if (!Read())
			{
				throw CreateUnexpectedEndException();
			}
		}

		internal JsonReaderException CreateUnexpectedEndException()
		{
			return JsonReaderException.Create(this, "Unexpected end when reading JSON.");
		}

		internal void ReadIntoWrappedTypeObject()
		{
			ReaderReadAndAssert();
			if (Value != null && Value.ToString() == "$type")
			{
				ReaderReadAndAssert();
				if (Value != null && Value.ToString().StartsWith("System.Byte[]", StringComparison.Ordinal))
				{
					ReaderReadAndAssert();
					if (Value.ToString() == "$value")
					{
						return;
					}
				}
			}
			throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, JsonToken.StartObject));
		}

		public void Skip()
		{
			if (TokenType == JsonToken.PropertyName)
			{
				Read();
			}
			if (JsonTokenUtils.IsStartToken(TokenType))
			{
				int depth = Depth;
				while (Read() && depth < Depth)
				{
				}
			}
		}

		protected void SetToken(JsonToken newToken)
		{
			SetToken(newToken, null, updateIndex: true);
		}

		protected void SetToken(JsonToken newToken, object? value)
		{
			SetToken(newToken, value, updateIndex: true);
		}

		protected void SetToken(JsonToken newToken, object? value, bool updateIndex)
		{
			_tokenType = newToken;
			_value = value;
			switch (newToken)
			{
			case JsonToken.StartObject:
				_currentState = State.ObjectStart;
				Push(JsonContainerType.Object);
				break;
			case JsonToken.StartArray:
				_currentState = State.ArrayStart;
				Push(JsonContainerType.Array);
				break;
			case JsonToken.StartConstructor:
				_currentState = State.ConstructorStart;
				Push(JsonContainerType.Constructor);
				break;
			case JsonToken.EndObject:
				ValidateEnd(JsonToken.EndObject);
				break;
			case JsonToken.EndArray:
				ValidateEnd(JsonToken.EndArray);
				break;
			case JsonToken.EndConstructor:
				ValidateEnd(JsonToken.EndConstructor);
				break;
			case JsonToken.PropertyName:
				_currentState = State.Property;
				_currentPosition.PropertyName = (string)value;
				break;
			case JsonToken.Raw:
			case JsonToken.Integer:
			case JsonToken.Float:
			case JsonToken.String:
			case JsonToken.Boolean:
			case JsonToken.Null:
			case JsonToken.Undefined:
			case JsonToken.Date:
			case JsonToken.Bytes:
				SetPostValueState(updateIndex);
				break;
			case JsonToken.Comment:
				break;
			}
		}

		internal void SetPostValueState(bool updateIndex)
		{
			if (Peek() != 0 || SupportMultipleContent)
			{
				_currentState = State.PostValue;
			}
			else
			{
				SetFinished();
			}
			if (updateIndex)
			{
				UpdateScopeWithFinishedValue();
			}
		}

		private void UpdateScopeWithFinishedValue()
		{
			if (_currentPosition.HasIndex)
			{
				_currentPosition.Position++;
			}
		}

		private void ValidateEnd(JsonToken endToken)
		{
			JsonContainerType jsonContainerType = Pop();
			if (GetTypeForCloseToken(endToken) != jsonContainerType)
			{
				throw JsonReaderException.Create(this, "JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, jsonContainerType));
			}
			if (Peek() != 0 || SupportMultipleContent)
			{
				_currentState = State.PostValue;
			}
			else
			{
				SetFinished();
			}
		}

		protected void SetStateBasedOnCurrent()
		{
			JsonContainerType jsonContainerType = Peek();
			switch (jsonContainerType)
			{
			case JsonContainerType.Object:
				_currentState = State.Object;
				break;
			case JsonContainerType.Array:
				_currentState = State.Array;
				break;
			case JsonContainerType.Constructor:
				_currentState = State.Constructor;
				break;
			case JsonContainerType.None:
				SetFinished();
				break;
			default:
				throw JsonReaderException.Create(this, "While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, jsonContainerType));
			}
		}

		private void SetFinished()
		{
			_currentState = ((!SupportMultipleContent) ? State.Finished : State.Start);
		}

		private JsonContainerType GetTypeForCloseToken(JsonToken token)
		{
			return token switch
			{
				JsonToken.EndObject => JsonContainerType.Object, 
				JsonToken.EndArray => JsonContainerType.Array, 
				JsonToken.EndConstructor => JsonContainerType.Constructor, 
				_ => throw JsonReaderException.Create(this, "Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token)), 
			};
		}

		void IDisposable.Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (_currentState != State.Closed && disposing)
			{
				Close();
			}
		}

		public virtual void Close()
		{
			_currentState = State.Closed;
			_tokenType = JsonToken.None;
			_value = null;
		}

		internal void ReadAndAssert()
		{
			if (!Read())
			{
				throw JsonSerializationException.Create(this, "Unexpected end when reading JSON.");
			}
		}

		internal void ReadForTypeAndAssert(JsonContract? contract, bool hasConverter)
		{
			if (!ReadForType(contract, hasConverter))
			{
				throw JsonSerializationException.Create(this, "Unexpected end when reading JSON.");
			}
		}

		internal bool ReadForType(JsonContract? contract, bool hasConverter)
		{
			if (hasConverter)
			{
				return Read();
			}
			switch (contract?.InternalReadType ?? ReadType.Read)
			{
			case ReadType.Read:
				return ReadAndMoveToContent();
			case ReadType.ReadAsInt32:
				ReadAsInt32();
				break;
			case ReadType.ReadAsInt64:
			{
				bool result = ReadAndMoveToContent();
				if (TokenType == JsonToken.Undefined)
				{
					throw JsonReaderException.Create(this, "An undefined token is not a valid {0}.".FormatWith(CultureInfo.InvariantCulture, contract?.UnderlyingType ?? typeof(long)));
				}
				return result;
			}
			case ReadType.ReadAsDecimal:
				ReadAsDecimal();
				break;
			case ReadType.ReadAsDouble:
				ReadAsDouble();
				break;
			case ReadType.ReadAsBytes:
				ReadAsBytes();
				break;
			case ReadType.ReadAsBoolean:
				ReadAsBoolean();
				break;
			case ReadType.ReadAsString:
				ReadAsString();
				break;
			case ReadType.ReadAsDateTime:
				ReadAsDateTime();
				break;
			case ReadType.ReadAsDateTimeOffset:
				ReadAsDateTimeOffset();
				break;
			default:
				throw new ArgumentOutOfRangeException();
			}
			return TokenType != JsonToken.None;
		}

		internal bool ReadAndMoveToContent()
		{
			if (Read())
			{
				return MoveToContent();
			}
			return false;
		}

		internal bool MoveToContent()
		{
			JsonToken tokenType = TokenType;
			while (tokenType == JsonToken.None || tokenType == JsonToken.Comment)
			{
				if (!Read())
				{
					return false;
				}
				tokenType = TokenType;
			}
			return true;
		}

		private JsonToken GetContentToken()
		{
			JsonToken tokenType;
			do
			{
				if (!Read())
				{
					SetToken(JsonToken.None);
					return JsonToken.None;
				}
				tokenType = TokenType;
			}
			while (tokenType == JsonToken.Comment);
			return tokenType;
		}
	}
	[Serializable]
	public class JsonReaderException : JsonException
	{
		public int LineNumber { get; }

		public int LinePosition { get; }

		public string? Path { get; }

		public JsonReaderException()
		{
		}

		public JsonReaderException(string message)
			: base(message)
		{
		}

		public JsonReaderException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		public JsonReaderException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		public JsonReaderException(string message, string path, int lineNumber, int linePosition, Exception? innerException)
			: base(message, innerException)
		{
			Path = path;
			LineNumber = lineNumber;
			LinePosition = linePosition;
		}

		internal static JsonReaderException Create(JsonReader reader, string message)
		{
			return Create(reader, message, null);
		}

		internal static JsonReaderException Create(JsonReader reader, string message, Exception? ex)
		{
			return Create(reader as IJsonLineInfo, reader.Path, message, ex);
		}

		internal static JsonReaderException Create(IJsonLineInfo? lineInfo, string path, string message, Exception? ex)
		{
			message = JsonPosition.FormatMessage(lineInfo, path, message);
			int lineNumber;
			int linePosition;
			if (lineInfo != null && lineInfo.HasLineInfo())
			{
				lineNumber = lineInfo.LineNumber;
				linePosition = lineInfo.LinePosition;
			}
			else
			{
				lineNumber = 0;
				linePosition = 0;
			}
			return new JsonReaderException(message, path, lineNumber, linePosition, ex);
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public sealed class JsonRequiredAttribute : Attribute
	{
	}
	[Serializable]
	public class JsonSerializationException : JsonException
	{
		public int LineNumber { get; }

		public int LinePosition { get; }

		public string? Path { get; }

		public JsonSerializationException()
		{
		}

		public JsonSerializationException(string message)
			: base(message)
		{
		}

		public JsonSerializationException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		public JsonSerializationException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		public JsonSerializationException(string message, string path, int lineNumber, int linePosition, Exception? innerException)
			: base(message, innerException)
		{
			Path = path;
			LineNumber = lineNumber;
			LinePosition = linePosition;
		}

		internal static JsonSerializationException Create(JsonReader reader, string message)
		{
			return Create(reader, message, null);
		}

		internal static JsonSerializationException Create(JsonReader reader, string message, Exception? ex)
		{
			return Create(reader as IJsonLineInfo, reader.Path, message, ex);
		}

		internal static JsonSerializationException Create(IJsonLineInfo? lineInfo, string path, string message, Exception? ex)
		{
			message = JsonPosition.FormatMessage(lineInfo, path, message);
			int lineNumber;
			int linePosition;
			if (lineInfo != null && lineInfo.HasLineInfo())
			{
				lineNumber = lineInfo.LineNumber;
				linePosition = lineInfo.LinePosition;
			}
			else
			{
				lineNumber = 0;
				linePosition = 0;
			}
			return new JsonSerializationException(message, path, lineNumber, linePosition, ex);
		}
	}
	public class JsonSerializer
	{
		internal TypeNameHandling _typeNameHandling;

		internal TypeNameAssemblyFormatHandling _typeNameAssemblyFormatHandling;

		internal PreserveReferencesHandling _preserveReferencesHandling;

		internal ReferenceLoopHandling _referenceLoopHandling;

		internal MissingMemberHandling _missingMemberHandling;

		internal ObjectCreationHandling _objectCreationHandling;

		internal NullValueHandling _nullValueHandling;

		internal DefaultValueHandling _defaultValueHandling;

		internal ConstructorHandling _constructorHandling;

		internal MetadataPropertyHandling _metadataPropertyHandling;

		internal JsonConverterCollection? _converters;

		internal IContractResolver _contractResolver;

		internal ITraceWriter? _traceWriter;

		internal IEqualityComparer? _equalityComparer;

		internal ISerializationBinder _serializationBinder;

		internal StreamingContext _context;

		private IReferenceResolver? _referenceResolver;

		private Formatting? _formatting;

		private DateFormatHandling? _dateFormatHandling;

		private DateTimeZoneHandling? _dateTimeZoneHandling;

		private DateParseHandling? _dateParseHandling;

		private FloatFormatHandling? _floatFormatHandling;

		private FloatParseHandling? _floatParseHandling;

		private StringEscapeHandling? _stringEscapeHandling;

		private CultureInfo _culture;

		private int? _maxDepth;

		private bool _maxDepthSet;

		private bool? _checkAdditionalContent;

		private string? _dateFormatString;

		private bool _dateFormatStringSet;

		public virtual IReferenceResolver? ReferenceResolver
		{
			get
			{
				return GetReferenceResolver();
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value", "Reference resolver cannot be null.");
				}
				_referenceResolver = value;
			}
		}

		[Obsolete("Binder is obsolete. Use SerializationBinder instead.")]
		public virtual SerializationBinder Binder
		{
			get
			{
				if (_serializationBinder is SerializationBinder result)
				{
					return result;
				}
				if (_serializationBinder is SerializationBinderAdapter serializationBinderAdapter)
				{
					return serializationBinderAdapter.SerializationBinder;
				}
				throw new InvalidOperationException("Cannot get SerializationBinder because an ISerializationBinder was previously set.");
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value", "Serialization binder cannot be null.");
				}
				_serializationBinder = (value as ISerializationBinder) ?? new SerializationBinderAdapter(value);
			}
		}

		public virtual ISerializationBinder SerializationBinder
		{
			get
			{
				return _serializationBinder;
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value", "Serialization binder cannot be null.");
				}
				_serializationBinder = value;
			}
		}

		public virtual ITraceWriter? TraceWriter
		{
			get
			{
				return _traceWriter;
			}
			set
			{
				_traceWriter = value;
			}
		}

		public virtual IEqualityComparer? EqualityComparer
		{
			get
			{
				return _equalityComparer;
			}
			set
			{
				_equalityComparer = value;
			}
		}

		public virtual TypeNameHandling TypeNameHandling
		{
			get
			{
				return _typeNameHandling;
			}
			set
			{
				if (value < TypeNameHandling.None || value > TypeNameHandling.Auto)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_typeNameHandling = value;
			}
		}

		[Obsolete("TypeNameAssemblyFormat is obsolete. Use TypeNameAssemblyFormatHandling instead.")]
		public virtual FormatterAssemblyStyle TypeNameAssemblyFormat
		{
			get
			{
				return (FormatterAssemblyStyle)_typeNameAssemblyFormatHandling;
			}
			set
			{
				if (value < FormatterAssemblyStyle.Simple || value > FormatterAssemblyStyle.Full)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_typeNameAssemblyFormatHandling = (TypeNameAssemblyFormatHandling)value;
			}
		}

		public virtual TypeNameAssemblyFormatHandling TypeNameAssemblyFormatHandling
		{
			get
			{
				return _typeNameAssemblyFormatHandling;
			}
			set
			{
				if (value < TypeNameAssemblyFormatHandling.Simple || value > TypeNameAssemblyFormatHandling.Full)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_typeNameAssemblyFormatHandling = value;
			}
		}

		public virtual PreserveReferencesHandling PreserveReferencesHandling
		{
			get
			{
				return _preserveReferencesHandling;
			}
			set
			{
				if (value < PreserveReferencesHandling.None || value > PreserveReferencesHandling.All)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_preserveReferencesHandling = value;
			}
		}

		public virtual ReferenceLoopHandling ReferenceLoopHandling
		{
			get
			{
				return _referenceLoopHandling;
			}
			set
			{
				if (value < ReferenceLoopHandling.Error || value > ReferenceLoopHandling.Serialize)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_referenceLoopHandling = value;
			}
		}

		public virtual MissingMemberHandling MissingMemberHandling
		{
			get
			{
				return _missingMemberHandling;
			}
			set
			{
				if (value < MissingMemberHandling.Ignore || value > MissingMemberHandling.Error)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_missingMemberHandling = value;
			}
		}

		public virtual NullValueHandling NullValueHandling
		{
			get
			{
				return _nullValueHandling;
			}
			set
			{
				if (value < NullValueHandling.Include || value > NullValueHandling.Ignore)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_nullValueHandling = value;
			}
		}

		public virtual DefaultValueHandling DefaultValueHandling
		{
			get
			{
				return _defaultValueHandling;
			}
			set
			{
				if (value < DefaultValueHandling.Include || value > DefaultValueHandling.IgnoreAndPopulate)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_defaultValueHandling = value;
			}
		}

		public virtual ObjectCreationHandling ObjectCreationHandling
		{
			get
			{
				return _objectCreationHandling;
			}
			set
			{
				if (value < ObjectCreationHandling.Auto || value > ObjectCreationHandling.Replace)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_objectCreationHandling = value;
			}
		}

		public virtual ConstructorHandling ConstructorHandling
		{
			get
			{
				return _constructorHandling;
			}
			set
			{
				if (value < ConstructorHandling.Default || value > ConstructorHandling.AllowNonPublicDefaultConstructor)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_constructorHandling = value;
			}
		}

		public virtual MetadataPropertyHandling MetadataPropertyHandling
		{
			get
			{
				return _metadataPropertyHandling;
			}
			set
			{
				if (value < MetadataPropertyHandling.Default || value > MetadataPropertyHandling.Ignore)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_metadataPropertyHandling = value;
			}
		}

		public virtual JsonConverterCollection Converters
		{
			get
			{
				if (_converters == null)
				{
					_converters = new JsonConverterCollection();
				}
				return _converters;
			}
		}

		public virtual IContractResolver ContractResolver
		{
			get
			{
				return _contractResolver;
			}
			set
			{
				_contractResolver = value ?? DefaultContractResolver.Instance;
			}
		}

		public virtual StreamingContext Context
		{
			get
			{
				return _context;
			}
			set
			{
				_context = value;
			}
		}

		public virtual Formatting Formatting
		{
			get
			{
				return _formatting.GetValueOrDefault();
			}
			set
			{
				_formatting = value;
			}
		}

		public virtual DateFormatHandling DateFormatHandling
		{
			get
			{
				return _dateFormatHandling.GetValueOrDefault();
			}
			set
			{
				_dateFormatHandling = value;
			}
		}

		public virtual DateTimeZoneHandling DateTimeZoneHandling
		{
			get
			{
				return _dateTimeZoneHandling ?? DateTimeZoneHandling.RoundtripKind;
			}
			set
			{
				_dateTimeZoneHandling = value;
			}
		}

		public virtual DateParseHandling DateParseHandling
		{
			get
			{
				return _dateParseHandling ?? DateParseHandling.DateTime;
			}
			set
			{
				_dateParseHandling = value;
			}
		}

		public virtual FloatParseHandling FloatParseHandling
		{
			get
			{
				return _floatParseHandling.GetValueOrDefault();
			}
			set
			{
				_floatParseHandling = value;
			}
		}

		public virtual FloatFormatHandling FloatFormatHandling
		{
			get
			{
				return _floatFormatHandling.GetValueOrDefault();
			}
			set
			{
				_floatFormatHandling = value;
			}
		}

		public virtual StringEscapeHandling StringEscapeHandling
		{
			get
			{
				return _stringEscapeHandling.GetValueOrDefault();
			}
			set
			{
				_stringEscapeHandling = value;
			}
		}

		public virtual string DateFormatString
		{
			get
			{
				return _dateFormatString ?? "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
			}
			set
			{
				_dateFormatString = value;
				_dateFormatStringSet = true;
			}
		}

		public virtual CultureInfo Culture
		{
			get
			{
				return _culture ?? JsonSerializerSettings.DefaultCulture;
			}
			set
			{
				_culture = value;
			}
		}

		public virtual int? MaxDepth
		{
			get
			{
				return _maxDepth;
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentException("Value must be positive.", "value");
				}
				_maxDepth = value;
				_maxDepthSet = true;
			}
		}

		public virtual bool CheckAdditionalContent
		{
			get
			{
				return _checkAdditionalContent.GetValueOrDefault();
			}
			set
			{
				_checkAdditionalContent = value;
			}
		}

		public virtual event EventHandler<Newtonsoft.Json.Serialization.ErrorEventArgs>? Error;

		internal bool IsCheckAdditionalContentSet()
		{
			return _checkAdditionalContent.HasValue;
		}

		public JsonSerializer()
		{
			_referenceLoopHandling = ReferenceLoopHandling.Error;
			_missingMemberHandling = MissingMemberHandling.Ignore;
			_nullValueHandling = NullValueHandling.Include;
			_defaultValueHandling = DefaultValueHandling.Include;
			_objectCreationHandling = ObjectCreationHandling.Auto;
			_preserveReferencesHandling = PreserveReferencesHandling.None;
			_constructorHandling = ConstructorHandling.Default;
			_typeNameHandling = TypeNameHandling.None;
			_metadataPropertyHandling = MetadataPropertyHandling.Default;
			_context = JsonSerializerSettings.DefaultContext;
			_serializationBinder = DefaultSerializationBinder.Instance;
			_culture = JsonSerializerSettings.DefaultCulture;
			_contractResolver = DefaultContractResolver.Instance;
		}

		public static JsonSerializer Create()
		{
			return new JsonSerializer();
		}

		public static JsonSerializer Create(JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = Create();
			if (settings != null)
			{
				ApplySerializerSettings(jsonSerializer, settings);
			}
			return jsonSerializer;
		}

		public static JsonSerializer CreateDefault()
		{
			return Create(JsonConvert.DefaultSettings?.Invoke());
		}

		public static JsonSerializer CreateDefault(JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = CreateDefault();
			if (settings != null)
			{
				ApplySerializerSettings(jsonSerializer, settings);
			}
			return jsonSerializer;
		}

		private static void ApplySerializerSettings(JsonSerializer serializer, JsonSerializerSettings settings)
		{
			if (!CollectionUtils.IsNullOrEmpty(settings.Converters))
			{
				for (int i = 0; i < settings.Converters.Count; i++)
				{
					serializer.Converters.Insert(i, settings.Converters[i]);
				}
			}
			if (settings._typeNameHandling.HasValue)
			{
				serializer.TypeNameHandling = settings.TypeNameHandling;
			}
			if (settings._metadataPropertyHandling.HasValue)
			{
				serializer.MetadataPropertyHandling = settings.MetadataPropertyHandling;
			}
			if (settings._typeNameAssemblyFormatHandling.HasValue)
			{
				serializer.TypeNameAssemblyFormatHandling = settings.TypeNameAssemblyFormatHandling;
			}
			if (settings._preserveReferencesHandling.HasValue)
			{
				serializer.PreserveReferencesHandling = settings.PreserveReferencesHandling;
			}
			if (settings._referenceLoopHandling.HasValue)
			{
				serializer.ReferenceLoopHandling = settings.ReferenceLoopHandling;
			}
			if (settings._missingMemberHandling.HasValue)
			{
				serializer.MissingMemberHandling = settings.MissingMemberHandling;
			}
			if (settings._objectCreationHandling.HasValue)
			{
				serializer.ObjectCreationHandling = settings.ObjectCreationHandling;
			}
			if (settings._nullValueHandling.HasValue)
			{
				serializer.NullValueHandling = settings.NullValueHandling;
			}
			if (settings._defaultValueHandling.HasValue)
			{
				serializer.DefaultValueHandling = settings.DefaultValueHandling;
			}
			if (settings._constructorHandling.HasValue)
			{
				serializer.ConstructorHandling = settings.ConstructorHandling;
			}
			if (settings._context.HasValue)
			{
				serializer.Context = settings.Context;
			}
			if (settings._checkAdditionalContent.HasValue)
			{
				serializer._checkAdditionalContent = settings._checkAdditionalContent;
			}
			if (settings.Error != null)
			{
				serializer.Error += settings.Error;
			}
			if (settings.ContractResolver != null)
			{
				serializer.ContractResolver = settings.ContractResolver;
			}
			if (settings.ReferenceResolverProvider != null)
			{
				serializer.ReferenceResolver = settings.ReferenceResolverProvider();
			}
			if (settings.TraceWriter != null)
			{
				serializer.TraceWriter = settings.TraceWriter;
			}
			if (settings.EqualityComparer != null)
			{
				serializer.EqualityComparer = settings.EqualityComparer;
			}
			if (settings.SerializationBinder != null)
			{
				serializer.SerializationBinder = settings.SerializationBinder;
			}
			if (settings._formatting.HasValue)
			{
				serializer._formatting = settings._formatting;
			}
			if (settings._dateFormatHandling.HasValue)
			{
				serializer._dateFormatHandling = settings._dateFormatHandling;
			}
			if (settings._dateTimeZoneHandling.HasValue)
			{
				serializer._dateTimeZoneHandling = settings._dateTimeZoneHandling;
			}
			if (settings._dateParseHandling.HasValue)
			{
				serializer._dateParseHandling = settings._dateParseHandling;
			}
			if (settings._dateFormatStringSet)
			{
				serializer._dateFormatString = settings._dateFormatString;
				serializer._dateFormatStringSet = settings._dateFormatStringSet;
			}
			if (settings._floatFormatHandling.HasValue)
			{
				serializer._floatFormatHandling = settings._floatFormatHandling;
			}
			if (settings._floatParseHandling.HasValue)
			{
				serializer._floatParseHandling = settings._floatParseHandling;
			}
			if (settings._stringEscapeHandling.HasValue)
			{
				serializer._stringEscapeHandling = settings._stringEscapeHandling;
			}
			if (settings._culture != null)
			{
				serializer._culture = settings._culture;
			}
			if (settings._maxDepthSet)
			{
				serializer._maxDepth = settings._maxDepth;
				serializer._maxDepthSet = settings._maxDepthSet;
			}
		}

		[DebuggerStepThrough]
		public void Populate(TextReader reader, object target)
		{
			Populate(new JsonTextReader(reader), target);
		}

		[DebuggerStepThrough]
		public void Populate(JsonReader reader, object target)
		{
			PopulateInternal(reader, target);
		}

		internal virtual void PopulateInternal(JsonReader reader, object target)
		{
			ValidationUtils.ArgumentNotNull(reader, "reader");
			ValidationUtils.ArgumentNotNull(target, "target");
			SetupReader(reader, out CultureInfo previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string previousDateFormatString);
			TraceJsonReader traceJsonReader = ((TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) ? CreateTraceJsonReader(reader) : null);
			new JsonSerializerInternalReader(this).Populate(traceJsonReader ?? reader, target);
			if (traceJsonReader != null)
			{
				TraceWriter.Trace(TraceLevel.Verbose, traceJsonReader.GetDeserializedJsonMessage(), null);
			}
			ResetReader(reader, previousCulture, previousDateTimeZoneHandling, previousDateParseHandling, previousFloatParseHandling, previousMaxDepth, previousDateFormatString);
		}

		[DebuggerStepThrough]
		public object? Deserialize(JsonReader reader)
		{
			return Deserialize(reader, null);
		}

		[DebuggerStepThrough]
		public object? Deserialize(TextReader reader, Type objectType)
		{
			return Deserialize(new JsonTextReader(reader), objectType);
		}

		[DebuggerStepThrough]
		public T? Deserialize<T>(JsonReader reader)
		{
			return (T)Deserialize(reader, typeof(T));
		}

		[DebuggerStepThrough]
		public object? Deserialize(JsonReader reader, Type? objectType)
		{
			return DeserializeInternal(reader, objectType);
		}

		internal virtual object? DeserializeInternal(JsonReader reader, Type? objectType)
		{
			ValidationUtils.ArgumentNotNull(reader, "reader");
			SetupReader(reader, out CultureInfo previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string previousDateFormatString);
			TraceJsonReader traceJsonReader = ((TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) ? CreateTraceJsonReader(reader) : null);
			object? result = new JsonSerializerInternalReader(this).Deserialize(traceJsonReader ?? reader, objectType, CheckAdditionalContent);
			if (traceJsonReader != null)
			{
				TraceWriter.Trace(TraceLevel.Verbose, traceJsonReader.GetDeserializedJsonMessage(), null);
			}
			ResetReader(reader, previousCulture, previousDateTimeZoneHandling, previousDateParseHandling, previousFloatParseHandling, previousMaxDepth, previousDateFormatString);
			return result;
		}

		internal void SetupReader(JsonReader reader, out CultureInfo? previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string? previousDateFormatString)
		{
			if (_culture != null && !_culture.Equals(reader.Culture))
			{
				previousCulture = reader.Culture;
				reader.Culture = _culture;
			}
			else
			{
				previousCulture = null;
			}
			if (_dateTimeZoneHandling.HasValue && reader.DateTimeZoneHandling != _dateTimeZoneHandling)
			{
				previousDateTimeZoneHandling = reader.DateTimeZoneHandling;
				reader.DateTimeZoneHandling = _dateTimeZoneHandling.GetValueOrDefault();
			}
			else
			{
				previousDateTimeZoneHandling = null;
			}
			if (_dateParseHandling.HasValue && reader.DateParseHandling != _dateParseHandling)
			{
				previousDateParseHandling = reader.DateParseHandling;
				reader.DateParseHandling = _dateParseHandling.GetValueOrDefault();
			}
			else
			{
				previousDateParseHandling = null;
			}
			if (_floatParseHandling.HasValue && reader.FloatParseHandling != _floatParseHandling)
			{
				previousFloatParseHandling = reader.FloatParseHandling;
				reader.FloatParseHandling = _floatParseHandling.GetValueOrDefault();
			}
			else
			{
				previousFloatParseHandling = null;
			}
			if (_maxDepthSet && reader.MaxDepth != _maxDepth)
			{
				previousMaxDepth = reader.MaxDepth;
				reader.MaxDepth = _maxDepth;
			}
			else
			{
				previousMaxDepth = null;
			}
			if (_dateFormatStringSet && reader.DateFormatString != _dateFormatString)
			{
				previousDateFormatString = reader.DateFormatString;
				reader.DateFormatString = _dateFormatString;
			}
			else
			{
				previousDateFormatString = null;
			}
			if (reader is JsonTextReader jsonTextReader && jsonTextReader.PropertyNameTable == null && _contractResolver is DefaultContractResolver defaultContractResolver)
			{
				jsonTextReader.PropertyNameTable = defaultContractResolver.GetNameTable();
			}
		}

		private void ResetReader(JsonReader reader, CultureInfo? previousCulture, DateTimeZoneHandling? previousDateTimeZoneHandling, DateParseHandling? previousDateParseHandling, FloatParseHandling? previousFloatParseHandling, int? previousMaxDepth, string? previousDateFormatString)
		{
			if (previousCulture != null)
			{
				reader.Culture = previousCulture;
			}
			if (previousDateTimeZoneHandling.HasValue)
			{
				reader.DateTimeZoneHandling = previousDateTimeZoneHandling.GetValueOrDefault();
			}
			if (previousDateParseHandling.HasValue)
			{
				reader.DateParseHandling = previousDateParseHandling.GetValueOrDefault();
			}
			if (previousFloatParseHandling.HasValue)
			{
				reader.FloatParseHandling = previousFloatParseHandling.GetValueOrDefault();
			}
			if (_maxDepthSet)
			{
				reader.MaxDepth = previousMaxDepth;
			}
			if (_dateFormatStringSet)
			{
				reader.DateFormatString = previousDateFormatString;
			}
			if (reader is JsonTextReader jsonTextReader && jsonTextReader.PropertyNameTable != null && _contractResolver is DefaultContractResolver defaultContractResolver && jsonTextReader.PropertyNameTable == defaultContractResolver.GetNameTable())
			{
				jsonTextReader.PropertyNameTable = null;
			}
		}

		public void Serialize(TextWriter textWriter, object? value)
		{
			Serialize(new JsonTextWriter(textWriter), value);
		}

		public void Serialize(JsonWriter jsonWriter, object? value, Type? objectType)
		{
			SerializeInternal(jsonWriter, value, objectType);
		}

		public void Serialize(TextWriter textWriter, object? value, Type objectType)
		{
			Serialize(new JsonTextWriter(textWriter), value, objectType);
		}

		public void Serialize(JsonWriter jsonWriter, object? value)
		{
			SerializeInternal(jsonWriter, value, null);
		}

		private TraceJsonReader CreateTraceJsonReader(JsonReader reader)
		{
			TraceJsonReader traceJsonReader = new TraceJsonReader(reader);
			if (reader.TokenType != 0)
			{
				traceJsonReader.WriteCurrentToken();
			}
			return traceJsonReader;
		}

		internal virtual void SerializeInternal(JsonWriter jsonWriter, object? value, Type? objectType)
		{
			ValidationUtils.ArgumentNotNull(jsonWriter, "jsonWriter");
			Formatting? formatting = null;
			if (_formatting.HasValue && jsonWriter.Formatting != _formatting)
			{
				formatting = jsonWriter.Formatting;
				jsonWriter.Formatting = _formatting.GetValueOrDefault();
			}
			DateFormatHandling? dateFormatHandling = null;
			if (_dateFormatHandling.HasValue && jsonWriter.DateFormatHandling != _dateFormatHandling)
			{
				dateFormatHandling = jsonWriter.DateFormatHandling;
				jsonWriter.DateFormatHandling = _dateFormatHandling.GetValueOrDefault();
			}
			DateTimeZoneHandling? dateTimeZoneHandling = null;
			if (_dateTimeZoneHandling.HasValue && jsonWriter.DateTimeZoneHandling != _dateTimeZoneHandling)
			{
				dateTimeZoneHandling = jsonWriter.DateTimeZoneHandling;
				jsonWriter.DateTimeZoneHandling = _dateTimeZoneHandling.GetValueOrDefault();
			}
			FloatFormatHandling? floatFormatHandling = null;
			if (_floatFormatHandling.HasValue && jsonWriter.FloatFormatHandling != _floatFormatHandling)
			{
				floatFormatHandling = jsonWriter.FloatFormatHandling;
				jsonWriter.FloatFormatHandling = _floatFormatHandling.GetValueOrDefault();
			}
			StringEscapeHandling? stringEscapeHandling = null;
			if (_stringEscapeHandling.HasValue && jsonWriter.StringEscapeHandling != _stringEscapeHandling)
			{
				stringEscapeHandling = jsonWriter.StringEscapeHandling;
				jsonWriter.StringEscapeHandling = _stringEscapeHandling.GetValueOrDefault();
			}
			CultureInfo cultureInfo = null;
			if (_culture != null && !_culture.Equals(jsonWriter.Culture))
			{
				cultureInfo = jsonWriter.Culture;
				jsonWriter.Culture = _culture;
			}
			string dateFormatString = null;
			if (_dateFormatStringSet && jsonWriter.DateFormatString != _dateFormatString)
			{
				dateFormatString = jsonWriter.DateFormatString;
				jsonWriter.DateFormatString = _dateFormatString;
			}
			TraceJsonWriter traceJsonWriter = ((TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) ? new TraceJsonWriter(jsonWriter) : null);
			new JsonSerializerInternalWriter(this).Serialize(traceJsonWriter ?? jsonWriter, value, objectType);
			if (traceJsonWriter != null)
			{
				TraceWriter.Trace(TraceLevel.Verbose, traceJsonWriter.GetSerializedJsonMessage(), null);
			}
			if (formatting.HasValue)
			{
				jsonWriter.Formatting = formatting.GetValueOrDefault();
			}
			if (dateFormatHandling.HasValue)
			{
				jsonWriter.DateFormatHandling = dateFormatHandling.GetValueOrDefault();
			}
			if (dateTimeZoneHandling.HasValue)
			{
				jsonWriter.DateTimeZoneHandling = dateTimeZoneHandling.GetValueOrDefault();
			}
			if (floatFormatHandling.HasValue)
			{
				jsonWriter.FloatFormatHandling = floatFormatHandling.GetValueOrDefault();
			}
			if (stringEscapeHandling.HasValue)
			{
				jsonWriter.StringEscapeHandling = stringEscapeHandling.GetValueOrDefault();
			}
			if (_dateFormatStringSet)
			{
				jsonWriter.DateFormatString = dateFormatString;
			}
			if (cultureInfo != null)
			{
				jsonWriter.Culture = cultureInfo;
			}
		}

		internal IReferenceResolver GetReferenceResolver()
		{
			if (_referenceResolver == null)
			{
				_referenceResolver = new DefaultReferenceResolver();
			}
			return _referenceResolver;
		}

		internal JsonConverter? GetMatchingConverter(Type type)
		{
			return GetMatchingConverter(_converters, type);
		}

		internal static JsonConverter? GetMatchingConverter(IList<JsonConverter>? converters, Type objectType)
		{
			if (converters != null)
			{
				for (int i = 0; i < converters.Count; i++)
				{
					JsonConverter jsonConverter = converters[i];
					if (jsonConverter.CanConvert(objectType))
					{
						return jsonConverter;
					}
				}
			}
			return null;
		}

		internal void OnError(Newtonsoft.Json.Serialization.ErrorEventArgs e)
		{
			this.Error?.Invoke(this, e);
		}
	}
	public class JsonSerializerSettings
	{
		internal const ReferenceLoopHandling DefaultReferenceLoopHandling = ReferenceLoopHandling.Error;

		internal const MissingMemberHandling DefaultMissingMemberHandling = MissingMemberHandling.Ignore;

		internal const NullValueHandling DefaultNullValueHandling = NullValueHandling.Include;

		internal const DefaultValueHandling DefaultDefaultValueHandling = DefaultValueHandling.Include;

		internal const ObjectCreationHandling DefaultObjectCreationHandling = ObjectCreationHandling.Auto;

		internal const PreserveReferencesHandling DefaultPreserveReferencesHandling = PreserveReferencesHandling.None;

		internal const ConstructorHandling DefaultConstructorHandling = ConstructorHandling.Default;

		internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None;

		internal const MetadataPropertyHandling DefaultMetadataPropertyHandling = MetadataPropertyHandling.Default;

		internal static readonly StreamingContext DefaultContext;

		internal const Formatting DefaultFormatting = Formatting.None;

		internal const DateFormatHandling DefaultDateFormatHandling = DateFormatHandling.IsoDateFormat;

		internal const DateTimeZoneHandling DefaultDateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;

		internal const DateParseHandling DefaultDateParseHandling = DateParseHandling.DateTime;

		internal const FloatParseHandling DefaultFloatParseHandling = FloatParseHandling.Double;

		internal const FloatFormatHandling DefaultFloatFormatHandling = FloatFormatHandling.String;

		internal const StringEscapeHandling DefaultStringEscapeHandling = StringEscapeHandling.Default;

		internal const TypeNameAssemblyFormatHandling DefaultTypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple;

		internal static readonly CultureInfo DefaultCulture;

		internal const bool DefaultCheckAdditionalContent = false;

		internal const string DefaultDateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";

		internal const int DefaultMaxDepth = 64;

		internal Formatting? _formatting;

		internal DateFormatHandling? _dateFormatHandling;

		internal DateTimeZoneHandling? _dateTimeZoneHandling;

		internal DateParseHandling? _dateParseHandling;

		internal FloatFormatHandling? _floatFormatHandling;

		internal FloatParseHandling? _floatParseHandling;

		internal StringEscapeHandling? _stringEscapeHandling;

		internal CultureInfo? _culture;

		internal bool? _checkAdditionalContent;

		internal int? _maxDepth;

		internal bool _maxDepthSet;

		internal string? _dateFormatString;

		internal bool _dateFormatStringSet;

		internal TypeNameAssemblyFormatHandling? _typeNameAssemblyFormatHandling;

		internal DefaultValueHandling? _defaultValueHandling;

		internal PreserveReferencesHandling? _preserveReferencesHandling;

		internal NullValueHandling? _nullValueHandling;

		internal ObjectCreationHandling? _objectCreationHandling;

		internal MissingMemberHandling? _missingMemberHandling;

		internal ReferenceLoopHandling? _referenceLoopHandling;

		internal StreamingContext? _context;

		internal ConstructorHandling? _constructorHandling;

		internal TypeNameHandling? _typeNameHandling;

		internal MetadataPropertyHandling? _metadataPropertyHandling;

		public ReferenceLoopHandling ReferenceLoopHandling
		{
			get
			{
				return _referenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_referenceLoopHandling = value;
			}
		}

		public MissingMemberHandling MissingMemberHandling
		{
			get
			{
				return _missingMemberHandling.GetValueOrDefault();
			}
			set
			{
				_missingMemberHandling = value;
			}
		}

		public ObjectCreationHandling ObjectCreationHandling
		{
			get
			{
				return _objectCreationHandling.GetValueOrDefault();
			}
			set
			{
				_objectCreationHandling = value;
			}
		}

		public NullValueHandling NullValueHandling
		{
			get
			{
				return _nullValueHandling.GetValueOrDefault();
			}
			set
			{
				_nullValueHandling = value;
			}
		}

		public DefaultValueHandling DefaultValueHandling
		{
			get
			{
				return _defaultValueHandling.GetValueOrDefault();
			}
			set
			{
				_defaultValueHandling = value;
			}
		}

		public IList<JsonConverter> Converters { get; set; }

		public PreserveReferencesHandling PreserveReferencesHandling
		{
			get
			{
				return _preserveReferencesHandling.GetValueOrDefault();
			}
			set
			{
				_preserveReferencesHandling = value;
			}
		}

		public TypeNameHandling TypeNameHandling
		{
			get
			{
				return _typeNameHandling.GetValueOrDefault();
			}
			set
			{

BepInEx/plugins/ValheimSagas/Sagas.Core.dll

Decompiled a day ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using LiteDB;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sagas.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d2ac062f97621ac92b916586f737cf12791d6309")]
[assembly: AssemblyProduct("Sagas.Core")]
[assembly: AssemblyTitle("Sagas.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimSagas
{
	public sealed class SagaService : IDisposable
	{
		private sealed class PinAssembly
		{
			internal DateTime Started = DateTime.UtcNow;

			internal string Revision = "";

			internal MapPins?[] Parts = Array.Empty<MapPins>();

			internal Action<bool>?[] Callbacks = Array.Empty<Action<bool>>();
		}

		private sealed class BackgroundRequest
		{
			public string World { get; set; } = "";

			public string Biome { get; set; } = "";
		}

		private readonly object versionGate = new object();

		private DateTime versionRead;

		private object? versionInfo;

		private static readonly (string Key, string Name, int Order, string Biome)[] KnownBosses = BossCatalog.Bosses;

		private readonly string mapInstance = Guid.NewGuid().ToString("N");

		private readonly object mapMetaGate = new object();

		private string mapMetaKey = "";

		private object? mapMetaCache;

		private readonly Dictionary<string, PinAssembly> pinAssemblies = new Dictionary<string, PinAssembly>();

		private WorldClock? worldClock;

		private static readonly Dictionary<string, string> BackgroundBiomes = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			{ "meadows", "Meadows" },
			{ "black-forest", "BlackForest" },
			{ "swamp", "Swamp" },
			{ "mountain", "Mountain" },
			{ "plains", "Plains" },
			{ "mistlands", "Mistlands" },
			{ "ashlands", "Ashlands" },
			{ "deep-north", "DeepNorth" }
		};

		private static readonly string[] BackgroundOrder = new string[8] { "meadows", "black-forest", "swamp", "mountain", "plains", "mistlands", "ashlands", "deep-north" };

		public const int MaximumPortraitBytes = 1048576;

		public const int MaximumPortraitWidth = 1024;

		public const int MaximumPortraitHeight = 1536;

		public const int MaximumIconBytes = 49152;

		private readonly SagaOptions options;

		private readonly SagaStore store;

		private readonly BlockingCollection<Action> queue;

		private readonly CancellationTokenSource stop = new CancellationTokenSource();

		private readonly SagaDiagnostics diagnostics;

		private string? errorId;

		private readonly JsonSerializerSettings json = new JsonSerializerSettings
		{
			ContractResolver = (IContractResolver)new CamelCasePropertyNamesContractResolver(),
			DateTimeZoneHandling = (DateTimeZoneHandling)1
		};

		private readonly SemaphoreSlim httpSlots = new SemaphoreSlim(8);

		private Task? writer;

		private Task? web;

		private Task? lore;

		private HttpListener? listener;

		private bool started;

		private bool disposed;

		private long rejected;

		private string? failure;

		private DateTime nextRetention = DateTime.MinValue;

		private int gzipDisabled;

		private readonly HttpClient? loreClient;

		private static readonly Dictionary<string, string> StaticFiles = BuildStaticFiles();

		public long Rejected => Interlocked.Read(in rejected);

		public bool WebsiteListening
		{
			get
			{
				if (listener != null)
				{
					return listener.IsListening;
				}
				return false;
			}
		}

		public SagaStore Store => store;

		private static bool OrdinaryBoss(SagaEvent e)
		{
			if (e.Kind == "kill" && e.Boss && !e.NemesisBoss)
			{
				return !BossCatalog.IntermediatePhase(e.Prefab);
			}
			return false;
		}

		private static string[] AdventureTeam(SagaEvent e, Dictionary<string, PlayerSnapshot> players)
		{
			return e.Contributors.Concat(new string[1] { e.PlayerId }).Where(players.ContainsKey).Distinct<string>(StringComparer.Ordinal)
				.ToArray();
		}

		private AdventureMoment AdventureProjection(string world, SagaEvent e, Dictionary<string, PlayerSnapshot> players, HashSet<string> owners)
		{
			bool flag = players.ContainsKey(e.PlayerId) && owners.Contains(e.PlayerId) && e.X.HasValue && e.Z.HasValue && store.KnownPoint(world, owners, e.X.Value, e.Z.Value);
			PlayerSnapshot value;
			return new AdventureMoment
			{
				Id = e.Id,
				Prefab = e.Prefab,
				Biome = e.Biome,
				Utc = e.Utc,
				Kind = e.Kind,
				Name = e.Name,
				PlayerId = (players.ContainsKey(e.PlayerId) ? e.PlayerId : ""),
				PlayerName = (players.TryGetValue(e.PlayerId, out value) ? value.Name : ""),
				Stars = e.Stars,
				Amount = e.Amount,
				Rarity = e.Rarity,
				RarityColor = e.RarityColor,
				Boss = e.Boss,
				NemesisBoss = e.NemesisBoss,
				Team = (from id in AdventureTeam(e, players)
					select new LeaderboardPerson
					{
						PlayerId = id,
						Name = players[id].Name
					}).ToArray(),
				X = (flag ? e.X : ((float?)null)),
				Z = (flag ? e.Z : ((float?)null))
			};
		}

		public object Adventures(string world, TimeWindow window, DateTime? since = null, HashSet<string>? selected = null)
		{
			Dictionary<string, PlayerSnapshot> players = (from p in store.Players(world)
				where p.ShareProfile
				select p).ToDictionary<PlayerSnapshot, string>((PlayerSnapshot p) => p.PlayerId, StringComparer.Ordinal);
			HashSet<string> ids = new HashSet<string>(players.Keys.Where((string id) => selected == null || selected.Contains(id)), StringComparer.Ordinal);
			HashSet<string> owners = new HashSet<string>(from p in players.Values
				where p.ShareMap && ids.Contains(p.PlayerId)
				select p.PlayerId, StringComparer.Ordinal);
			IReadOnlyList<SagaEvent> source = store.AnalyticsHistory(world);
			SagaEvent[] events = source.Where((SagaEvent e) => window.Contains(e.Utc) && InScope(e)).ToArray();
			SagaEvent[] bosses = (from e in events.Where(OrdinaryBoss)
				orderby e.Utc
				select e).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal).ToArray();
			LeaderboardBossKill[] records = bosses.Select(Kill).ToArray();
			HashSet<string> earnedKeys = new HashSet<string>(ids.SelectMany((string id) => store.CompletedBossKeys(world, id)), StringComparer.OrdinalIgnoreCase);
			(string, string, int?)[] array = source.Where((SagaEvent e) => OrdinaryBoss(e) && InScope(e)).Select(BossIdentity).ToArray();
			(string, string, int?)[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				(string, string, int?) tuple = array2[num];
				earnedKeys.Add(tuple.Item1);
			}
			var trophies = (from b in (from g in KnownBosses.Select(((string Key, string Name, int Order, string Biome) b) => ((string Key, string Name, int? Order))(Key: b.Key, Name: b.Name, Order: b.Order)).Concat(from b in array
						where !b.Order.HasValue
						select (Key: b.Key, Name: b.Name, Order: b.Order)).GroupBy<(string, string, int?), string>(((string Key, string Name, int? Order) b) => b.Key, StringComparer.OrdinalIgnoreCase)
					select g.First()).Select(delegate((string Key, string Name, int? Order) b)
				{
					LeaderboardBossKill[] array4 = records.Where((LeaderboardBossKill r) => string.Equals(r.BossKey, b.Key, StringComparison.OrdinalIgnoreCase)).ToArray();
					return new
					{
						key = b.Key,
						name = b.Name,
						order = b.Order,
						earned = earnedKeys.Contains(b.Key),
						kills = array4.Length,
						uniquePlayers = (from p in array4.SelectMany((LeaderboardBossKill r) => r.Team)
							select p.PlayerId).Distinct().Count(),
						first = array4.FirstOrDefault(),
						highestStars = (from r in array4
							orderby r.Stars descending, (!r.DurationSeconds.HasValue) ? 1 : 0, r.DurationSeconds, r.Utc
							select r).FirstOrDefault(),
						fastest = (from r in array4
							where r.DurationSeconds.HasValue
							orderby r.DurationSeconds, r.Utc
							select r).FirstOrDefault()
					};
				})
				orderby b.order ?? int.MaxValue
				select b).ThenBy(b => b.key, StringComparer.Ordinal).ToArray();
			var comparisons = players.Values.Where((PlayerSnapshot p) => ids.Contains(p.PlayerId)).OrderBy<PlayerSnapshot, string>((PlayerSnapshot p) => p.Name, StringComparer.Ordinal).Select(delegate(PlayerSnapshot p)
			{
				SagaEvent[] source3 = events.Where((SagaEvent e) => e.PlayerId == p.PlayerId).ToArray();
				SagaEvent[] array4 = source3.Where((SagaEvent e) => e.Kind == "kill").ToArray();
				return new
				{
					playerId = p.PlayerId,
					name = p.Name,
					kills = array4.Length,
					deaths = source3.Count((SagaEvent e) => e.Kind == "death"),
					bossParticipations = bosses.Count((SagaEvent e) => AdventureTeam(e, players).Contains(p.PlayerId)),
					nemesisBossParticipations = events.Count((SagaEvent e) => e.Kind == "kill" && e.NemesisBoss && AdventureTeam(e, players).Contains(p.PlayerId)),
					collected = source3.Where((SagaEvent e) => e.Kind == "collect").Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount)),
					drops = source3.Where((SagaEvent e) => e.Kind == "drop").Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount)),
					highestStars = ((array4.Length == 0) ? ((int?)null) : new int?(array4.Max((SagaEvent e) => e.Stars))),
					bounties = source3.Count((SagaEvent e) => e.Kind == "bounty"),
					rarities = (from e in source3
						where e.Kind == "collect"
						group e by LeaderboardRarity(e.Rarity)).ToDictionary((IGrouping<string, SagaEvent> g) => g.Key, (IGrouping<string, SagaEvent> g) => ((IEnumerable<SagaEvent>)g).Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount)))
				};
			})
				.ToArray();
			DateTime until = DateTime.UtcNow;
			DateTime start = since ?? until.AddDays(-1.0);
			if (start > until)
			{
				start = until;
			}
			SagaEvent[] source2 = source.Where((SagaEvent e) => e.Utc > start && e.Utc <= until && InScope(e)).ToArray();
			SagaEvent[] array3 = (from e in source2
				where OrdinaryBoss(e) || (e.Kind == "kill" && (e.NemesisBoss || e.Stars >= 2)) || e.Kind == "death" || e.Kind == "bounty" || (e.Kind == "collect" && !string.IsNullOrWhiteSpace(e.Rarity))
				orderby e.Utc descending
				select e).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal).ToArray();
			var chapters = (from c in (from c in store.Chapters(world).Concat(store.ServerChapters(world)).Where(delegate(SagaChapter c)
					{
						if (c.Utc > start && c.Utc <= until && GeneratedChapter(c) && ChapterContextVisible(c))
						{
							if (!(c.Scope == "server"))
							{
								return ids.Contains(c.PlayerId);
							}
							if (SharedChapterVisible(c, new HashSet<string>(players.Keys)))
							{
								if (selected != null)
								{
									return c.Participants.Any((SagaParticipant p) => ids.Contains(p.PlayerId));
								}
								return true;
							}
							return false;
						}
						return false;
					})
					orderby c.Utc descending
					select c).Take(12)
				select new
				{
					id = c.Id,
					playerId = c.PlayerId,
					scope = c.Scope,
					title = c.Title,
					utc = c.Utc
				}).ToArray();
			return new
			{
				world = world,
				from = window.From,
				to = window.To,
				since = start,
				partialHistory = (window.From < store.TrackingSince || (store.StatisticsSince.HasValue && window.From < store.StatisticsSince.Value)),
				recap = new
				{
					from = start,
					to = until,
					partialHistory = (start < store.TrackingSince || (store.StatisticsSince.HasValue && start < store.StatisticsSince.Value)),
					totals = new
					{
						kills = source2.Count((SagaEvent e) => e.Kind == "kill" && ids.Contains(e.PlayerId)),
						deaths = source2.Count((SagaEvent e) => e.Kind == "death"),
						bossKills = source2.Count(OrdinaryBoss),
						collected = source2.Where((SagaEvent e) => e.Kind == "collect").Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount))
					},
					moments = (from e in array3.Take(100)
						select AdventureProjection(world, e, players, owners)).ToArray(),
					truncated = (array3.Length > 100),
					chapters = chapters
				},
				trophies = trophies,
				comparisons = comparisons,
				integrations = IntegrationSummary(players.Values.Where((PlayerSnapshot p) => ids.Contains(p.PlayerId)), ActiveSls(world).Installed && ActiveSls(world).NemesisEnabled),
				notes = new string[5] { "Trophies and comparisons follow the selected period; first means first retained victory within that period.", "Boss participation includes recorded contributors and finisher. Private profiles are omitted, so a displayed team can be incomplete. Other kills use finishing-blow credit.", "Collections exclude drops and unknown pickups. Imported exploration does not establish a dated discovery.", "Fastest fights require a complete recorded duration; Deep North multi-phase fights have no complete duration yet.", "Recap follows this browser's last visit, independently of the statistics period; its first visit covers the previous 24 hours." }
			};
			bool InScope(SagaEvent e)
			{
				if (!ids.Contains(e.PlayerId))
				{
					if (OrdinaryBoss(e) || e.NemesisBoss)
					{
						return AdventureTeam(e, players).Any(ids.Contains);
					}
					return false;
				}
				return true;
			}
			LeaderboardBossKill Kill(SagaEvent e)
			{
				(string, string, int?) tuple2 = BossIdentity(e);
				PlayerSnapshot value;
				return new LeaderboardBossKill
				{
					BossKey = tuple2.Item1,
					BossName = tuple2.Item2,
					Order = tuple2.Item3,
					Utc = e.Utc,
					Stars = e.Stars,
					DurationSeconds = ((!BossCatalog.FinalNorthPhase(e.Prefab) && e.DurationSeconds.HasValue && e.DurationSeconds > 0.0 && !double.IsNaN(e.DurationSeconds.Value) && !double.IsInfinity(e.DurationSeconds.Value)) ? e.DurationSeconds : ((double?)null)),
					Team = (from id in AdventureTeam(e, players)
						select new LeaderboardPerson
						{
							PlayerId = id,
							Name = players[id].Name
						}).ToArray(),
					Finisher = (players.TryGetValue(e.PlayerId, out value) ? new LeaderboardPerson
					{
						PlayerId = value.PlayerId,
						Name = value.Name
					} : null)
				};
			}
		}

		public object ChapterMap(string world, string chapterId, HashSet<string>? selected = null)
		{
			Dictionary<string, PlayerSnapshot> players = (from p in store.Players(world)
				where p.ShareProfile
				select p).ToDictionary<PlayerSnapshot, string>((PlayerSnapshot p) => p.PlayerId, StringComparer.Ordinal);
			HashSet<string> consent = new HashSet<string>(players.Keys, StringComparer.Ordinal);
			SagaChapter sagaChapter = store.Chapters(world).Concat(store.ServerChapters(world)).FirstOrDefault((SagaChapter c) => c.Id == chapterId && GeneratedChapter(c) && ChapterContextVisible(c) && ((!(c.Scope == "server")) ? consent.Contains(c.PlayerId) : SharedChapterVisible(c, consent)));
			HashSet<string> owners = new HashSet<string>(from p in players.Values
				where p.ShareMap && (selected == null || selected.Contains(p.PlayerId))
				select p.PlayerId, StringComparer.Ordinal);
			if (sagaChapter == null)
			{
				return new
				{
					world = world,
					chapterId = "",
					moments = new AdventureMoment[0],
					truncated = false
				};
			}
			HashSet<string> eventIds = new HashSet<string>(sagaChapter.EventIds, StringComparer.Ordinal);
			SagaEvent[] array = (from e in store.AnalyticsHistory(world)
				where eventIds.Contains(e.Id) && players.ContainsKey(e.PlayerId) && owners.Contains(e.PlayerId) && e.X.HasValue && e.Z.HasValue
				orderby e.Utc descending
				select e).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal).Take(500).ToArray();
			AdventureMoment[] array2 = (from e in array
				select AdventureProjection(world, e, players, owners) into e
				where e.X.HasValue && e.Z.HasValue
				select e).Take(101).ToArray();
			return new
			{
				world = world,
				chapterId = (sagaChapter?.Id ?? ""),
				moments = array2.Take(100).ToArray(),
				truncated = (array2.Length > 100 || array.Length == 500)
			};
		}

		private void SanitizePlayers(List<PlayerSnapshot> players, SlsWorldState sls)
		{
			foreach (PlayerSnapshot player in players)
			{
				player.Online = player.Online && (DateTime.UtcNow - (player.LastSeenUtc ?? player.Utc)).TotalSeconds <= (double)options.PresenceTimeoutSeconds;
				if (!player.SharePosition || !player.X.HasValue || !player.Z.HasValue)
				{
					player.X = null;
					player.Z = null;
					player.PositionUtc = null;
					player.PositionLive = false;
				}
				else
				{
					player.PositionUtc = player.PositionUtc ?? player.Utc;
					player.PositionLive = player.Online && (DateTime.UtcNow - player.PositionUtc.Value).TotalSeconds <= (double)options.PresenceTimeoutSeconds;
				}
				if (!player.ShareProfile)
				{
					player.CompletedBossKeys.Clear();
					player.ProgressionTier = 0;
					player.ProgressionBoss = "";
					player.LastAchievementUtc = null;
					player.ProfileBiome = "";
					player.ProfileBiomeEvidence = "";
					player.BackgroundUnlocked = false;
					player.BackgroundPreference = "automatic";
					player.Gear.Clear();
					player.Hotbar.Clear();
					player.PortraitStatus = "";
					player.EffectiveStats.Clear();
					player.EffectiveResistances.Clear();
					player.PortraitId = "";
					player.Gold = null;
					player.EpicLootInstalled = false;
					player.JewelcraftingInstalled = false;
				}
			}
			foreach (PlayerSnapshot player2 in players)
			{
				if (!player2.ShareProfile || !sls.Installed || !sls.NemesisEnabled)
				{
					player2.NemesisScore = null;
				}
			}
		}

		private static DateTime? AdventureSince(string? value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return null;
			}
			if (!DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result))
			{
				throw new ArgumentException("Invalid recap date.");
			}
			return result;
		}

		public object WorldState(string world)
		{
			SlsWorldState slsWorldState = ActiveSls(world);
			List<PlayerSnapshot> players = store.Players(world);
			SanitizePlayers(players, slsWorldState);
			return new
			{
				world = world,
				worlds = store.Worlds(),
				worldNames = store.WorldNames(),
				server = new
				{
					name = options.ServerName,
					address = options.ServerAddress
				},
				players = players,
				sls = SlsSummary(slsWorldState),
				synthetic = options.Synthetic,
				serverUtc = DateTime.UtcNow
			};
		}

		public object VersionInfo()
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Invalid comparison between Unknown and I4
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Invalid comparison between Unknown and I4
			lock (versionGate)
			{
				if (versionInfo != null && (DateTime.UtcNow - versionRead).TotalSeconds < 30.0)
				{
					return versionInfo;
				}
				string text = "";
				string text2 = "";
				string text3 = "";
				try
				{
					string text4 = Path.Combine(options.WebDirectory, "version.json");
					if (File.Exists(text4) && new FileInfo(text4).Length <= 4096)
					{
						JObject obj = JObject.Parse(File.ReadAllText(text4));
						JToken val = obj["version"];
						JToken val2 = obj["appSha256"];
						if (val != null && (int)val.Type == 8 && VersionPolicy.Valid((string)val))
						{
							text = (string)val;
						}
						if (val2 != null && (int)val2.Type == 8)
						{
							string text5 = (string)val2;
							if (text5.Length == 64 && text5.All((char c) => (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
							{
								text3 = text5;
							}
						}
					}
				}
				catch (IOException)
				{
				}
				catch (UnauthorizedAccessException)
				{
				}
				catch (JsonException)
				{
				}
				try
				{
					string path = Path.Combine(options.WebDirectory, "app.js");
					if (File.Exists(path))
					{
						using SHA256 sHA = SHA256.Create();
						using FileStream inputStream = File.OpenRead(path);
						text2 = BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "").ToLowerInvariant();
					}
				}
				catch (IOException)
				{
				}
				catch (UnauthorizedAccessException)
				{
				}
				bool mismatch = string.IsNullOrWhiteSpace(text) || (!string.IsNullOrWhiteSpace(options.ServerVersion) && text != options.ServerVersion) || text3 == "" || !string.Equals(text2, text3, StringComparison.OrdinalIgnoreCase);
				versionRead = DateTime.UtcNow;
				return versionInfo = new
				{
					serverVersion = options.ServerVersion,
					websiteVersion = text,
					websiteHash = text2,
					expectedWebsiteHash = text3,
					mismatch = mismatch
				};
			}
		}

		private void WarnWebsiteMismatch()
		{
			if (string.IsNullOrWhiteSpace(options.ServerVersion))
			{
				return;
			}
			JToken obj = JObject.FromObject(VersionInfo())["mismatch"];
			if (obj == null || !Extensions.Value<bool>((IEnumerable<JToken>)obj))
			{
				return;
			}
			try
			{
				options.Log?.Invoke("WARNING: Sagas website files do not match server version " + options.ServerVersion + ". Stop the server, replace the complete matching Sagas package including its web folder (preserve configuration and recorded data), restart, and reload website tabs. Missing, invalid, or mixed website files can leave an older map renderer running.");
			}
			catch
			{
			}
		}

		private IEnumerable<SagaChapter> VisibleJourneys(string world, Dictionary<string, PlayerSnapshot> players, HashSet<string>? selected)
		{
			HashSet<string> consent = new HashSet<string>(players.Keys, StringComparer.Ordinal);
			return store.Chapters(world).Concat(store.ServerChapters(world)).Where(delegate(SagaChapter c)
			{
				if (GeneratedChapter(c) && ChapterContextVisible(c))
				{
					if (!(c.Scope == "server"))
					{
						if (consent.Contains(c.PlayerId))
						{
							if (selected != null)
							{
								return selected.Contains(c.PlayerId);
							}
							return true;
						}
						return false;
					}
					if (SharedChapterVisible(c, consent))
					{
						if (selected != null)
						{
							return c.Participants.Any((SagaParticipant p) => selected.Contains(p.PlayerId));
						}
						return true;
					}
					return false;
				}
				return false;
			});
		}

		private object JourneyChapter(SagaChapter c, bool full)
		{
			return new
			{
				id = c.Id,
				playerId = c.PlayerId,
				scope = c.Scope,
				title = c.Title,
				utc = c.Utc,
				fromUtc = c.FromUtc,
				toUtc = c.ToUtc,
				text = (full ? c.Text : ""),
				excerpt = ((c.Text.Length > 360) ? (c.Text.Substring(0, 360) + "…") : c.Text),
				participants = c.Participants,
				model = (full ? c.Model : ""),
				facts = (full ? c.Facts : new List<string>())
			};
		}

		public object SagaJourneys(string world, HashSet<string>? selected = null, string before = "")
		{
			Dictionary<string, PlayerSnapshot> players = (from p in store.Players(world)
				where p.ShareProfile
				select p).ToDictionary<PlayerSnapshot, string>((PlayerSnapshot p) => p.PlayerId, StringComparer.Ordinal);
			SagaChapter[] array = (from c in VisibleJourneys(world, players, selected)
				orderby c.Utc descending
				select c).ThenBy<SagaChapter, string>((SagaChapter c) => c.Id, StringComparer.Ordinal).ToArray();
			int num = ((!string.IsNullOrEmpty(before)) ? (Array.FindIndex(array, (SagaChapter c) => c.Id == before) + 1) : 0);
			if (!string.IsNullOrEmpty(before) && num == 0)
			{
				return new
				{
					world = world,
					chapters = new object[0],
					next = ""
				};
			}
			SagaChapter[] array2 = array.Skip(num).Take(24).ToArray();
			HashSet<string> owners = new HashSet<string>(from p in players.Values
				where p.ShareMap && (selected == null || selected.Contains(p.PlayerId))
				select p.PlayerId, StringComparer.Ordinal);
			Dictionary<string, SagaEvent> history = store.AnalyticsHistory(world).ToDictionary<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal);
			var chapters = array2.Select(delegate(SagaChapter c)
			{
				AdventureMoment anchor = null;
				foreach (string item in c.EventIds.Take(500))
				{
					if (history.TryGetValue(item, out var value) && owners.Contains(value.PlayerId))
					{
						AdventureMoment adventureMoment = AdventureProjection(world, value, players, owners);
						if (adventureMoment.X.HasValue && adventureMoment.Z.HasValue)
						{
							anchor = adventureMoment;
							break;
						}
					}
				}
				return new
				{
					chapter = JourneyChapter(c, full: false),
					anchor = anchor
				};
			}).ToArray();
			return new
			{
				world = world,
				chapters = chapters,
				next = ((array.Length > num + array2.Length && array2.Length != 0) ? array2[^1].Id : "")
			};
		}

		private static AdventureMoment[] JourneyHighlights(List<AdventureMoment> source)
		{
			if (source.Count <= 6)
			{
				return source.ToArray();
			}
			List<int> chosen = new List<int>();
			while (chosen.Count < 6)
			{
				int item = (from i in Enumerable.Range(0, source.Count)
					where !chosen.Contains(i)
					select i).OrderByDescending(delegate(int i)
				{
					AdventureMoment m = source[i];
					double num2 = Importance(m);
					if (chosen.Any((int j) => source[j].Kind == m.Kind && source[j].Prefab == m.Prefab && source[j].Rarity == m.Rarity))
					{
						num2 -= 45.0;
					}
					if (!chosen.Any((int j) => source[j].Biome == m.Biome))
					{
						num2 += 18.0;
					}
					if (chosen.Count > 0)
					{
						num2 += Math.Min(20.0, chosen.Min((int j) => Math.Sqrt(Math.Pow(m.X.Value - source[j].X.Value, 2.0) + Math.Pow(m.Z.Value - source[j].Z.Value, 2.0))) / 15.0);
					}
					if (i == 0 || i == source.Count - 1)
					{
						num2 += 8.0;
					}
					return num2;
				}).ThenBy((int i) => i).First();
				chosen.Add(item);
			}
			chosen.Sort();
			int num = -1;
			List<AdventureMoment> list = new List<AdventureMoment>();
			foreach (int item2 in chosen)
			{
				AdventureMoment adventureMoment = source[item2];
				adventureMoment.BreakBefore = num < 0 || source.Skip(num + 1).Take(item2 - num).Any((AdventureMoment p) => p.BreakBefore) || adventureMoment.PlayerId != source[num].PlayerId || adventureMoment.Utc - source[num].Utc > TimeSpan.FromMinutes(30.0) || Math.Pow(adventureMoment.X.Value - source[num].X.Value, 2.0) + Math.Pow(adventureMoment.Z.Value - source[num].Z.Value, 2.0) > 1440000.0;
				list.Add(adventureMoment);
				num = item2;
			}
			return list.ToArray();
			static double Importance(AdventureMoment m)
			{
				return (m.Boss || m.NemesisBoss) ? 100 : ((m.Kind == "death") ? 65 : ((m.Kind == "bounty") ? 60 : ((m.Kind == "collect" && !string.IsNullOrWhiteSpace(m.Rarity)) ? 55 : ((m.Kind == "kill") ? (25 + Math.Min(m.Stars, 10) * 3) : 5))));
			}
		}

		private static AdventureMoment[] SceneMoments(SagaChapter chapter, List<AdventureMoment> records)
		{
			List<AdventureMoment> list = new List<AdventureMoment>();
			var lookup = records.Select((AdventureMoment m2, int i) => new
			{
				m = m2,
				i = i
			}).ToDictionary(x => x.m.Id, StringComparer.Ordinal);
			int num = -1;
			foreach (SagaScene item in chapter.Scenes.Take(6))
			{
				if (item.EventIds.Count == 0 || item.EventIds.Any((string id) => !lookup.ContainsKey(id)))
				{
					num = -1;
					continue;
				}
				var array = (from id in item.EventIds
					select lookup[id] into x
					orderby x.i
					select x).ToArray();
				var anon = array.FirstOrDefault(x => x.m.Kind == "kill" && (x.m.Boss || x.m.NemesisBoss)) ?? array[0];
				AdventureMoment m = anon.m;
				m.BreakBefore = num < 0 || records.Skip(num + 1).Take(anon.i - num).Any((AdventureMoment p) => p.BreakBefore) || m.PlayerId != records[num].PlayerId || m.Utc - records[num].Utc > TimeSpan.FromMinutes(30.0) || Math.Pow(m.X.Value - records[num].X.Value, 2.0) + Math.Pow(m.Z.Value - records[num].Z.Value, 2.0) > 1440000.0;
				m.SceneTitle = item.Title;
				m.SceneText = item.Text;
				m.EvidenceIds = item.EventIds.ToList();
				list.Add(m);
				num = anon.i;
			}
			return list.ToArray();
		}

		public object SagaJourney(string world, string id, HashSet<string>? selected = null)
		{
			Dictionary<string, PlayerSnapshot> dictionary = (from p in store.Players(world)
				where p.ShareProfile
				select p).ToDictionary<PlayerSnapshot, string>((PlayerSnapshot p) => p.PlayerId, StringComparer.Ordinal);
			SagaChapter sagaChapter = VisibleJourneys(world, dictionary, selected).FirstOrDefault((SagaChapter c) => c.Id == id);
			if (sagaChapter == null)
			{
				return new
				{
					world = world,
					chapter = (object)null,
					moments = new AdventureMoment[0],
					truncated = false
				};
			}
			HashSet<string> hashSet = new HashSet<string>(from p in dictionary.Values
				where p.ShareMap && (selected == null || selected.Contains(p.PlayerId))
				select p.PlayerId, StringComparer.Ordinal);
			HashSet<string> ids = new HashSet<string>(sagaChapter.EventIds, StringComparer.Ordinal);
			SagaEvent[] array = (from e in store.AnalyticsHistory(world)
				where ids.Contains(e.Id)
				orderby e.Utc
				select e).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal).Take(501).ToArray();
			List<AdventureMoment> list = new List<AdventureMoment>();
			AdventureMoment adventureMoment = null;
			int num = -2;
			for (int num2 = 0; num2 < Math.Min(500, array.Length); num2++)
			{
				SagaEvent sagaEvent = array[num2];
				if (hashSet.Contains(sagaEvent.PlayerId))
				{
					AdventureMoment adventureMoment2 = AdventureProjection(world, sagaEvent, dictionary, hashSet);
					if (adventureMoment2.X.HasValue && adventureMoment2.Z.HasValue)
					{
						adventureMoment2.BreakBefore = adventureMoment == null || num2 != num + 1 || adventureMoment.PlayerId != adventureMoment2.PlayerId || adventureMoment2.Utc - adventureMoment.Utc > TimeSpan.FromMinutes(30.0) || Math.Pow(adventureMoment2.X.Value - adventureMoment.X.Value, 2.0) + Math.Pow(adventureMoment2.Z.Value - adventureMoment.Z.Value, 2.0) > 1440000.0;
						list.Add(adventureMoment2);
						adventureMoment = adventureMoment2;
						num = num2;
					}
				}
			}
			return new
			{
				world = world,
				chapter = JourneyChapter(sagaChapter, full: true),
				moments = ((sagaChapter.Scenes.Count > 0) ? SceneMoments(sagaChapter, list) : JourneyHighlights(list)),
				truncated = (array.Length > 500 || (sagaChapter.Scenes.Count == 0 && list.Count > 6)),
				notes = "Connections show the order of recorded moments, not travelled paths. Gaps, different Vikings, long intervals and distant locations break the trail."
			};
		}

		private static object IntegrationSummary(IEnumerable<PlayerSnapshot> source, bool nemesis)
		{
			PlayerSnapshot[] source2 = source.ToArray();
			return new
			{
				epicLoot = source2.Any((PlayerSnapshot p) => p.EpicLootInstalled),
				jewelcrafting = source2.Any((PlayerSnapshot p) => p.JewelcraftingInstalled),
				nemesis = nemesis
			};
		}

		private static string LeaderboardRarity(string rarity)
		{
			if (!string.IsNullOrWhiteSpace(rarity))
			{
				return rarity.Trim();
			}
			return "Unenchanted";
		}

		private static string BossPrefab(SagaEvent e)
		{
			if (!e.Prefab.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return e.Prefab;
			}
			return e.Prefab.Substring(0, e.Prefab.Length - 7).Trim();
		}

		private static (string Key, string Name, int? Order) BossIdentity(SagaEvent e)
		{
			string text = BossPrefab(e);
			(string, string, int, string)[] knownBosses = KnownBosses;
			for (int i = 0; i < knownBosses.Length; i++)
			{
				(string, string, int, string) tuple = knownBosses[i];
				if (string.Equals(text, tuple.Item1, StringComparison.OrdinalIgnoreCase))
				{
					return (Key: tuple.Item1, Name: tuple.Item2, Order: tuple.Item3);
				}
			}
			return (Key: string.IsNullOrWhiteSpace(text) ? ("unknown:" + e.Name) : text, Name: string.IsNullOrWhiteSpace(e.Name) ? text : e.Name, Order: null);
		}

		public object Leaderboard(string world, TimeWindow window, HashSet<string>? selected = null)
		{
			Dictionary<string, PlayerSnapshot> players = (from p in store.Players(world)
				where p.ShareProfile && (selected == null || selected.Contains(p.PlayerId))
				select p).ToDictionary<PlayerSnapshot, string>((PlayerSnapshot p) => p.PlayerId, StringComparer.Ordinal);
			SagaEvent[] source = (from e in store.AnalyticsHistory(world)
				where window.Contains(e.Utc)
				select e).ToArray();
			var bossEvents = (from x in source.Where((SagaEvent e) => e.Kind == "kill" && e.Boss && !e.NemesisBoss && !BossCatalog.IntermediatePhase(e.Prefab)).Select(delegate(SagaEvent e)
				{
					(string, string, int?) tuple = BossIdentity(e);
					LeaderboardPerson[] team = (from id in e.Contributors.Concat(new string[1] { e.PlayerId })
						where !string.IsNullOrEmpty(id) && players.ContainsKey(id)
						select id).Distinct<string>(StringComparer.Ordinal).Select(Person).OrderBy<LeaderboardPerson, string>((LeaderboardPerson p) => p.Name, StringComparer.Ordinal)
						.ThenBy<LeaderboardPerson, string>((LeaderboardPerson p) => p.PlayerId, StringComparer.Ordinal)
						.ToArray();
					return new
					{
						Event = e,
						Row = new LeaderboardBossKill
						{
							BossKey = tuple.Item1,
							BossName = tuple.Item2,
							Order = tuple.Item3,
							Utc = e.Utc,
							DurationSeconds = ((!BossCatalog.FinalNorthPhase(e.Prefab) && e.DurationSeconds.HasValue && e.DurationSeconds.Value > 0.0 && !double.IsNaN(e.DurationSeconds.Value) && !double.IsInfinity(e.DurationSeconds.Value)) ? e.DurationSeconds : ((double?)null)),
							Stars = e.Stars,
							Team = team,
							Finisher = (players.ContainsKey(e.PlayerId) ? Person(e.PlayerId) : null)
						}
					};
				})
				where x.Row.Team.Length != 0
				orderby x.Event.Utc
				select x).ThenBy(x => x.Event.Id, StringComparer.Ordinal).ToArray();
			(string Key, string Name, int? Order)[] source2 = KnownBosses.Select(((string Key, string Name, int Order, string Biome) b) => ((string Key, string Name, int? Order))(Key: b.Key, Name: b.Name, Order: b.Order)).Concat(from g in (from x in bossEvents
					where !x.Row.Order.HasValue
					select ((string Key, string Name, int? Order))(Key: x.Row.BossKey, Name: x.Row.BossName, Order: null)).GroupBy<(string, string, int?), string>(((string Key, string Name, int? Order) x) => x.Key, StringComparer.OrdinalIgnoreCase)
				select g.First()).ToArray();
			LeaderboardBossKill[] fastest = (from x in (from x in bossEvents
					where x.Row.DurationSeconds.HasValue
					orderby x.Row.DurationSeconds, x.Row.Utc
					select x).ThenBy(x => x.Row.BossKey, StringComparer.Ordinal).ThenBy(x => x.Event.Id, StringComparer.Ordinal)
				select x.Row).ToArray();
			var bosses = (from b in source2.Select(delegate((string Key, string Name, int? Order) b)
				{
					var array4 = bossEvents.Where(x => string.Equals(x.Row.BossKey, b.Key, StringComparison.OrdinalIgnoreCase)).ToArray();
					LeaderboardPerson[] array5 = (from g in array4.SelectMany(x => x.Row.Team).GroupBy<LeaderboardPerson, string>((LeaderboardPerson p) => p.PlayerId, StringComparer.Ordinal)
						select g.First()).OrderBy<LeaderboardPerson, string>((LeaderboardPerson p) => p.Name, StringComparer.Ordinal).ThenBy<LeaderboardPerson, string>((LeaderboardPerson p) => p.PlayerId, StringComparer.Ordinal).ToArray();
					return new
					{
						key = b.Key,
						name = b.Name,
						order = b.Order,
						kills = array4.Length,
						uniquePlayers = array5.Length,
						players = array5,
						fastest = fastest.FirstOrDefault((LeaderboardBossKill x) => string.Equals(x.BossKey, b.Key, StringComparison.OrdinalIgnoreCase))
					};
				})
				orderby b.order ?? int.MaxValue
				select b).ThenBy(b => b.key, StringComparer.Ordinal).ToArray();
			LeaderboardProgress[] array = (from p in players.Values.Select(delegate(PlayerSnapshot p)
				{
					LeaderboardBossProgress[] array4 = (from b in bossEvents.Where(x => x.Row.Team.Any((LeaderboardPerson person) => person.PlayerId == p.PlayerId)).GroupBy(x => x.Row.BossKey, StringComparer.OrdinalIgnoreCase).Select(g =>
						{
							LeaderboardBossKill row = g.First().Row;
							return new LeaderboardBossProgress
							{
								Key = row.BossKey,
								Name = row.BossName,
								Order = row.Order,
								FirstUtc = row.Utc,
								LastUtc = g.Last().Row.Utc,
								Kills = g.Count(),
								BestDurationSeconds = (from x in g
									where x.Row.DurationSeconds.HasValue
									select x.Row.DurationSeconds).DefaultIfEmpty(null).Min(),
								FirstTeam = row.Team
							};
						})
						orderby b.Order ?? int.MaxValue
						select b).ThenBy<LeaderboardBossProgress, string>((LeaderboardBossProgress b) => b.Key, StringComparer.Ordinal).ToArray();
					LeaderboardBossProgress leaderboardBossProgress = (from b in array4
						where b.Order.HasValue
						orderby b.Order descending
						select b).FirstOrDefault();
					return new LeaderboardProgress
					{
						PlayerId = p.PlayerId,
						Name = p.Name,
						HighestOrder = (leaderboardBossProgress?.Order).GetValueOrDefault(),
						HighestBoss = leaderboardBossProgress?.Name,
						BossCount = array4.Count((LeaderboardBossProgress b) => b.Order.HasValue),
						Bosses = array4
					};
				})
				where p.Bosses.Length != 0
				orderby p.HighestOrder descending, p.BossCount descending
				select p).ThenBy<LeaderboardProgress, string>((LeaderboardProgress p) => p.Name, StringComparer.Ordinal).ThenBy<LeaderboardProgress, string>((LeaderboardProgress p) => p.PlayerId, StringComparer.Ordinal).ToArray();
			for (int num = 0; num < array.Length; num++)
			{
				array[num].Rank = ((num > 0 && array[num].HighestOrder == array[num - 1].HighestOrder && array[num].BossCount == array[num - 1].BossCount) ? array[num - 1].Rank : (num + 1));
			}
			SagaEvent[] source3 = (from g in source.Where((SagaEvent e) => e.Kind == "kill" && e.NemesisBoss).GroupBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal)
				select g.First()).ToArray();
			LeaderboardRank[] nemesisBossKills = Rank(from g in source3.SelectMany((SagaEvent e) => e.Contributors.Concat(new string[1] { e.PlayerId }).Where(players.ContainsKey).Distinct<string>(StringComparer.Ordinal)).GroupBy<string, string>((string id) => id, StringComparer.Ordinal)
				select new KeyValuePair<string, long>(g.Key, g.LongCount()));
			SagaEvent[] source4 = source.Where((SagaEvent e) => e.Kind == "kill" && players.ContainsKey(e.PlayerId)).ToArray();
			LeaderboardRank[] kills = Rank(from e in source4
				group e by e.PlayerId into g
				select new KeyValuePair<string, long>(g.Key, g.LongCount()));
			Dictionary<string, SagaEvent> starEvidence = (from e in source4
				group e by e.PlayerId).ToDictionary<IGrouping<string, SagaEvent>, string, SagaEvent>((IGrouping<string, SagaEvent> g) => g.Key, (IGrouping<string, SagaEvent> g) => (from e in g
				orderby e.Stars descending, e.Utc
				select e).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal).First(), StringComparer.Ordinal);
			LeaderboardStarRank[] highestStars = (from row in Rank(starEvidence.Select((KeyValuePair<string, SagaEvent> pair) => new KeyValuePair<string, long>(pair.Key, pair.Value.Stars)))
				select new LeaderboardStarRank
				{
					Rank = row.Rank,
					PlayerId = row.PlayerId,
					Name = row.Name,
					Value = row.Value,
					CreatureName = starEvidence[row.PlayerId].Name,
					Prefab = starEvidence[row.PlayerId].Prefab,
					Utc = starEvidence[row.PlayerId].Utc
				}).ToArray();
			var collectionsByRarity = (from g in source.Where((SagaEvent e) => e.Kind == "collect" && players.ContainsKey(e.PlayerId)).GroupBy<SagaEvent, string>((SagaEvent e) => LeaderboardRarity(e.Rarity), StringComparer.OrdinalIgnoreCase)
				select new
				{
					rarity = g.Key,
					rarityColor = ((from e in g.OrderByDescending((SagaEvent e) => e.Utc).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal)
						select e.RarityColor).FirstOrDefault((string c) => !string.IsNullOrEmpty(c)) ?? ""),
					rows = Rank(from e in g
						group e by e.PlayerId into p
						select new KeyValuePair<string, long>(p.Key, ((IEnumerable<SagaEvent>)p).Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount))))
				}).OrderBy(g => g.rarity, StringComparer.OrdinalIgnoreCase).ToArray();
			LeaderboardSnapshotRank[] array2 = Snapshots((PlayerSnapshot p) => p.Gold);
			SagaEvent[] array3 = source.Where((SagaEvent e) => e.Kind == "bounty" && players.ContainsKey(e.PlayerId)).ToArray();
			LeaderboardRank[] bounties = Rank(from e in array3
				group e by e.PlayerId into g
				select new KeyValuePair<string, long>(g.Key, g.LongCount()));
			return new
			{
				server = new
				{
					name = options.ServerName,
					address = options.ServerAddress
				},
				world = world,
				from = window.From,
				to = window.To,
				trackingSince = store.TrackingSince,
				statisticsSince = store.StatisticsSince,
				partialHistory = (window.From < store.TrackingSince || (store.StatisticsSince.HasValue && window.From < store.StatisticsSince.Value)),
				scope = "selected-window",
				snapshotScope = "latest-character-snapshot",
				synthetic = options.Synthetic,
				equippedGems = Snapshots((PlayerSnapshot p) => (!p.JewelcraftingInstalled) ? ((long?)null) : new long?(p.Gear.Sum((GearItem g) => g.Sockets.Count((GemSocket s) => s.Prefab != "")))),
				equippedSockets = Snapshots((PlayerSnapshot p) => (!p.JewelcraftingInstalled) ? ((long?)null) : new long?(p.Gear.Sum((GearItem g) => g.Sockets.Count))),
				bosses = bosses,
				progression = array,
				fastestBossKills = fastest,
				kills = kills,
				highestStars = highestStars,
				collectionsByRarity = collectionsByRarity,
				gold = array2,
				bounties = bounties,
				nemesisBossKills = nemesisBossKills,
				availability = new
				{
					epicLootAvailable = players.Values.Any((PlayerSnapshot p) => p.EpicLootInstalled),
					jewelcraftingAvailable = players.Values.Any((PlayerSnapshot p) => p.JewelcraftingInstalled),
					nemesisTrackingAvailable = (ActiveSls(world).Installed && ActiveSls(world).NemesisEnabled),
					timedBossKills = fastest.Length,
					untimedBossKills = bossEvents.Length - fastest.Length,
					goldSnapshots = array2.Length,
					bountyEvents = array3.Length,
					bountyTrackingAvailable = (array3.Length != 0 || players.Values.Any((PlayerSnapshot p) => p.EpicLootInstalled)),
					epicLootPlayers = players.Values.Count((PlayerSnapshot p) => p.EpicLootInstalled)
				},
				notes = new string[8] { "Boss credit is the distinct recorded contributors plus the finisher. Only currently shared profiles are listed; a displayed team may therefore be incomplete.", "Progression ranks the highest known vanilla boss tier, then the number of distinct known bosses. It does not imply every earlier tier was completed; modded bosses have no invented progression tier.", "Kill and highest-star rankings use finishing-blow credit. Collection rankings exclude drops and unknown pickups. All event panels follow the selected window.", "Duration runs from the first observed damaging hit on a full-health boss to death. Partial fights and untimed legacy reports are omitted from speed rankings. Kall Fimbulbringer final-phase victories count once; earlier phases are excluded. Full multi-phase duration is not yet tracked, so Kall is omitted from speed rankings. His credited team includes recorded final-phase contributors only.", "Gold and equipped socket/gem counts use the last shared loadout, independent of the selected period. Socket capacity is not a combat-power score.", "Epic Loot bounties count recorded completion transitions in the selected period; historical completions are not imported.", "Nemesis boss rankings count distinct recorded defeats for each consenting contributor and finisher; these never unlock vanilla progression or enter ordinary boss speed rankings.", "Equal scores share competition rank; display order breaks ties by name and character ID. Only retained history is available." }.Where((string note) => (!note.StartsWith("Epic Loot", StringComparison.Ordinal) || players.Values.Any((PlayerSnapshot p) => p.EpicLootInstalled)) && (!note.StartsWith("Nemesis", StringComparison.Ordinal) || (ActiveSls(world).Installed && ActiveSls(world).NemesisEnabled))).ToArray()
			};
			LeaderboardPerson Person(string id)
			{
				return new LeaderboardPerson
				{
					PlayerId = id,
					Name = players[id].Name
				};
			}
			LeaderboardRank[] Rank(IEnumerable<KeyValuePair<string, long>> values)
			{
				LeaderboardRank[] array4 = (from pair in values
					where players.ContainsKey(pair.Key)
					select new LeaderboardRank
					{
						PlayerId = pair.Key,
						Name = players[pair.Key].Name,
						Value = pair.Value
					} into row
					orderby row.Value descending
					select row).ThenBy<LeaderboardRank, string>((LeaderboardRank row) => row.Name, StringComparer.Ordinal).ThenBy<LeaderboardRank, string>((LeaderboardRank row) => row.PlayerId, StringComparer.Ordinal).ToArray();
				for (int num2 = 0; num2 < array4.Length; num2++)
				{
					array4[num2].Rank = ((num2 > 0 && array4[num2].Value == array4[num2 - 1].Value) ? array4[num2 - 1].Rank : (num2 + 1));
				}
				return array4;
			}
			LeaderboardSnapshotRank[] Snapshots(Func<PlayerSnapshot, long?> metric)
			{
				return (from p in Rank(from p in players.Values
						where metric(p).HasValue
						select new KeyValuePair<string, long>(p.PlayerId, metric(p).Value))
					select new LeaderboardSnapshotRank
					{
						Rank = p.Rank,
						PlayerId = p.PlayerId,
						Name = p.Name,
						Value = p.Value,
						Utc = players[p.PlayerId].Utc
					}).ToArray();
			}
		}

		private HashSet<string> MapOwners(string world, HashSet<string>? selected)
		{
			return new HashSet<string>(from p in store.Players(world)
				where p.ShareMap && (selected == null || selected.Contains(p.PlayerId))
				select p.PlayerId);
		}

		private string MapScope(string world, HashSet<string> owners)
		{
			using SHA256 sHA = SHA256.Create();
			return mapInstance + Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(world + "\n" + string.Join("\n", owners.OrderBy<string, string>((string x) => x, StringComparer.Ordinal)))));
		}

		public object MapStream(string world, HashSet<string>? selected = null, string? scope = null, string? cursor = null)
		{
			HashSet<string> owners = MapOwners(world, selected);
			string text = MapScope(world, owners);
			bool flag = text != scope;
			long result = 0L;
			if (!flag && (!long.TryParse(cursor, NumberStyles.None, CultureInfo.InvariantCulture, out result) || result < 0 || result > store.MapRevision))
			{
				flag = true;
				result = 0L;
			}
			bool flag2 = store.MigrateMapPage();
			(List<MapOverviewCell>, long, bool) tuple = store.MapPage(world, owners, result);
			return new
			{
				scope = text,
				cursor = tuple.Item2.ToString(CultureInfo.InvariantCulture),
				reset = flag,
				more = (flag2 || tuple.Item3),
				cells = tuple.Item1,
				cellSize = 64
			};
		}

		public object MapDetail(string world, HashSet<string>? selected, string keys, int side = 64)
		{
			List<(int, int)> list = new List<(int, int)>();
			if (keys.Length > 2048)
			{
				throw new ArgumentException("Too many map detail keys.");
			}
			string[] array = keys.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(',');
				if (array2.Length != 2 || !int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
				{
					throw new ArgumentException("Invalid map tile coordinate.");
				}
				list.Add((result, result2));
			}
			HashSet<string> owners = MapOwners(world, selected);
			return new
			{
				scope = MapScope(world, owners),
				cells = store.MapDetail(world, owners, list, side),
				cellSize = 64
			};
		}

		public object MapMeta(string world, HashSet<string>? selected = null)
		{
			HashSet<string> owners = MapOwners(world, selected);
			SlsWorldState sls = ActiveSls(world);
			string text = MapScope(world, owners);
			string text2 = text + ":" + store.MapRevision + ":" + sls.Utc.Ticks;
			Dictionary<(int X, int Z), byte[]> masks;
			lock (mapMetaGate)
			{
				if (mapMetaKey == text2 && mapMetaCache != null)
				{
					return mapMetaCache;
				}
				(List<MapOverviewCell>, bool) tuple = ((sls.Installed && sls.ZoneScalingEnabled && sls.OverlayEnabled && !sls.AboveFog) ? store.MapMasks(world, owners) : (new List<MapOverviewCell>(), store.MapImported(world, owners)));
				masks = tuple.Item1.ToDictionary((MapOverviewCell c) => (X: c.X, Z: c.Z), (MapOverviewCell c) => Convert.FromBase64String(c.ExplorationMask));
				List<SlsZone> zones = ((sls.Installed && sls.ZoneScalingEnabled && sls.OverlayEnabled) ? sls.Zones.Where((SlsZone z) => sls.AboveFog || Visible(z)).ToList() : new List<SlsZone>());
				mapMetaCache = new
				{
					scope = text,
					sls = new { sls.Installed, sls.NemesisEnabled, sls.ZoneScalingEnabled, sls.OverlayEnabled, sls.AboveFog, sls.Opacity, sls.OutlineInset, sls.Utc, zones },
					imported = tuple.Item2,
					label = "Voluntarily shared known map; imported discoveries predate tracking. Runtime-rendered Valheim map with personal fog."
				};
				mapMetaKey = text2;
				return mapMetaCache;
			}
			bool Visible(SlsZone zone)
			{
				for (int i = (int)Math.Floor(zone.MinZ / 64f); (double)i < Math.Ceiling(zone.MaxZ / 64f); i++)
				{
					for (int j = (int)Math.Floor(zone.MinX / 64f); (double)j < Math.Ceiling(zone.MaxX / 64f); j++)
					{
						if (masks.TryGetValue((j, i), out var value))
						{
							for (int k = Math.Max(0, (int)Math.Floor(zone.MinZ - (float)(i * 64))); k < Math.Min(64, (int)Math.Ceiling(zone.MaxZ - (float)(i * 64))); k++)
							{
								for (int l = Math.Max(0, (int)Math.Floor(zone.MinX - (float)(j * 64))); l < Math.Min(64, (int)Math.Ceiling(zone.MaxX - (float)(j * 64))); l++)
								{
									if (MapOverviewCell.Known(value, l, k))
									{
										return true;
									}
								}
							}
						}
					}
				}
				return false;
			}
		}

		public void UpdateClock(WorldClock clock)
		{
			if (!(clock.World != options.World) && clock.Day >= 0 && Finite(clock.Fraction) && !(clock.Fraction < 0f) && !(clock.Fraction > 1f) && (!clock.SecondsToTransition.HasValue || (!double.IsNaN(clock.SecondsToTransition.Value) && !double.IsInfinity(clock.SecondsToTransition.Value) && !(clock.SecondsToTransition < 0.0))) && (!(clock.NextPhase != "") || !(clock.NextPhase != "day") || !(clock.NextPhase != "night")))
			{
				Volatile.Write(ref worldClock, new WorldClock
				{
					World = clock.World,
					Day = clock.Day,
					Fraction = clock.Fraction,
					SecondsToTransition = clock.SecondsToTransition,
					NextPhase = clock.NextPhase,
					Running = clock.Running,
					Skipping = clock.Skipping
				});
			}
		}

		public WorldClock? Clock(string world)
		{
			WorldClock worldClock = Volatile.Read(in this.worldClock);
			if (!(worldClock?.World == world))
			{
				return null;
			}
			return worldClock;
		}

		public bool UpdatePins(MapPins batch, Action<bool>? committed = null)
		{
			if (batch == null || !TextValid(batch.World, 100, required: true) || !TextValid(batch.PlayerId, 100, required: true) || !TextValid(batch.Revision, 64, required: true) || batch.Count < 1 || batch.Count > 64 || batch.Index < 0 || batch.Index >= batch.Count || batch.Pins == null || batch.Pins.Count > 32 || batch.Pins.Any((MapPin p) => p == null || !TextValid(p.Name, 100) || !TextValid(p.Type, 60) || !MediaId(p.IconId) || !Coordinate(p.X) || !Coordinate(p.Z)))
			{
				return false;
			}
			MapPins copy = Copy(batch);
			return Enqueue(delegate
			{
				if (store.PinRevision(copy.World, copy.PlayerId) == copy.Revision)
				{
					committed?.Invoke(obj: true);
				}
				else
				{
					string[] array = (from p in pinAssemblies
						where (DateTime.UtcNow - p.Value.Started).TotalMinutes > 10.0
						select p.Key).ToArray();
					foreach (string key in array)
					{
						pinAssemblies.Remove(key);
					}
					string key2 = copy.World + ":" + copy.PlayerId;
					if (!pinAssemblies.TryGetValue(key2, out PinAssembly value) || value.Revision != copy.Revision)
					{
						if (value == null && pinAssemblies.Count >= 64)
						{
							return;
						}
						value = new PinAssembly
						{
							Revision = copy.Revision,
							Parts = new MapPins[copy.Count],
							Callbacks = new Action<bool>[copy.Count]
						};
						pinAssemblies[key2] = value;
					}
					if (value.Parts.Length == copy.Count)
					{
						value.Parts[copy.Index] = copy;
						value.Callbacks[copy.Index] = committed;
						if (!value.Parts.Any((MapPins p) => p == null))
						{
							store.SavePins(new MapPins
							{
								World = copy.World,
								PlayerId = copy.PlayerId,
								Revision = copy.Revision,
								Pins = value.Parts.SelectMany((MapPins p) => p.Pins).ToList()
							});
							pinAssemblies.Remove(key2);
							Action<bool>[] callbacks = value.Callbacks;
							for (int num = 0; num < callbacks.Length; num++)
							{
								callbacks[num]?.Invoke(obj: true);
							}
						}
					}
				}
			});
		}

		public object MapIcons(string world, HashSet<string>? selected = null)
		{
			return new
			{
				pins = VisiblePins(world, selected)
			};
		}

		internal VisibleMapPin[] VisiblePins(string world, HashSet<string>? selected = null)
		{
			Dictionary<string, PlayerSnapshot> dictionary = (from p in store.Players(world)
				where p.ShareMap && (selected == null || selected.Contains(p.PlayerId))
				select p).ToDictionary((PlayerSnapshot p) => p.PlayerId);
			List<VisibleMapPin> list = new List<VisibleMapPin>();
			Dictionary<string, VisibleMapPin> dictionary2 = new Dictionary<string, VisibleMapPin>();
			foreach (MapPins item2 in store.Pins(world))
			{
				if (!dictionary.TryGetValue(item2.PlayerId, out var owner))
				{
					continue;
				}
				HashSet<string> owners = new HashSet<string> { owner.PlayerId };
				foreach (MapPin pin in item2.Pins)
				{
					if ((pin.Personal && !owner.SharePins) || (!pin.Personal && !store.KnownPoint(world, owners, pin.X, pin.Z)))
					{
						continue;
					}
					string key = pin.Type + ":" + pin.X.ToString("R", CultureInfo.InvariantCulture) + ":" + pin.Z.ToString("R", CultureInfo.InvariantCulture);
					MapPinSource item = new MapPinSource
					{
						PlayerId = owner.PlayerId,
						Name = owner.Name,
						Color = owner.MapColor,
						IconId = pin.IconId
					};
					if (!pin.Personal && dictionary2.TryGetValue(key, out var value))
					{
						if (!value.Sources.Any((MapPinSource p) => p.PlayerId == owner.PlayerId))
						{
							value.Sources.Add(item);
						}
						continue;
					}
					VisibleMapPin visibleMapPin = new VisibleMapPin
					{
						Name = pin.Name,
						Type = pin.Type,
						IconId = pin.IconId,
						X = pin.X,
						Z = pin.Z,
						Personal = pin.Personal,
						Checked = pin.Checked,
						PlayerId = owner.PlayerId,
						Owner = owner.Name,
						Sources = new List<MapPinSource> { item }
					};
					list.Add(visibleMapPin);
					if (!pin.Personal)
					{
						dictionary2[key] = visibleMapPin;
					}
				}
			}
			return list.ToArray();
		}

		internal bool VisiblePinMedia(string world, string player, string id)
		{
			if (!store.IsMapIcon(world, player, id))
			{
				return false;
			}
			PlayerSnapshot owner = store.Players(world).FirstOrDefault((PlayerSnapshot p) => p.PlayerId == player && p.ShareMap);
			if (owner == null)
			{
				return false;
			}
			MapPins mapPins = store.Pins(world).FirstOrDefault((MapPins p) => p.PlayerId == player);
			if (mapPins == null)
			{
				return false;
			}
			HashSet<string> mapOwner = new HashSet<string> { player };
			return mapPins.Pins.Any((MapPin p) => p.IconId == id && (!p.Personal || owner.SharePins) && (p.Personal || store.KnownPoint(world, mapOwner, p.X, p.Z)));
		}

		private SagaOptions PlayerLoreOptions(string world, string player)
		{
			PersonalLoreSettings s = store.PersonalLore(world, player, includeKey: true);
			LorePreset lorePreset = s.Presets.FirstOrDefault((LorePreset p) => p.Name == s.Active);
			if (s.Key == "" || lorePreset == null)
			{
				return options;
			}
			return new SagaOptions
			{
				LoreEnabled = options.LoreEnabled,
				OpenRouterKey = s.Key,
				LoreModel = lorePreset.Route,
				LoreAllowPaid = lorePreset.AllowPaid,
				LoreMaxPrice = (lorePreset.AllowPaid ? lorePreset.MaxPrice : 0m),
				LoreDailyBudget = Math.Min(20, lorePreset.DailyRequests),
				Log = options.Log
			};
		}

		private async Task PersonalLoreApi(HttpListenerContext c, PlayerLoginIdentity? identity)
		{
			if (identity == null)
			{
				await Respond(c, 403, new
				{
					error = "Personal login required."
				});
				return;
			}
			if (c.Request.HttpMethod == "POST")
			{
				if (c.Request.ContentLength64 < 1 || c.Request.ContentLength64 > 12000 || !(c.Request.ContentType ?? "").StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
				{
					await Respond(c, 400, new
					{
						error = "A bounded JSON request is required."
					});
					return;
				}
				PersonalLoreSettings settings;
				try
				{
					using StreamReader reader = new StreamReader(c.Request.InputStream, Encoding.UTF8);
					Task<string> read = reader.ReadToEndAsync();
					if (await Task.WhenAny(new Task[2]
					{
						read,
						Task.Delay(5000)
					}) != read)
					{
						await Respond(c, 408, new
						{
							error = "Request timed out."
						});
						return;
					}
					settings = JsonConvert.DeserializeObject<PersonalLoreSettings>(await read, new JsonSerializerSettings
					{
						MaxDepth = 5,
						MissingMemberHandling = (MissingMemberHandling)1
					});
				}
				catch (JsonException)
				{
					await Respond(c, 400, new
					{
						error = "Invalid settings."
					});
					return;
				}
				if (settings == null || settings.KeyProfile == null || settings.PreviousName == null || settings.Key == null || settings.Key.Length > 512 || settings.Key.Any(char.IsControl) || settings.Presets == null || settings.Presets.Count > 8 || settings.Presets.Any((LorePreset p) => p == null || !TextValid(p.Name, 60, required: true) || !LoreEngine.IsRoute(p.Route) || p.DailyRequests < 1 || p.DailyRequests > 20 || p.MaxPrice < 0m || p.MaxPrice > 100m || (p.AllowPaid && p.MaxPrice <= 0m) || (!p.AllowPaid && !LoreEngine.IsFreeModel(p.Route) && !p.Route.StartsWith("@preset/", StringComparison.Ordinal))) || (settings.KeyProfile != "" && !settings.Presets.Any((LorePreset p) => p.Name == settings.KeyProfile)) || settings.Presets.Select((LorePreset p) => p.Name).Distinct<string>(StringComparer.Ordinal).Count() != settings.Presets.Count || settings.Active == null || (settings.Active != "" && !settings.Presets.Any((LorePreset p) => p.Name == settings.Active)))
				{
					await Respond(c, 400, new
					{
						error = "Use up to 8 uniquely named presets, a valid model or @preset/name, and an explicit paid ceiling when paid routing is enabled."
					});
					return;
				}
				if (settings.Key != "" && !c.Request.IsSecureConnection && (c.Request.RemoteEndPoint == null || !IPAddress.IsLoopback(c.Request.RemoteEndPoint.Address)))
				{
					await Respond(c, 400, new
					{
						error = "Use HTTPS or a loopback reverse proxy to submit an API key."
					});
					return;
				}
				store.SavePersonalLore(identity.World, identity.PlayerId, settings);
				store.QueueLore(identity.World, identity.PlayerId);
			}
			PersonalLoreSettings personalLoreSettings = store.PersonalLore(identity.World, identity.PlayerId);
			await Respond(c, 200, new
			{
				world = identity.World,
				hostEnabled = (options.LoreEnabled && !string.IsNullOrWhiteSpace(options.OpenRouterKey)),
				hasKey = (personalLoreSettings.Key != ""),
				active = personalLoreSettings.Active,
				presets = personalLoreSettings.Presets.Select((LorePreset p) => new
				{
					name = p.Name,
					route = p.Route,
					allowPaid = p.AllowPaid,
					maxPrice = p.MaxPrice,
					dailyRequests = p.DailyRequests,
					hasKey = store.PersonalProfileHasKey(identity.World, identity.PlayerId, p.Name)
				})
			});
		}

		private int CompletedBosses(string world, string player)
		{
			return store.CompletedBossCount(world, player);
		}

		private int BackgroundProgress(string world, string player)
		{
			IReadOnlyList<string> receipts = store.CompletedBossKeys(world, player);
			return (from b in KnownBosses
				where receipts.Contains(b.Key)
				select b.Order).DefaultIfEmpty(0).Max();
		}

		private string[] AllowedBackgrounds(string world, string player)
		{
			return new string[1] { "automatic" }.Concat(BackgroundOrder.Take(BackgroundProgress(world, player) + 1)).ToArray();
		}

		private object LoginSession(string world, PlayerLoginIdentity? identity)
		{
			world = identity?.World ?? world;
			PlayerSnapshot playerSnapshot = ((identity != null) ? store.Players(world).FirstOrDefault((PlayerSnapshot p) => p.PlayerId == identity.PlayerId) : null);
			int completedBosses = ((playerSnapshot != null) ? CompletedBosses(world, playerSnapshot.PlayerId) : 0);
			return new
			{
				world = identity?.World,
				playerId = playerSnapshot?.PlayerId,
				name = playerSnapshot?.Name,
				backgroundUnlocked = (playerSnapshot?.ShareProfile ?? false),
				backgroundPreference = ((playerSnapshot == null) ? "automatic" : store.BackgroundPreference(world, playerSnapshot.PlayerId)),
				completedBosses = completedBosses,
				progressionTier = ((playerSnapshot != null) ? BackgroundProgress(world, playerSnapshot.PlayerId) : 0),
				allowedBackgrounds = ((playerSnapshot == null || !playerSnapshot.ShareProfile) ? Array.Empty<string>() : AllowedBackgrounds(world, playerSnapshot.PlayerId)),
				requiredBosses = BossCatalog.Bosses.Length
			};
		}

		private async Task SaveBackground(HttpListenerContext c, PlayerLoginIdentity? identity)
		{
			if (identity == null)
			{
				await Respond(c, 403, new
				{
					error = "Log in with your personal Viking token to customize your own background."
				});
				return;
			}
			if (c.Request.ContentLength64 < 1 || c.Request.ContentLength64 > 1024 || !(c.Request.ContentType ?? "").StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
			{
				await Respond(c, 400, new
				{
					error = "A bounded JSON request is required."
				});
				return;
			}
			BackgroundRequest request;
			try
			{
				using StreamReader reader = new StreamReader(c.Request.InputStream, Encoding.UTF8);
				Task<string> read = reader.ReadToEndAsync();
				if (await Task.WhenAny(new Task[2]
				{
					read,
					Task.Delay(5000)
				}) != read)
				{
					await Respond(c, 408, new
					{
						error = "Request timed out."
					});
					return;
				}
				request = JsonConvert.DeserializeObject<BackgroundRequest>(await read, new JsonSerializerSettings
				{
					MaxDepth = 4,
					TypeNameHandling = (TypeNameHandling)0,
					MissingMemberHandling = (MissingMemberHandling)1
				});
			}
			catch (JsonException)
			{
				await Respond(c, 400, new
				{
					error = "Invalid background request."
				});
				return;
			}
			if (request == null || request.World != identity.World || request.Biome == null || (request.Biome != "automatic" && !BackgroundBiomes.ContainsKey(request.Biome)))
			{
				await Respond(c, 400, new
				{
					error = "Choose a valid background for your logged-in world."
				});
				return;
			}
			PlayerSnapshot playerSnapshot = store.Players(identity.World).FirstOrDefault((PlayerSnapshot p) => p.PlayerId == identity.PlayerId);
			if (playerSnapshot == null || !playerSnapshot.ShareProfile || !AllowedBackgrounds(identity.World, identity.PlayerId).Contains(request.Biome))
			{
				await Respond(c, 403, new
				{
					error = "Choose scenery up to your highest recorded boss victory. Profile sharing must be enabled; team kill credit counts."
				});
				return;
			}
			store.SetBackgroundPreference(identity.World, identity.PlayerId, request.Biome);
			await Respond(c, 200, LoginSession(identity.World, identity));
		}

		private void ApplyProfileBiomes(IReadOnlyList<PlayerSnapshot> players, IReadOnlyList<SagaEvent> history)
		{
			Dictionary<string, DateTime> dictionary = new Dictionary<string, DateTime>(StringComparer.Ordinal);
			foreach (SagaEvent item in history.Where((SagaEvent e) => e.Kind == "kill" && e.Boss && !e.NemesisBoss && KnownBosses.Any(((string Key, string Name, int Order, string Biome) b) => b.Key.Equals(BossCatalog.Prefab(e.Prefab), StringComparison.OrdinalIgnoreCase))))
			{
				foreach (string item2 in (from id in item.Contributors.Concat(new string[1] { item.PlayerId })
					where !string.IsNullOrEmpty(id)
					select id).Distinct<string>(StringComparer.Ordinal))
				{
					if (!dictionary.TryGetValue(item2, out var value) || item.Utc > value)
					{
						dictionary[item2] = item.Utc;
					}
				}
			}
			foreach (PlayerSnapshot player in players)
			{
				player.ProfileBiome = (player.ShareProfile ? "Meadows" : "");
				player.ProfileBiomeEvidence = (player.ShareProfile ? "Ambient Meadows backdrop; no recorded boss victory" : "");
			}
			foreach (PlayerSnapshot player2 in players)
			{
				IReadOnlyList<string> receipts = store.CompletedBossKeys(player2.World, player2.PlayerId);
				(string, string, int, string) tuple = (from b in KnownBosses
					where receipts.Contains(b.Key)
					orderby b.Order descending
					select b).FirstOrDefault();
				player2.CompletedBossKeys = (player2.ShareProfile ? (from b in KnownBosses
					where receipts.Contains(b.Key)
					orderby b.Order
					select b.Key).ToList() : new List<string>());
				player2.ProgressionTier = (player2.ShareProfile ? tuple.Item3 : 0);
				player2.ProgressionBoss = (player2.ShareProfile ? (tuple.Item2 ?? "") : "");
				player2.LastAchievementUtc = ((player2.ShareProfile && dictionary.TryGetValue(player2.PlayerId, out var value2)) ? new DateTime?(value2) : ((DateTime?)null));
				if (player2.ShareProfile && tuple.Item3 > 0)
				{
					player2.ProfileBiome = (new string[8] { "Meadows", "BlackForest", "Swamp", "Mountain", "Plains", "Mistlands", "Ashlands", "DeepNorth" })[Math.Min(7, tuple.Item3)];
					player2.ProfileBiomeEvidence = "Recorded team victory over " + tuple.Item2;
				}
				player2.BackgroundUnlocked = player2.ShareProfile;
				player2.BackgroundPreference = (player2.BackgroundUnlocked ? store.BackgroundPreference(player2.World, player2.PlayerId) : "automatic");
				if (player2.BackgroundUnlocked && player2.BackgroundPreference != "automatic" && AllowedBackgrounds(player2.World, player2.PlayerId).Contains(player2.BackgroundPreference))
				{
					player2.ProfileBiome = BackgroundBiomes[player2.BackgroundPreference];
					player2.ProfileBiomeEvidence = "Chosen by this Viking within their recorded boss progression";
				}
			}
		}

		public static bool MediaId(string s)
		{
			if (s != null)
			{
				if (!(s == ""))
				{
					if (s.Length == 64)
					{
						return s.All((char c) => (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'));
					}
					return false;
				}
				return true;
			}
			return false;
		}

		public static bool ValidMedia(MediaUpload m)
		{
			if (m == null || !TextValid(m.World, 100, required: true) || !TextValid(m.PlayerId, 100, required: true) || m.Id == "" || !MediaId(m.Id) || (m.Kind != "icon" && m.Kind != "portrait" && m.Kind != "map-icon") || m.Png == null || m.Png.Length < 33 || m.Png.Length > ((m.Kind == "portrait") ? 1048576 : 49152))
			{
				return false;
			}
			byte[] png = m.Png;
			byte[] array = new byte[16]
			{
				137, 80, 78, 71, 13, 10, 26, 10, 0, 0,
				0, 13, 73, 72, 68, 82
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (png[i] != array[i])
				{
					return false;
				}
			}
			long num = 0L;
			long num2 = 0L;
			for (int j = 16; j < 20; j++)
			{
				num = num * 256 + png[j];
			}
			for (int k = 20; k < 24; k++)
			{
				num2 = num2 * 256 + png[k];
			}
			if (num < 1 || num2 < 1 || num > ((m.Kind == "portrait") ? 1024 : 512) || num2 > ((m.Kind == "portrait") ? 1536 : 512))
			{
				return false;
			}
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(png)).Replace("-", "").ToLowerInvariant() == m.Id;
		}

		public bool UploadMedia(MediaUpload m, Action<bool>? committed = null)
		{
			if (!ValidMedia(m))
			{
				return false;
			}
			MediaUpload copy = Copy(m);
			return Enqueue(delegate
			{
				try
				{
					store.Media(copy);
					committed?.Invoke(obj: true);
				}
				catch
				{
					committed?.Invoke(obj: false);
					throw;
				}
			});
		}

		private async Task ServeMedia(HttpListenerContext c, string world, string player, string id)
		{
			if (!MediaId(id) || id == "" || !TextValid(player, 100, required: true))
			{
				await Respond(c, 404, new
				{
					error = "Artwork unavailable"
				});
				return;
			}
			PlayerSnapshot playerSnapshot = store.Players(world).FirstOrDefault((PlayerSnapshot x) => x.PlayerId == player);
			if ((playerSnapshot == null || !playerSnapshot.ShareProfile || (playerSnapshot.PortraitId != id && !playerSnapshot.Gear.Any((GearItem g) => g.IconId == id || g.Sockets.Any((GemSocket s) => s.IconId == id)) && !playerSnapshot.Hotbar.Any((GearItem g) => g.IconId == id || g.Sockets.Any((GemSocket s) => s.IconId == id)))) && !VisiblePinMedia(world, player, id))
			{
				await Respond(c, 404, new
				{
					error = "Artwork unavailable"
				});
				return;
			}
			byte[] array = store.Media(world, player, id);
			if (array == null)
			{
				await Respond(c, 404, new
				{
					error = "Artwork pending"
				});
				return;
			}
			c.Response.StatusCode = 200;
			c.Response.ContentType = "image/png";
			c.Response.ContentLength64 = array.Length;
			await c.Response.OutputStream.WriteAsync(array, 0, array.Length);
			c.Response.Close();
		}

		public SagaService(SagaOptions options, HttpClient? loreClient = null)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			this.loreClient = loreClient;
			this.options = options;
			diagnostics = new SagaDiagnostics(options);
			store = new SagaStore(options.DataDirectory);
			if (TextValid(options.World, 100, required: true) && TextValid(options.WorldName, 200, required: true))
			{
				store.WorldName(options.World, options.WorldName);
			}
			queue = new BlockingCollection<Action>(Math.Max(64, options.QueueCapacity));
		}

		public void Start()
		{
			if (started)
			{
				return;
			}
			if (!string.IsNullOrWhiteSpace(options.ListenPrefix) && options.RequireViewerToken && options.ViewerToken.Length < 24)
			{
				throw new ArgumentException("ViewerToken must contain at least 24 characters.");
			}
			started = true;
			writer = Task.Run(delegate
			{
				foreach (Action item in queue.GetConsumingEnumerable())
				{
					try
					{
						item();
					}
					catch (Exception ex)
					{
						failure = ex.GetType().Name;
						errorId = diagnostics.Report("storage", ex);
					}
				}
			});
			foreach (string item2 in store.Worlds().Concat(new string[1] { options.World }).Distinct())
			{
				store.QueueServerLore(item2);
			}
			lore = Task.Run((Func<Task?>)LoreLoop);
			if (string.IsNullOrWhiteSpace(options.ListenPrefix))
			{
				return;
			}
			if (options.RequireViewerToken && options.ViewerToken.Length < 24)
			{
				throw new ArgumentException("ViewerToken must contain at least 24 characters.");
			}
			listener = new HttpListener();
			listener.Prefixes.Add(options.ListenPrefix);
			try
			{
				listener.Start();
				web = Task.Run(async delegate
				{
					WarnWebsiteMismatch();
					await WebLoop();
				});
			}
			catch (Exception error)
			{
				errorId = diagnostics.Report("website-listener", error);
				listener.Close();
				listener = null;
			}
		}

		private bool Enqueue(Action a)
		{
			if (disposed || queue.IsAddingCompleted)
			{
				return false;
			}
			try
			{
				if (queue.TryAdd(a))
				{
					return true;
				}
			}
			catch (InvalidOperationException)
			{
			}
			Interlocked.Increment(ref rejected);
			return false;
		}

		private static T Copy<T>(T value)
		{
			return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject((object)value));
		}

		private static bool TextValid(string? x, int max, bool required = false)
		{
			if (x != null && x.Length <= max && (!required || x.Length > 0))
			{
				return !x.Any(char.IsControl);
			}
			return false;
		}

		private static bool Coordinate(float? x)
		{
			if (x.HasValue)
			{
				if (!float.IsNaN(x.Value) && !float.IsInfinity(x.Value))
				{
					return Math.Abs(x.Value) <= 20000f;
				}
				return false;
			}
			return true;
		}

		private static bool StatsValid(Dictionary<string, float>? values)
		{
			if (values != null && values.Count <= 128)
			{
				return values.All<KeyValuePair<string, float>>((KeyValuePair<string, float> p) => TextValid(p.Key, 160) && !float.IsNaN(p.Value) && !float.IsInfinity(p.Value));
			}
			return false;
		}

		private static bool SocketsValid(List<GemSocket>? sockets)
		{
			if (sockets != null && sockets.Count <= 11)
			{
				return sockets.All((GemSocket s) => s != null && TextValid(s.Prefab, 200) && TextValid(s.Name, 200) && MediaId(s.IconId) && s.Effects != null && s.Effects.Count <= 8 && s.Effects.All((string e) => TextValid(e, 240)));
			}
			return false;
		}

		private static bool GearValid(GearItem? g)
		{
			if (g != null && SocketsValid(g.Sockets) && RarityColors.Valid(g.SocketColor) && g.HotbarSlot >= 0 && g.HotbarSlot <= 8 && MediaId(g.IconId) && TextValid(g.Name, 200) && TextValid(g.Prefab, 200) && TextValid(g.Type, 100) && TextValid(g.Slot, 100) && TextValid(g.Rarity, 80) && RarityColors.Valid(g.RarityColor) && TextValid(g.Note, 2000) && g.Quality >= 1 && g.Quality <= 1000 && StatsValid(g.BaseStats) && StatsValid(g.Stats) && g.Effects != null && g.Effects.Count <= 64 && g.Effects.All((string e) => TextValid(e, 500)) && !float.IsNaN(g.Durability) && !float.IsInfinity(g.Durability) && !float.IsNaN(g.MaxDurability))
			{
				return !float.IsInfinity(g.MaxDurability);
			}
			return false;
		}

		public static bool ValidEvent(SagaEvent e)
		{
			if (SocketsValid(e.Sockets) && RarityColors.Valid(e.SocketColor) && TextValid(e.Id, 240, required: true) && TextValid(e.World, 100, required: true) && TextValid(e.PlayerId, 100) && TextValid(e.PlayerName, 100) && TextValid(e.Name, 200) && TextValid(e.Prefab, 200) && TextValid(e.Source, 100) && TextValid(e.Provenance, 240) && TextValid(e.Rarity, 80) && RarityColors.Valid(e.RarityColor) && TextValid(e.ItemType, 100) && TextValid(e.Biome, 100) && e.Effects != null && e.Effects.Count <= 64 && e.Effects.All((string x) => TextValid(x, 500)) && e.Contributors != null && e.Contributors.Count <= 64 && e.Contributors.All((string x) => TextValid(x, 100)) && new string[8] { "kill", "drop", "collect", "pickup", "death", "join", "leave", "bounty" }.Contains(e.Kind) && (e.Kind != "bounty" || e.Amount == 1) && (!e.DurationSeconds.HasValue || (!double.IsNaN(e.DurationSeconds.Value) && !double.IsInfinity(e.DurationSeconds.Value) && e.DurationSeconds.Value > 0.0 && e.DurationSeconds.Value <= 604800.0)) && e.Stars >= 0 && e.Stars <= 1000 && e.Amount > 0 && e.Amount <= 1000000 && e.Quality >= 1 && e.Quality <= 1000 && Coordinate(e.X) && Coordinate(e.Z) && e.Utc.Kind == DateTimeKind.Utc && e.Utc <= DateTime.UtcNow.AddMinutes(2.0))
			{
				return e.Utc >= new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
			}
			return false;
		}

		public bool TryEvent(SagaEvent e)
		{
			return TryEvent(e, null);
		}

		public bool TryEvent(SagaEvent e, Action<bool>? committed)
		{
			if (!ValidEvent(e) || (e.Kind == "collect" && string.IsNullOrEmpty(e.Provenance)))
			{
				Interlocked.Increment(ref rejected);
				return false;
			}
			SagaEvent copy = Copy(e);
			return Enqueue(delegate
			{
				try
				{
					store.AddEvent(copy);
					if (!string.IsNullOrEmpty(copy.PlayerId) && (copy.Kind == "kill" || copy.Kind == "collect" || copy.Kind == "death" || copy.Kind == "bounty" || copy.Kind == "leave"))
					{
						store.QueueLore(copy.World, copy.PlayerId);
					}
					committed?.Invoke(obj: true);
				}
				catch
				{
					committed?.Invoke(obj: false);
					throw;
				}
			});
		}

		public bool UpdatePlayer(PlayerSnapshot p)
		{
			if (p.NemesisScore.HasValue && !Finite(p.NemesisScore.Value))
			{
				p.NemesisScore = null;
			}
			if (p.Gold < 0 || !TextValid(p.PortraitStatus, 300) || p.Hotbar == null || p.Hotbar.Count > 8 || p.Hotbar.Any((GearItem g) => !GearValid(g) || g.HotbarSlot < 1) || p.Hotbar.Select((GearItem g) => g.HotbarSlot).Distinct().Count() != p.Hotbar.Count || !MediaId(p.PortraitId) || p.EffectiveResistances == null || p.EffectiveResistances.Count > 32 || p.EffectiveResistances.Any<KeyValuePair<string, string>>((KeyValuePair<string, string> r) => !TextValid(r.Key, 80) || !TextValid(r.Value, 100)) || !TextValid(p.World, 100, required: true) || !TextValid(p.PlayerId, 100, required: true) || !TextValid(p.Name, 100) || p.Gear == null || p.Gear.Count > 32 || p.Gear.Any((GearItem g) => !GearValid(g)) || !StatsValid(p.EffectiveStats) || !TextValid(p.StatsNote, 2000) || !Coordinate(p.X) || !Coordinate(p.Z) || JsonConvert.SerializeObject((object)p).Length > 65536)
			{
				return false;
			}
			PlayerSnapshot copy = Copy(p);
			copy.ProfileBiome = "";
			copy.ProfileBiomeEvidence = "";
			copy.Utc = DateTime.UtcNow;
			copy.LastSeenUtc = copy.Utc;
			copy.PositionUtc = ((copy.SharePosition && copy.X.HasValue && copy.Z.HasValue) ? new DateTime?(copy.Utc) : ((DateTime?)null));
			copy.PositionLive = false;
			if (!copy.SharePosition)
			{
				copy.X = null;
				copy.Z = null;
			}
			return Enqueue(delegate
			{
				PlayerSnapshot playerSnapshot = store.Players(copy.World).FirstOrDefault((PlayerSnapshot x) => x.PlayerId == copy.PlayerId);
				if (copy.ShareProfile && playerSnapshot != null && playerSnapshot.ShareProfile && playerSnapshot.PortraitId != "" && (copy.PortraitId == "" || (copy.PortraitId != playerSnapshot.PortraitId && !store.HasMedia(copy.World, copy.PlayerId, copy.PortraitId))))
				{
					copy.PortraitId = playerSnapshot.PortraitId;
				}
				store.Player(copy);
				if (copy.Online && (playerSnapshot == null || !playerSnapshot.Online))
				{
					RecordTransition(copy, "join");
				}
			});
		}

		public bool Explore(ExplorationBatch b)
		{
			return Explore(b, null);
		}

		public bool Explore(ExplorationBatch b, Action<bool>? committed)
		{
			if (!TextValid(b.World, 100, required: true) || !TextValid(b.PlayerId, 100, required: true) || b.CellSize != 64 || b.Cells == null || b.Cells.Count > 1024 || ((IEnumerable<MapCell>)b.Cells).Sum((Func<MapCell, long>)((MapCell c) => c.TerrainPixels?.Length ?? 0)) > 96000 || b.Cells.Any((MapCell c) => Math.Abs((long)c.X) > 313 || Math.Abs((long)c.Z) > 313 || !TextValid(c.Biome, 80) || !TerrainTile.Valid(c.TerrainPixels) || float.IsNaN(c.Height) || float.IsInfinity(c.Height)))
			{
				return false;
			}
			ExplorationBatch copy = Copy(b);
			return Enqueue(delegate
			{
				try
				{
					store.Explore(copy);
					committed?.Invoke(obj: true);
				}
				catch
				{
					committed?.Invoke(obj: false);
					throw;
				}
			});
		}

		public bool TouchPresence(string world, string playerId, string name, bool? publicPosition = null)
		{
			if (!TextValid(world, 100, required: true) || !TextValid(playerId, 100, required: true) || !TextValid(name, 100))
			{
				return false;
			}
			return Enqueue(delegate
			{
				PlayerSnapshot playerSnapshot = store.Players(world).FirstOrDefault((PlayerSnapshot x) => x.PlayerId == playerId);
				bool num = playerSnapshot == null || !playerSnapshot.Online;
				playerSnapshot = playerSnapshot ?? new PlayerSnapshot
				{
					World = world,
					PlayerId = playerId,
					Utc = DateTime.MinValue,
					StatsNote = "No equipment snapshot received."
				};
				playerSnapshot.Name = name;
				playerSnapshot.Online = true;
				if (publicPosition == false)
				{
					playerSnapshot.SharePosition = false;
					playerSnapshot.X = null;
					playerSnapshot.Z = null;
					playerSnapshot.PositionUtc = null;
					playerSnapshot.PositionLive = false;
				}
				playerSnapshot.LastSeenUtc = DateTime.UtcNow;
				store.Player(playerSnapshot);
				if (num)
				{
					RecordTransition(playerSnapshot, "join");
				}
			});
		}

		public void SetOffline(string world, string playerId)
		{
			Enqueue(delegate
			{
				PlayerSnapshot playerSnapshot = store.Players(world).FirstOrDefault((PlayerSnapshot x) => x.PlayerId == playerId);
				if (playerSnapshot != null && playerSnapshot.Online)
				{
					playerSnapshot.Online = false;
					playerSnapshot.LastSeenUtc = DateTime.UtcNow;
					store.Player(playerSnapshot);
					RecordTransition(playerSnapshot, "leave");
					store.QueueLore(world, playerId);
				}
			});
		}

		private void RecordTransition(PlayerSnapshot p, string kind)
		{
			store.AddEvent(new SagaEvent
			{
				Id = "session-" + Guid.NewGuid().ToString("N"),
				World = p.World,
				PlayerId = p.PlayerId,
				PlayerName = p.Name,
				Kind = kind,
				Utc = (p.LastSeenUtc ?? DateTime.UtcNow),
				Source = "server-connection"
			});
		}

		public bool Flush(int milliseconds = 10000)
		{
			TaskCompletionSource<bool> signal = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
			if (!Enqueue(delegate
			{
				signal.TrySetResult(result: true);
			}))
			{
				return false;
			}
			return signal.Task.Wait(milliseconds);
		}

		public object State(string world, TimeWindow window, HashSet<string>? selected = null)
		{
			SlsWorldState slsWorldState = ActiveSls(world);
			List<PlayerSnapshot> list = store.Players(world);
			foreach (PlayerSnapshot item in list)
			{
				if (!item.ShareProfile || !slsWorldState.Installed || !slsWorldState.NemesisEnabled)
				{
					item.NemesisScore = null;
				}
			}
			HashSet<string> shared = new HashSet<string>(from p in list
				where p.ShareMap
				select p.PlayerId);
			HashSet<string> visible = new HashSet<string>(from p in list
				where p.ShareProfile
				select p.PlayerId);
			visible.Add("");
			IReadOnlyList<SagaEvent> readOnlyList = store.AnalyticsHistory(world);
			ApplyProfileBiomes(list, readOnlyList);
			List<SagaEvent> list2 = readOnlyList.Where((SagaEvent e) => window.Contains(e.Utc) && (selected == null || selected.Contains(e.PlayerId)) && visible.Contains(e.PlayerId)).ToList();
			SanitizePlayers(list, slsWorldState);
			SagaEvent[] array = list2.OrderByDescending((SagaEvent e) => e.Utc).Take(5000).Select(Copy)
				.ToArray();
			store.HideUnknownEventCoordinates(world, shared, array);
			SagaEvent[] array2 = array;
			foreach (SagaEvent obj in array2)
			{
				obj.Contributors = obj.Contributors.Where((string id) => !string.IsNullOrEmpty(id) && visible.Contains(id)).Distinct<string>(StringComparer.Ordinal).ToList();
			}
			List<SagaEvent> list3 = list2.Where((SagaEvent e) => e.Kind == "kill").ToList();
			List<SagaEvent> source = list2.Where((SagaEvent e) => e.Kind == "drop" || e.Kind == "collect" || e.Kind == "pickup").ToList();
			SagaChapter[] chapters = (from c in store.Chapters(world)
				where GeneratedChapter(c) && visible.Contains(c.PlayerId) && c.Participants.All((SagaParticipant p) => p.PlayerId != "" && visible.Contains(p.PlayerId)) && c.MapParticipants.All((string id) => id != "" && visible.Contains(id) && shared.Contains(id))
				select c).Select(WithoutTemplateBio).ToArray();
			return new
			{
				bossCatalog = KnownBosses.Select(((string Key, string Name, int Order, string Biome) b) => new
				{
					key = b.Key,
					name = b.Name,
					order = b.Order
				}).ToArray(),
				sls = SlsSummary(slsWorldState),
				server = new
				{
					name = options.ServerName,
					address = options.ServerAddress
				},
				world = world,
				worlds = store.Worlds(),
				worldNames = store.WorldNames(),
				trackingSince = store.TrackingSince,
				statisticsSince = store.StatisticsSince,
				detailRetentionDays = options.RetentionDays,
				partialHistory = (window.From < store.TrackingSince || (store.StatisticsSince.HasValue && window.From < store.StatisticsSince.Value)),
				from = window.From,
				to = window.To,
				players = list,
				events = array,
				eventCount = list2.Count,
				eventsTruncated = (list2.Count > 5000),
				chapters = chapters,
				synthetic = options.Synthetic,
				stats = new
				{
					integrations = IntegrationSummary(list.Where((PlayerSnapshot p) => p.ShareProfile && (selected == null || selected.Contains(p.PlayerId))), slsWorldState.Installed && slsWorldState.NemesisEnabled),
					equippedGems = list.Where((PlayerSnapshot p) => p.ShareProfile && (selected == null || selected.Contains(p.PlayerId))).Sum((PlayerSnapshot p) => p.Gear.Sum((GearItem g) => g.Sockets.Count((GemSocket x) => x.Prefab != ""))),
					nemesisBossKills = readOnlyList.Where((SagaEvent e) => e.Kind == "kill" && e.NemesisBoss && window.Contains(e.Utc)).Count((SagaEvent e) => e.Contributors.Concat(new string[1] { e.PlayerId }).Any((string id) => id != "" && visible.Contains(id) && (selected == null || selected.Contains(id)))),
					bounties = list2.Count((SagaEvent e) => e.Kind == "bounty"),
					deaths = list2.Count((SagaEvent e) => e.Kind == "death"),
					bossKills = list3.Count((SagaEvent e) => e.Boss),
					highestStars = ((list3.Count == 0) ? ((int?)null) : new int?(list3.Max((SagaEvent e) => e.Stars))),
					creatureTypes = list3.Select((SagaEvent e) => (!string.IsNullOrEmpty(e.Prefab)) ? e.Prefab : e.Name).Distinct<string>(StringComparer.OrdinalIgnoreCase).Count(),
					rarityCollected = (from e in source
						where e.Kind == "collect"
						group e by LeaderboardRarity(e.Rarity)).ToDictionary((IGrouping<string, SagaEvent> g) => g.Key, (IGrouping<string, SagaEvent> g) => ((IEnumerable<SagaEvent>)g).Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount))),
					kills = list3.Count,
					dropped = source.Where((SagaEvent e) => e.Kind == "drop").Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount)),
					collected = source.Where((SagaEvent e) => e.Kind == "collect").Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount)),
					unknownPickups = source.Where((SagaEvent e) => e.Kind == "pickup").Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount)),
					stars = (from e in list3
						group e by e.Stars).ToDictionary((IGrouping<int, SagaEvent> g) => g.Key.ToString(), (IGrouping<int, SagaEvent> g) => g.Count()),
					creatures = (from e in list3
						group e by new { e.Prefab, e.Name, e.Stars, e.Biome, e.Boss } into g
						select new
						{
							Prefab = g.Key.Prefab,
							Name = g.Key.Name,
							Stars = g.Key.Stars,
							Biome = g.Key.Biome,
							Boss = g.Key.Boss,
							count = g.Count()
						} into x
						orderby x.count descending
						select x).ToArray(),
					loot = (from e in source
						group e by new { e.Prefab, e.Name, e.Kind, e.ItemType, e.Quality, e.Rarity } into g
						select new
						{
							Prefab = g.Key.Prefab,
							Name = g.Key.Name,
							Kind = g.Key.Kind,
							ItemType = g.Key.ItemType,
							Quality = g.Key.Quality,
							Rarity = g.Key.Rarity,
							rarityColor = ((from e in g
								orderby e.Utc descending
								select e.RarityColor).FirstOrDefault((string c) => !string.IsNullOrEmpty(c)) ?? ""),
							amount = ((IEnumerable<SagaEvent>)g).Sum((Func<SagaEvent, long>)((SagaEvent e) => e.Amount))
						} into x
						orderby x.amount descending
						select x).ToArray()
				},
				health = Health(),
				coverage = "Requires Sagas on all players; unknown provenance pickups excluded from earned loot. Fog-aware 2D terrain."
			};
		}

		public object Map(string world, HashSet<string>? selected = null)
		{
			HashSet<string> players = new HashSet<string>(from p in store.Players(world)
				where p.ShareMap && (selected == null || selected.Contains(p.PlayerId))
				select p.PlayerId);
			List<MapCell> cells = store.Cells(world, players);
			return new
			{
				sls = SlsMap(world, cells),
				cellSize = 64,
				cells = cells,
				imported = store.Imported(world, players),
				label = "Voluntarily shared known map; imported discoveries predate tracking. Runtime-rendered Valheim map with personal fog. Older or fallback tiles may have lower detail."
			};
		}

		private static bool LoreEvent(SagaEvent e)
		{
			if (!(e.Kind == "kill") && !(e.Kind == "collect") && !(e.Kind == "death"))
			{
				return e.Kind == "bounty";
			}
			return true;
		}

		private bool Milestone(IReadOnlyList<SagaEvent> events)
		{
			if (events.Count > 0)
			{
				if (events.Count < Math.Max(1, options.LoreMilestoneEvents))
				{
					return events.Any((SagaEvent e) => e.Boss || e.Stars >= 3 || !string.IsNullOrEmpty(e.Rarity));
				}
				return true;
			}
			return false;
		}

		private static SagaChapter WithoutTemplateBio(SagaChapter c)
		{
			if (c.CharacterBioModel == "local-template")
			{
				c.CharacterBio = "";
			}
			if (c.ServerBioModel == "local-template")
			{
				c.ServerBio = "";
			}
			return c;
		}

		private static bool GeneratedChapter(SagaChapter c)
		{
			if (c.Model != "local-template")
			{
				return !string.IsNullOrWhiteSpace(c.Text);
			}
			return false;
		}

		private bool ChapterContextVisible(SagaChapter chapter)
		{
			List<PlayerSnapshot> players = store.Players(chapter.World);
			if (chapter.Participants.All((SagaParticipant p) => players.Any((PlayerSnapshot x) => x.PlayerId == p.PlayerId && x.ShareProfile)))
			{
				return chapter.MapParticipants.All((string id) => players.Any((PlayerSnapshot p) => p.PlayerId == id && p.ShareProfile && p.ShareMap));
			}
			return false;
		}

		private bool SharedChapterVisible(SagaChapter chapter, HashSet<string> consent)
		{
			if (ChapterContextVisible(chapter) && chapter.Scope == "server" && chapter.Participants.Count > 0)
			{
				return chapter.Participants.All((SagaParticipant p) => consent.Contains(p.PlayerId));
			}
			return false;
		}

		public object ServerSaga(string world)
		{
			HashSet<string> consent = new HashSet<string>(from p in store.Players(world)
				where p.ShareProfile
				select p.PlayerId);
			SagaChapter[] array = (from c in store.ServerChapters(world)
				where GeneratedChapter(c) && SharedChapterVisible(c, consent)
				select c).Select(WithoutTemplateBio).ToArray();
			SagaChapter sagaChapter = array.LastOrDefault();
			HashSet<string> covered = new HashSet<string>(store.ServerChapters(world).Where(GeneratedChapter).SelectMany((SagaChapter c) => c.EventIds));
			SagaEvent[] events = (from e in store.Events(world, new TimeWindow(DateTime.MinValue, DateTime.UtcNow), consent)
				where LoreEvent(e) && !covered.Contains(e.Id)
				select e).ToArray();
			Dictionary<string, string> dictionary = store.WorldNames();
			string value;
			return new
			{
				server = new
				{
					name = options.ServerName,
					address = options.ServerAddress
				},
				world = world,
				worldName = (dictionary.TryGetValue(world, out value) ? value : ((world == options.World) ? options.WorldName : "")),
				trackingSince = store.TrackingSince,
				synthetic = options.Synthetic,
				bio = (sagaChapter?.ServerBio ?? ""),
				bioModel = (sagaChapter?.ServerBioModel ?? "local-template"),
				chapters = array,
				participants = (from g in array.SelectMany((SagaChapter c) => c.Participants).GroupBy<SagaParticipant, string>((SagaParticipant p) => p.PlayerId, StringComparer.Ordinal)
					select g.Last()).OrderBy<SagaParticipant, string>((SagaParticipant p) => p.Name, StringComparer.Ordinal).ToArray(),
				pending = (options.LoreEnabled && !string.IsNullOrWhiteSpace(options.OpenRouterKey) && store.PendingServerLore().Contains(world) && Milestone(events)),
				fictional = true
			};
		}

		private async Task LoreLoop()
		{
			LoreEngine engine = new LoreEngine(options, loreClient, () => store.ReserveLoreRequest(DateTime.UtcNow, options.LoreDailyBudget));
			while (!stop.IsCancellationRequested)
			{
				try
				{
					if (DateTime.UtcNow >= nextRetention)
					{
						store.ApplyRetention(DateTime.UtcNow, options.RetentionDays, options.StatisticsRetentionDays);
						nextRetention = DateTime.UtcNow.AddMinutes(5.0);
					}
					if (!options.LoreEnabled || string.IsNullOrWhiteSpace(options.OpenRouterKey))
					{
						await Task.Delay(10000, stop.Token);
						continue;
					}
					(string World, string Player)[] array = store.PendingLore();
					for (int num = 0; num < array.Length; num++)
					{
						(string World, string Player) item = array[num];
						if (stop.IsCancellationRequested)
						{
							break;
						}
						PlayerSnapshot playerSnapshot = store.Players(item.World).FirstOrDefault((PlayerSnapshot x) => x.PlayerId == item.Player);
						if (playerSnapshot == null)
						{
							continue;
						}
						if (!playerSnapshot.ShareProfile)
						{
							store.CompleteLore(item.World, item.Player);
							continue;
						}
						SagaChapter[] source = (from c in store.Chapters(item.World)
							where c.PlayerId == item.Player && GeneratedChapter(c)
							select c).ToArray();
						SagaChapter sagaChapter = source.LastOrDefault(ChapterContextVisible);
						if (sagaChapter != null && (DateTime.UtcNow - sagaChapter.Utc).TotalMinutes < (double)options.LoreCooldownMinutes)
						{
							continue;
						}
						HashSet<string> covered = new HashSet<string>(source.SelectMany((SagaChapter c) => c.EventIds));
						List<SagaEvent> list = (from e in store.Events(item.World, new TimeWindow(DateTime.MinValue, DateTime.UtcNow), new HashSet<string> { item.Player }).Where(LoreEvent)
							orderby e.Utc
							select e).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal).ToList();
						List<SagaEvent> list2 = list.Where((SagaEvent e) => !covered.Contains(e.Id)).ToList();
						if (Milestone(list2) && store.LoreReady(item.World, item.Player))
						{
							SagaOptions playerOptions = PlayerLoreOptions(item.World, item.Player);
							SagaChapter sagaChapter2 = await new LoreEngine(playerOptions, loreClient, () => (playerOptions != options) ? store.ReservePersonalLore(item.World, item.Player, playerOptions.LoreDailyBudget) : store.ReserveLoreRequest(DateTime.UtcNow, options.LoreDailyBudget)).GenerateAsync(playerSnapshot.Name, list2.Take(100).ToArray(), sagaChapter, stop.Token, list, store.NarrativeContext(item.World, item.Player));
							if (GeneratedChapter(sagaChapter2) && store.Players(item.World).Any((PlayerSnapshot x) => x.PlayerId == item.Player && x.ShareProfile) && ChapterContextVisible(sagaChapter2))
							{
								store.Chapter(sagaChapter2);
							}
							else
							{
								store.DeferLore(item.World, item.Player);
							}
						}
					}
					string[] array2 = store.PendingServerLore();
					foreach (string world in array2)
					{
						if (stop.IsCancellationRequested)
						{
							break;
						}
						Dictionary<string, string> dictionary = (from p in store.Players(world)
							where p.ShareProfile
							select p).ToDictionary<PlayerSnapshot, string, string>((PlayerSnapshot p) => p.PlayerId, (PlayerSnapshot p) => p.Name, StringComparer.Ordinal);
						HashSet<string> consent = new HashSet<string>(dictionary.Keys);
						SagaChapter[] source2 = store.ServerChapters(world).Where(GeneratedChapter).ToArray();
						SagaChapter previous = source2.LastOrDefault((SagaChapter c) => SharedChapterVisible(c, consent));
						SagaChapter sagaChapter3 = source2.LastOrDefault();
						if (sagaChapter3 != null && (DateTime.UtcNow - sagaChapter3.Utc).TotalMinutes < (double)options.LoreCooldownMinutes)
						{
							continue;
						}
						HashSet<string> covered2 = new HashSet<string>(source2.SelectMany((SagaChapter c) => c.EventIds));
						List<SagaEvent> list3 = (from e in store.Events(world, new TimeWindow(DateTime.MinValue, DateTime.UtcNow), consent).Where(LoreEvent)
							orderby e.Utc
							select e).ThenBy<SagaEvent, string>((SagaEvent e) => e.Id, StringComparer.Ordinal).ToList();
						foreach (SagaEvent item2 in list3)
						{
							if (string.IsNullOrWhiteSpace(item2.PlayerName))
							{
								item2.PlayerName = dictionary[item2.PlayerId];
							}
						}
						List<SagaEvent> list4 = list3.Where((SagaEvent e) => !covered2.Contains(e.Id)).ToList();
						if (Milestone(list4) && store.LoreReady(world, ""))
						{
							SagaChapter sagaChapter4 = await engine.GenerateServerAsync(options.ServerName, list4.Take(100).ToArray(), previous, stop.Token, list3, store.NarrativeContext(world));
							HashSet<string> consent2 = new HashSet<string>(from p in store.Players(world)
								where p.ShareProfile
								select p.PlayerId);
							if (GeneratedChapter(sagaChapter4) && SharedChapterVisible(sagaChapter4, consent2))
							{
								store.Chapter(sagaChapter4);
							}
							else
							{
								store.DeferLore(world, "");
							}
						}
					}
				}
				catch (OperationCanceledException)
				{
				}
				catch (Exception error)
				{
					diagnostics.Report("lore-worker", error);
				}
				try
				{
					await Task.Delay(10000, stop.Token);
				}
				catch (OperationCanceledException)
				{
					break;
				}
			}
		}

		public object Health()
		{
			return new
			{
				rejected = Rejected,
				storageError = failure,
				errorId = errorId,
				queue = queue.Count
			};
		}

		private async Task WebLoop()
		{
			while (listener != null && listener.IsListening)
			{
				HttpListenerContext context;
				try
				{
					context = await listener.GetContextAsync();
				}
				catch
				{
					break;
				}
				if (!httpSlots.Wait(0))
				{
					context.Response.StatusCode = 503;
					context.Response.Close();
					continue;
				}
				Task.Run(async delegate
				{
					_ = 1;
					try
					{
						await Handle(context);
					}
					catch (Exception error)
					{
						string text = diagnostics.Report("http-request", error);
						try
						{
							await Respond(context, 500, new
							{
								error = "Sagas could not read or serve the recorded data. Check the server diagnostics log using the error ID.",
								errorId = text
							});
						}
						catch
						{
							try
							{
								context.Response.Close();
								goto end_IL_0155;
							}
							catch
							{
								goto end_IL_0155;
							}
							end_IL_0155:;
						}
					}
					finally
					{
						httpSlots.Release();
					}
				});
			}
		}

		private static bool TokenEquals(string expected, string actual)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(expected);
			byte[] bytes2 = Encoding.UTF8.GetBytes(actual);
			int num = bytes.Length ^ bytes2.Length;
			for (int i = 0; i < bytes.Length; i++)
			{
				num |= bytes[i] ^ ((i < bytes2.Length) ? bytes2[i] : 0);
			}
			return num == 0;
		}

		private async Task Handle(HttpListenerContext c)
		{
			c.Response.Headers["X-Content-Type-Options"] = "nosniff";
			c.Response.Headers["Referrer-Policy"] = "no-referrer";
			c.Response.Headers["Cache-Control"] = "no-store";
			c.Response.Headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'";
			string absolutePath = c.Request.Url.AbsolutePath;
			string value;
			if (c.Request.HttpMethod != "GET" && (!(c.Request.HttpMethod == "POST") || (!(absolutePath == "/api/profile-background") && !(absolutePath == "/api/lore-settings"))))
			{
				c.Response.StatusCode = 405;
				c.Response.Close();
			}
			else if (absolutePath == "/api/access")
			{
				await Respond(c, 200, new
				{
					requiresToken = options.RequireViewerToken
				});
			}
			else if (absolutePath.StartsWith("/api/", StringComparison.Ordinal))
			{
				string text = c.Request.Headers["Authorization"] ?? "";
				PlayerLoginIdentity playerLoginIdentity = (text.StartsWith("Bearer ", StringComparison.Ordinal) ? store.AuthenticatePlayer(text.Substring(7)) : null);
				bool flag = options.ViewerToken.Length >= 24 && TokenEquals("Bearer " + options.ViewerToken, text);
				if (playerLoginIdentity == null && !flag && (options.RequireViewerToken || text !

BepInEx/plugins/ValheimSagas/ValheimSagas.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Steamworks;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValheimSagas")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d2ac062f97621ac92b916586f737cf12791d6309")]
[assembly: AssemblyProduct("ValheimSagas")]
[assembly: AssemblyTitle("ValheimSagas")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimSagas
{
	internal sealed class AsyncResource<T> where T : class, IDisposable
	{
		private Task<T>? pending;

		private Task retirement = Task.CompletedTask;

		internal T? Current { get; private set; }

		internal bool Busy
		{
			get
			{
				if (pending == null)
				{
					return !retirement.IsCompleted;
				}
				return true;
			}
		}

		internal bool Begin(Func<T> factory)
		{
			if (Current != null || Busy)
			{
				return false;
			}
			Task task = retirement;
			retirement = Task.CompletedTask;
			task.GetAwaiter().GetResult();
			pending = Task.Run(factory);
			return true;
		}

		internal bool Poll()
		{
			if (pending == null || !pending.IsCompleted)
			{
				return false;
			}
			Task<T> task = pending;
			pending = null;
			Current = task.GetAwaiter().GetResult();
			return true;
		}

		internal void Retire(Action<T>? beforeDispose = null)
		{
			T value = Current;
			Task<T> creating = pending;
			if (value == null && creating == null)
			{
				return;
			}
			Current = null;
			pending = null;
			Task previous = retirement;
			retirement = Task.Run(async delegate
			{
				await previous.ConfigureAwait(continueOnCapturedContext: false);
				T val = value;
				if (creating != null)
				{
					try
					{
						val = await creating.ConfigureAwait(continueOnCapturedContext: false);
					}
					catch
					{
						return;
					}
				}
				if (val != null)
				{
					try
					{
						beforeDispose?.Invoke(val);
					}
					finally
					{
						val.Dispose();
					}
				}
			});
		}
	}
	internal static class BossTiming
	{
		internal static long ObserveDamage(long previous, float before, float maximum, long now)
		{
			if (now <= 0 || float.IsNaN(before) || float.IsNaN(maximum) || float.IsInfinity(before) || float.IsInfinity(maximum) || maximum <= 0f)
			{
				return -1L;
			}
			if (before >= maximum - 0.01f)
			{
				return now;
			}
			if (previous != 0L)
			{
				return previous;
			}
			return -1L;
		}

		internal static double? Elapsed(long started, long now)
		{
			if (started <= 0 || now <= started)
			{
				return null;
			}
			double num = (double)(now - started) / 1000.0;
			if (!(num <= 604800.0))
			{
				return null;
			}
			return num;
		}
	}
	[BepInPlugin("org.valheimsagas.collector", "Valheim Sagas", "0.3.36")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class SagasPlugin : BaseUnityPlugin
	{
		private sealed class ConfigurationManagerAttributes
		{
			private readonly bool hostOwned;

			public bool ReadOnly
			{
				get
				{
					if (hostOwned)
					{
						return RemoteSession;
					}
					return false;
				}
			}

			public ConfigurationManagerAttributes(bool hostOwned)
			{
				this.hostOwned = hostOwned;
			}
		}

		private sealed class PendingWire
		{
			public Packet Packet;

			public Task<WirePayload> Preparation;

			public string Id = Guid.NewGuid().ToString("N");

			public int Index;

			public int Lane;
		}

		private sealed class StartedService : IDisposable
		{
			internal SagaService Service;

			internal string[] Characters = Array.Empty<string>();

			internal double Milliseconds;

			public void Dispose()
			{
				Service.Dispose();
			}
		}

		public sealed class Packet
		{
			[JsonIgnore]
			internal string? Wire;

			public int Version = 1;

			public string AckId = "";

			public SagaEvent? Event;

			public PlayerSnapshot? Player;

			public ExplorationBatch? Exploration;

			public MapPins? Pins;

			public MediaUpload? Media;

			public MediaChunk? MediaChunk;
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class VersionConnectionPatch
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static void Prefix(ZNet __instance, ZNetPeer peer)
			{
				Instance?.RegisterVersion(__instance, peer);
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_ClientHandshake")]
		private static class VersionClientHelloPatch
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static void Prefix(ZNet __instance, ZRpc rpc)
			{
				if ((Object)(object)Instance != (Object)null && !__instance.IsServer())
				{
					rpc.Invoke("Sagas.Version.V1", new object[1] { ((BaseUnityPlugin)Instance).Info.Metadata.Version.ToString() });
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static class VersionPeerInfoPatch
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static bool Prefix(ZNet __instance, ZRpc rpc)
			{
				if (!((Object)(object)Instance == (Object)null))
				{
					return Instance.CheckPeerVersion(__instance, rpc);
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(ZNet), "Disconnect", new Type[] { typeof(ZNetPeer) })]
		private static class VersionDisconnectPatch
		{
			[HarmonyPrefix]
			private static void Prefix(ZNetPeer peer)
			{
				Instance?.peerVersions.Remove(peer.m_rpc);
				Instance?.versionRejected.Remove(peer.m_rpc);
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private static class VersionShutdownPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				Instance?.peerVersions.Clear();
				Instance?.versionRejected.Clear();
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
		private static class VersionErrorPatch
		{
			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Postfix(FejdStartup __instance)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Invalid comparison between Unknown and I4
				SagasPlugin instance = Instance;
				if ((Object)(object)instance == (Object)null || instance.versionFailure.Length == 0 || (int)ZNet.GetConnectionStatus() != 3 || !__instance.m_connectionFailedPanel.activeSelf)
				{
					return;
				}
				object obj = AccessTools.Field(typeof(FejdStartup), "m_connectionFailedError")?.GetValue(__instance);
				if (obj != null)
				{
					PropertyInfo propertyInfo = AccessTools.Property(obj.GetType(), "text");
					if (propertyInfo?.GetValue(obj) is string text && !text.Contains(instance.versionFailure))
					{
						propertyInfo.SetValue(obj, text + "\n\n" + instance.versionFailure);
					}
				}
			}
		}

		private sealed class HiddenShortcutMigration
		{
			public bool Browsable;
		}

		private static PropertyInfo? managerEntry;

		private static GUIStyle? ownershipStyle;

		private ConfigEntry<bool> sharePins;

		private RuntimeArt? pinArt;

		private readonly Dictionary<string, Packet> pendingPinPackets = new Dictionary<string, Packet>();

		private readonly HashSet<string> pinMediaSent = new HashSet<string>();

		private static readonly FieldInfo pinsField = AccessTools.Field(typeof(Minimap), "m_pins");

		private static readonly FieldInfo pinBitsField = AccessTools.Field(typeof(Minimap), "m_explored");

		private static readonly FieldInfo pinSizeField = AccessTools.Field(typeof(Minimap), "m_textureSize");

		private static readonly FieldInfo pinPixelField = AccessTools.Field(typeof(Minimap), "m_pixelSize");

		private double pinScanMaximum;

		private int pinExportCount;

		private List<MapPin>? scanningPins;

		private int pinCursor;

		private int pinSourceCount;

		private float nextPinScan;

		private float nextPinResync;

		private string pinOwner = "";

		private string pinSignature = "";

		private bool pinConsent;

		private Task<(string Signature, Packet[] Packets)>? pinBuild;

		private int pinEpoch;

		private int buildingPinEpoch;

		private bool pinUploadTurn;

		private const string FragmentRpc = "Sagas.Wire.V4";

		private readonly Dictionary<ZRpc, (float start, int count)> fragmentRates = new Dictionary<ZRpc, (float, int)>();

		private readonly WireTransfer wireReceiver = new WireTransfer();

		private readonly Dictionary<int, PendingWire> outgoingWires = new Dictionary<int, PendingWire>();

		private int wireTurn;

		private static readonly FieldInfo? steamConnection = AccessTools.Field(typeof(ZSteamSocket), "m_con");

		private float pressureSince = -1f;

		private float pressureLog;

		private ConfigEntry<KeyboardShortcut> loginShortcut;

		private ConfigEntry<KeyboardShortcut> revokeShortcut;

		private const string LoginRequest = "Sagas.Login.Request.V1";

		private const string LoginResponse = "Sagas.Login.Response.V1";

		private string loginNonce = "";

		private string loginWorld = "";

		private string loginPlayer = "";

		private string clipboardCredential = "";

		private float loginUntil;

		private float clipboardUntil;

		private float nextLoginRequest;

		private readonly HashSet<ZRpc> loginBusy = new HashSet<ZRpc>();

		private readonly Dictionary<ZRpc, float> loginRates = new Dictionary<ZRpc, float>();

		private float nextLoginFailure;

		internal static SagasPlugin? Instance;

		private RuntimeArt? artwork;

		private readonly Dictionary<string, Packet> pendingMedia = new Dictionary<string, Packet>();

		private readonly Dictionary<string, float> sentMedia = new Dictionary<string, float>();

		private readonly HashSet<string> fullyMapped = new HashSet<string>();

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

		private ConfigEntry<string> serverName;

		private ConfigEntry<string> serverAddress;

		private SagaService? service;

		private readonly AsyncResource<StartedService> serviceStartup = new AsyncResource<StartedService>();

		private float nextServiceStart;

		private Harmony? harmony;

		private Outbox? outbox;

		private readonly Dictionary<string, Packet> pendingMaps = new Dictionary<string, Packet>();

		private ConfigEntry<bool> requireToken;

		private ConfigEntry<bool> host;

		private ConfigEntry<bool> shareMap;

		private ConfigEntry<bool> sharePosition;

		private ConfigEntry<bool> shareProfile;

		private ConfigEntry<string> model;

		private ConfigEntry<int> daily;

		private ConfigEntry<int> cooldown;

		private ConfigEntry<int> milestones;

		private ConfigEntry<int> retention;

		private ConfigEntry<int> statisticsRetention;

		private ConfigEntry<string> data;

		private ConfigEntry<string> prefix;

		private ConfigEntry<string> token;

		private ConfigEntry<string> key;

		private ConfigEntry<bool> lore;

		private ConfigEntry<bool> allowPaidLore;

		private readonly HashSet<string> knownCharacters = new HashSet<string>();

		private readonly HashSet<ZRpc> registered = new HashSet<ZRpc>();

		private readonly Dictionary<long, string> online = new Dictionary<long, string>();

		private readonly Dictionary<string, SagaEvent> pending = new Dictionary<string, SagaEvent>();

		private readonly Dictionary<ZRpc, (float start, int count)> rates = new Dictionary<ZRpc, (float, int)>();

		private readonly HashSet<string> sentCells = new HashSet<string>();

		private int mapCursor;

		private int nearMapCursor;

		private bool historyMapTurn;

		private bool importing = true;

		private float nextTick;

		private string activeWorld = "";

		private string lastLocalId = "";

		private string journalId = "";

		private int retryCursor;

		private readonly ConcurrentQueue<Action> committed = new ConcurrentQueue<Action>();

		private readonly Dictionary<string, float> nextWarning = new Dictionary<string, float>();

		private string positionLogKey = "";

		private int startupTraceTicks;

		private bool profileTraceRecorded;

		private static readonly FieldInfo exploredField = AccessTools.Field(typeof(Minimap), "m_explored");

		private const string RpcName = "Sagas.V1";

		private const string AckName = "Sagas.Ack.V1";

		private readonly MediaTransfer mediaTransfer = new MediaTransfer();

		private readonly Dictionary<string, int> mediaCursors = new Dictionary<string, int>();

		private readonly TelemetryBudget telemetryBudget = new TelemetryBudget();

		private readonly Dictionary<string, float> retryAfter = new Dictionary<string, float>();

		private Packet? outboundProfile;

		private float nextUpload;

		private float nextMapImport;

		private int uploadLane;

		private float nextSlsRefresh;

		private SlsZoneCapture? slsCapture;

		private Task<bool>? slsPublish;

		private string slsWorld = "";

		private const string VersionHello = "Sagas.Version.V1";

		private const string VersionReply = "Sagas.VersionReply.V1";

		private const string VersionRejected = "Sagas.VersionRejected.V1";

		private ConfigEntry<bool> requireMatchingVersion;

		private readonly Dictionary<ZRpc, string> peerVersions = new Dictionary<ZRpc, string>();

		private readonly HashSet<ZRpc> versionRejected = new HashSet<ZRpc>();

		private string versionFailure = "";

		private ConfigEntry<KeyboardShortcut> websiteShortcut;

		private ConfigEntry<string> websiteUrl;

		private ConfigEntry<string> websiteOverride;

		private ConfigEntry<int> websitePort;

		private static readonly FieldInfo directServerHost = AccessTools.Field(typeof(ZNet), "m_serverHost");

		private const string WebsiteRequest = "Sagas.Website.Request.V1";

		private const string WebsiteResponse = "Sagas.Website.Response.V1";

		private string websiteNonce = "";

		private string websiteWorld = "";

		private float websiteDeadline;

		private float nextWebsiteOpen;

		private readonly Dictionary<ZRpc, float> websiteRates = new Dictionary<ZRpc, float>();

		internal static bool RemoteSession
		{
			get
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance))
				{
					return !ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static string World
		{
			get
			{
				World val = (Object.op_Implicit((Object)(object)ZNet.instance) ? ZNet.instance.GetWorld() : null);
				if (val == null || val.m_uid == 0L)
				{
					return "";
				}
				return val.m_uid.ToString(CultureInfo.InvariantCulture);
			}
		}

		internal static bool HostSetting(string section)
		{
			if (!(section == "Server"))
			{
				return section == "Lore";
			}
			return true;
		}

		private ConfigEntry<T> BindSetting<T>(string section, string name, T value, string description)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return BindSetting(section, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()));
		}

		private ConfigEntry<T> BindSetting<T>(string section, string name, T value, ConfigDescription description)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			bool flag = HostSetting(section);
			string text = (flag ? "Host only. This machine's setting is used only when it hosts a world. Remote server values are not copied here. Restart the hosted world after changing. " : "Client only. Your personal setting; the server cannot override it. ");
			object[] array = description.Tags.Concat(new object[1]
			{
				new ConfigurationManagerAttributes(flag)
			}).ToArray();
			return ((BaseUnityPlugin)this).Config.Bind<T>(section, name, value, new ConfigDescription(text + description.Description, description.AcceptableValues, array));
		}

		private void SetupConfigurationManager()
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			if (!Chainloader.PluginInfos.TryGetValue("_shudnal.ConfigurationManager", out var value))
			{
				return;
			}
			try
			{
				Type? type = ((object)value.Instance).GetType().Assembly.GetType("ConfigurationManager.ConfigSettingEntry");
				managerEntry = type?.GetProperty("Entry", BindingFlags.Instance | BindingFlags.Public);
				MethodInfo method = ((object)value.Instance).GetType().GetMethod("DrawSynchronizationIndicator", BindingFlags.Static | BindingFlags.NonPublic);
				MethodInfo methodInfo = type?.GetMethod("SetValue", BindingFlags.Instance | BindingFlags.NonPublic);
				if (managerEntry == null || method == null || methodInfo == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Sagas config ownership labels unavailable for this ConfigurationManager version; ownership descriptions still apply.");
					return;
				}
				harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(SagasPlugin), "ConfigOwnershipIndicator", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SagasPlugin), "ConfigOwnershipEdit", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception e)
			{
				Warn("configuration ownership UI", e);
			}
		}

		private static ConfigEntryBase? OwnManagerEntry(object instance)
		{
			if (managerEntry?.DeclaringType == null || !managerEntry.DeclaringType.IsInstanceOfType(instance))
			{
				return null;
			}
			object? obj = managerEntry?.GetValue(instance);
			ConfigEntryBase val = (ConfigEntryBase)((obj is ConfigEntryBase) ? obj : null);
			if (val == null || !((Object)(object)Instance != (Object)null) || val.ConfigFile != ((BaseUnityPlugin)Instance).Config)
			{
				return null;
			}
			return val;
		}

		private static bool ConfigOwnershipIndicator(object __0)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntryBase val = OwnManagerEntry(__0);
			if (val == null)
			{
				return true;
			}
			bool flag = HostSetting(val.Definition.Section);
			GUIContent val2 = new GUIContent(flag ? "<color=#D4B374>S</color>" : "<color=#88C8A8>C</color>", flag ? "Server / host-owned. Applied only on the machine hosting the world. Values and secrets are never synchronized. Locked while connected to another host. These are your local hosting settings, not a view of the remote server's config." : "Client-owned. Your privacy and login shortcuts; never overridden by a host.");
			ownershipStyle = (GUIStyle?)(((object)ownershipStyle) ?? ((object)new GUIStyle(GUI.skin.label)
			{
				richText = true,
				alignment = (TextAnchor)4
			}));
			GUILayout.Label(val2, ownershipStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(22f) });
			return false;
		}

		private static bool ConfigOwnershipEdit(object __instance)
		{
			ConfigEntryBase val = OwnManagerEntry(__instance);
			if (val != null && HostSetting(val.Definition.Section))
			{
				return !RemoteSession;
			}
			return true;
		}

		private void SetupMapDetails()
		{
			sharePins = BindSetting("Privacy", "SharePlayerPins", value: true, "Share your own manually placed map pins with website viewers. On by default for new configs; existing choices are preserved. Requires ShareMap. Personal pins can reveal marked locations outside explored terrain. Pins copied from another Viking's cartography are excluded.");
			pinArt = new RuntimeArt(Warn);
		}

		private void ResetMapDetails()
		{
			pinEpoch++;
			scanningPins = null;
			pinCursor = 0;
			pinOwner = "";
			pinSignature = "";
			nextPinScan = 0f;
			nextPinResync = 0f;
			pendingPinPackets.Clear();
			pinMediaSent.Clear();
			pinArt?.Clear();
		}

		private bool SendMapDetails()
		{
			pinUploadTurn = !pinUploadTurn;
			if (!pinUploadTurn)
			{
				return false;
			}
			foreach (Packet value in pendingPinPackets.Values)
			{
				if (Send(value))
				{
					return true;
				}
			}
			return false;
		}

		private void PumpMapDetails()
		{
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Expected I4, but got Unknown
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_0481: Unknown result type (might be due to invalid IL or missing references)
			//IL_048d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0495: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b2: Expected O, but got Unknown
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0561: Unknown result type (might be due to invalid IL or missing references)
			//IL_0569: Unknown result type (might be due to invalid IL or missing references)
			//IL_0581: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_059b: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c7: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || World != activeWorld || World == "" || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)Minimap.instance))
			{
				return;
			}
			if (!shareMap.Value)
			{
				if (pinOwner != "")
				{
					ResetMapDetails();
				}
				return;
			}
			string owner = lastLocalId;
			if (owner == "")
			{
				return;
			}
			if (pinOwner != owner || pinConsent != sharePins.Value)
			{
				ResetMapDetails();
				pinOwner = owner;
				pinConsent = sharePins.Value;
			}
			if (!RuntimeTerrainShader.Pending && !artworkCaptureBusy())
			{
				pinArt?.PumpIcons(permitted: true);
			}
			if (pinBuild != null)
			{
				if (!pinBuild.IsCompleted)
				{
					return;
				}
				Task<(string, Packet[])> task = pinBuild;
				pinBuild = null;
				if (task.IsFaulted)
				{
					Warn("map pin encoding", task.Exception);
					nextPinScan = Time.unscaledTime + 30f;
					return;
				}
				if (buildingPinEpoch == pinEpoch)
				{
					(string, Packet[]) result = task.Result;
					if (result.Item1 != pinSignature || Time.unscaledTime >= nextPinResync)
					{
						Packet[] item = result.Item2;
						foreach (Packet packet in item)
						{
							pendingPinPackets[packet.AckId] = packet;
						}
						pinSignature = result.Item1;
						nextPinResync = Time.unscaledTime + 120f;
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Sagas map pins snapshot queued: " + pinExportCount + " icons, " + result.Item2.Length + " paced parts; maximum scan slice " + pinScanMaximum.ToString("F2", CultureInfo.InvariantCulture) + " ms. Game locations require exploration; personal annotations require opt-in."));
					}
				}
			}
			if (scanningPins == null)
			{
				if (Time.unscaledTime < nextPinScan || pendingPinPackets.Count > 0)
				{
					return;
				}
				scanningPins = new List<MapPin>();
				pinScanMaximum = 0.0;
				pinCursor = 0;
				pinSourceCount = ((List<PinData>)pinsField.GetValue(Minimap.instance)).Count;
			}
			Minimap instance = Minimap.instance;
			List<PinData> list = (List<PinData>)pinsField.GetValue(instance);
			if (list.Count != pinSourceCount)
			{
				scanningPins = null;
				nextPinScan = Time.unscaledTime + 2f;
				return;
			}
			BitArray bitArray = (BitArray)pinBitsField.GetValue(instance);
			int num = (int)pinSizeField.GetValue(instance);
			float num2 = (float)pinPixelField.GetValue(instance);
			Stopwatch stopwatch = Stopwatch.StartNew();
			int num3 = 0;
			while (num3 < 16 && pinCursor < list.Count && scanningPins.Count < 2048)
			{
				PinData val = list[pinCursor];
				int num4 = MapPinPolicy.Classify((int)val.m_type, val.m_save, Object.op_Implicit((Object)(object)val.m_icon), val.m_ownerID != 0, val.m_shouldDelete);
				bool flag = num4 == 2;
				if (num4 != 0 && (!flag || pinConsent))
				{
					int num5 = Utils.RoundToInt(val.m_pos.x / num2 + (float)(num / 2));
					int num6 = Utils.RoundToInt(val.m_pos.z / num2 + (float)(num / 2));
					if (flag || (num5 >= 0 && num6 >= 0 && num5 < num && num6 < num && bitArray[num6 * num + num5]))
					{
						RuntimeArt.Image image = pinArt?.TryMapIcon(val.m_icon);
						string text = "";
						if (image != null)
						{
							text = image.Id;
							if (pinMediaSent.Count < 128 && pinMediaSent.Add(text))
							{
								Packet packet2 = new Packet
								{
									AckId = "pin-art:" + text,
									Media = new MediaUpload
									{
										World = World,
										PlayerId = owner,
										Id = text,
										Kind = "map-icon",
										Png = image.Png
									}
								};
								pendingPinPackets[packet2.AckId] = packet2;
							}
						}
						string text2 = Localize(val.m_name ?? "");
						if (text2 == "")
						{
							text2 = (Object.op_Implicit((Object)(object)val.m_icon) ? ((Object)val.m_icon).name : ((object)Unsafe.As<PinType, PinType>(ref val.m_type)/*cast due to .constrained prefix*/).ToString());
						}
						text2 = new string(text2.Where((char c) => !char.IsControl(c)).Take(100).ToArray());
						scanningPins.Add(new MapPin
						{
							Name = text2,
							Type = ((object)Unsafe.As<PinType, PinType>(ref val.m_type)/*cast due to .constrained prefix*/).ToString(),
							IconId = text,
							X = val.m_pos.x,
							Z = val.m_pos.z,
							Personal = flag,
							Checked = val.m_checked
						});
					}
				}
				num3++;
				pinCursor++;
			}
			pinScanMaximum = Math.Max(pinScanMaximum, stopwatch.Elapsed.TotalMilliseconds);
			if (pinCursor < list.Count && scanningPins.Count < 2048)
			{
				return;
			}
			List<MapPin> captured = scanningPins;
			pinExportCount = captured.Count;
			scanningPins = null;
			nextPinScan = Time.unscaledTime + 15f;
			string world = World;
			buildingPinEpoch = pinEpoch;
			pinBuild = Task.Run(delegate
			{
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_007b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_008e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: 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_00c4: Expected O, but got Unknown
				string item2 = JsonConvert.SerializeObject((object)captured);
				int num7 = Math.Max(1, (captured.Count + 31) / 32);
				string text3 = Guid.NewGuid().ToString("N");
				Packet[] array = new Packet[num7];
				for (int j = 0; j < num7; j++)
				{
					Packet packet3 = new Packet
					{
						AckId = "pins:" + text3 + ":" + j,
						Pins = new MapPins
						{
							World = world,
							PlayerId = owner,
							Revision = text3,
							Index = j,
							Count = num7,
							Pins = captured.Skip(j * 32).Take(32).ToList()
						}
					};
					packet3.Wire = JsonConvert.SerializeObject((object)packet3);
					array[j] = packet3;
				}
				return (signature: item2, packets: array);
			});
		}

		private bool artworkCaptureBusy()
		{
			return artwork?.CaptureBusy ?? false;
		}

		internal static long PeerPlayerId(ZNetPeer peer)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (!peer.IsReady())
			{
				return 0L;
			}
			if (ZDOMan.instance != null && !((ZDOID)(ref peer.m_characterID)).IsNone())
			{
				ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
				if (zDO != null)
				{
					return PeerIdentityPolicy.Resolve(peer.IsReady(), zDO.GetPrefab() == StringExtensionMethods.GetStableHashCode("Player"), peer.m_uid, zDO.GetOwner(), zDO.GetLong(ZDOVars.s_playerID, 0L), peer.m_playerID);
				}
			}
			return 0L;
		}

		private int QueueResult(int gate, int total, string detail)
		{
			if (gate >= 8192)
			{
				if (pressureSince < 0f)
				{
					pressureSince = Time.unscaledTime;
				}
				if (Time.unscaledTime >= pressureLog)
				{
					pressureLog = Time.unscaledTime + 60f;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Sagas uploads paused: outstanding=" + total + " bytes; " + detail + ". Gameplay headroom reserved; pending uploads retained."));
				}
			}
			else if (pressureSince >= 0f)
			{
				if (Time.unscaledTime - pressureSince >= 3f)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Sagas uploads resumed after " + (Time.unscaledTime - pressureSince).ToString("F1") + " seconds; outstanding=" + total + " bytes."));
				}
				pressureSince = -1f;
			}
			return gate;
		}

		private int UploadQueue(ZNetPeer peer)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Invalid comparison between Unknown and I4
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			int sendQueueSize = peer.m_socket.GetSendQueueSize();
			try
			{
				ISocket socket = peer.m_socket;
				ZSteamSocket val = (ZSteamSocket)(object)((socket is ZSteamSocket) ? socket : null);
				if (val != null && steamConnection?.GetValue(val) is HSteamNetConnection val2)
				{
					SteamNetConnectionRealTimeStatus_t val3 = default(SteamNetConnectionRealTimeStatus_t);
					SteamNetConnectionRealTimeLaneStatus_t val4 = default(SteamNetConnectionRealTimeLaneStatus_t);
					if ((int)SteamNetworkingSockets.GetConnectionRealTimeStatus(val2, ref val3, 0, ref val4) == 1)
					{
						return QueueResult((!TelemetryBudget.SteamHasHeadroom(sendQueueSize, val3.m_cbSentUnackedReliable, (long)val3.m_usecQueueTime)) ? Math.Max(8192, sendQueueSize) : 0, sendQueueSize, "Steam pending=" + Math.Max(0, sendQueueSize - val3.m_cbSentUnackedReliable) + ", unacknowledged=" + val3.m_cbSentUnackedReliable + ", queue delay us=" + (long)val3.m_usecQueueTime);
					}
				}
			}
			catch
			{
			}
			return QueueResult(sendQueueSize, sendQueueSize, ((object)peer.m_socket).GetType().Name + " conservative queue measurement");
		}

		private void BeginWire(ZNetPeer peer, Packet packet, string json, int lane)
		{
			outgoingWires[lane] = new PendingWire
			{
				Packet = packet,
				Preparation = Task.Run(() => WireCompression.Encode(json)),
				Lane = lane
			};
		}

		private void PumpWire()
		{
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Expected O, but got Unknown
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null || !serverPeer.IsReady())
			{
				return;
			}
			for (int i = 0; i < 4; i++)
			{
				int num = (wireTurn + i) % 4;
				if (!outgoingWires.TryGetValue(num, out PendingWire value) || !value.Preparation.IsCompleted)
				{
					continue;
				}
				if (value.Preparation.IsFaulted || value.Preparation.IsCanceled)
				{
					outgoingWires.Remove(num);
					Warn("upload compression", new InvalidOperationException("Upload preparation failed; retained events will retry."));
					continue;
				}
				WirePayload result = value.Preparation.Result;
				byte[] bytes = result.Bytes;
				Packet packet = value.Packet;
				if (((packet.Media != null || packet.MediaChunk != null) && !shareProfile.Value) || ((packet.Exploration != null || packet.Pins != null) && !shareMap.Value) || (packet.Pins != null && !sharePins.Value) || (packet.Player != null && (packet.Player.ShareProfile != shareProfile.Value || packet.Player.ShareMap != shareMap.Value || packet.Player.SharePins != sharePins.Value)))
				{
					outgoingWires.Remove(num);
					continue;
				}
				int num2 = Math.Min(2400, bytes.Length - value.Index * 2400);
				if (!telemetryBudget.Ready(num, 4096, Time.unscaledTime, UploadQueue(serverPeer)))
				{
					continue;
				}
				byte[] destinationArray = new byte[num2];
				Array.Copy(bytes, value.Index * 2400, destinationArray, 0, num2);
				string text = JsonConvert.SerializeObject((object)new WirePart
				{
					Id = value.Id,
					Compressed = result.Compressed,
					Lane = num,
					Index = value.Index,
					Count = (bytes.Length + 2400 - 1) / 2400,
					Data = destinationArray
				});
				if (telemetryBudget.TrySpend(num, Encoding.UTF8.GetByteCount(text) + 128, Time.unscaledTime, UploadQueue(serverPeer)))
				{
					serverPeer.m_rpc.Invoke("Sagas.Wire.V4", new object[1] { text });
					value.Index++;
					wireTurn = (num + 1) % 4;
					if (value.Index * 2400 >= bytes.Length)
					{
						outgoingWires.Remove(num);
					}
					break;
				}
			}
		}

		private void ReceiveFragment(ZNetPeer peer, ZRpc rpc, string json)
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || peer.m_rpc != rpc || !peer.IsReady() || json == null || json.Length > 4096)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (!fragmentRates.TryGetValue(rpc, out (float, int) value) || unscaledTime - value.Item1 > 10f)
			{
				value = (unscaledTime, 0);
			}
			value.Item2++;
			if (fragmentRates.Count >= 256 && !fragmentRates.ContainsKey(rpc))
			{
				fragmentRates.Clear();
			}
			fragmentRates[rpc] = value;
			if (value.Item2 > 80)
			{
				return;
			}
			try
			{
				WirePart val = JsonConvert.DeserializeObject<WirePart>(json, new JsonSerializerSettings
				{
					MaxDepth = 4,
					TypeNameHandling = (TypeNameHandling)0
				});
				if (val != null)
				{
					byte[] array = wireReceiver.Accept(peer.m_uid.ToString(), val, (double)Time.unscaledTime);
					if (array != null)
					{
						Receive(peer, rpc, Encoding.UTF8.GetString(array));
					}
				}
			}
			catch
			{
				Warn("fragment validation", new InvalidOperationException("Invalid telemetry fragment rejected."));
			}
		}

		private void GuardLogin(Action action)
		{
			try
			{
				action();
			}
			catch
			{
				if (Time.unscaledTime >= nextLoginFailure)
				{
					nextLoginFailure = Time.unscaledTime + 60f;
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Sagas personal login input or clipboard is unavailable; telemetry continues.");
				}
			}
		}

		private void ClearLoginClipboard()
		{
			if (clipboardCredential != "" && GUIUtility.systemCopyBuffer == clipboardCredential)
			{
				GUIUtility.systemCopyBuffer = "";
			}
			clipboardCredential = "";
		}

		private void SetupLogin()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			loginShortcut = BindSetting<KeyboardShortcut>("Website Login", "CopyLoginToken", new KeyboardShortcut((KeyCode)277, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "While playing, generate a personal website login and copy it to your clipboard. Keeps your other browser logins active. Never printed or saved locally.");
			revokeShortcut = BindSetting<KeyboardShortcut>("Website Login", "RevokeLoginToken", new KeyboardShortcut((KeyCode)279, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "While playing, revoke all your personal website logins for this character/world.");
		}

		private void LoginNotice(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		private void UpdateLogin()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			if (clipboardCredential != "" && Time.unscaledTime >= clipboardUntil)
			{
				if (GUIUtility.systemCopyBuffer == clipboardCredential)
				{
					GUIUtility.systemCopyBuffer = "";
				}
				clipboardCredential = "";
			}
			if (loginNonce != "" && Time.unscaledTime >= loginUntil)
			{
				loginNonce = "";
				LoginNotice("Sagas login request timed out. Try again shortly.");
			}
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)ZNet.instance) || Player.m_localPlayer.GetPlayerID() == 0L)
			{
				return;
			}
			KeyboardShortcut value = revokeShortcut.Value;
			bool flag = ((KeyboardShortcut)(ref value)).IsDown();
			if (!flag)
			{
				value = loginShortcut.Value;
				if (!((KeyboardShortcut)(ref value)).IsDown())
				{
					return;
				}
			}
			if (Time.unscaledTime < nextLoginRequest)
			{
				return;
			}
			nextLoginRequest = Time.unscaledTime + 5f;
			loginWorld = World;
			loginPlayer = Identity(Player.m_localPlayer.GetPlayerID());
			loginNonce = Guid.NewGuid().ToString("N");
			loginUntil = Time.unscaledTime + 15f;
			if (ZNet.instance.IsServer())
			{
				if (service != null)
				{
					service.TouchPresence(World, loginPlayer, Player.m_localPlayer.GetPlayerName(), (bool?)null);
					service.Flush(10000);
					try
					{
						if (flag)
						{
							service.Store.RevokePlayerLogin(World, loginPlayer);
						}
						AcceptLogin(loginNonce, flag ? "" : service.Store.IssuePlayerLogin(World, loginPlayer));
						return;
					}
					catch
					{
						loginNonce = "";
						LoginNotice("Sagas could not update your login. Try again shortly.");
						return;
					}
				}
				LoginNotice("Sagas is still starting. Try again shortly.");
				loginNonce = "";
			}
			else
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer == null || !serverPeer.IsReady())
				{
					loginNonce = "";
					return;
				}
				serverPeer.m_rpc.Invoke("Sagas.Login.Request.V1", new object[1] { loginNonce + (flag ? ":revoke" : ":issue") });
				LoginNotice("Requesting personal Sagas login...");
			}
		}

		private void RegisterLogin(ZNetPeer peer)
		{
			RegisterWebsiteOverlay(peer);
			peer.m_rpc.Register<string>("Sagas.Login.Request.V1", (Action<ZRpc, string>)delegate(ZRpc rpc, string request)
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer() && peer.m_rpc == rpc && peer.IsReady() && request != null && request.Length <= 40)
				{
					string[] array = request.Split(':');
					if (array.Length == 2 && Guid.TryParseExact(array[0], "N", out var _) && (!(array[1] != "issue") || !(array[1] != "revoke")) && (!loginRates.TryGetValue(rpc, out var value) || !(Time.unscaledTime < value)))
					{
						loginRates[rpc] = Time.unscaledTime + 5f;
						if (service == null)
						{
							rpc.Invoke("Sagas.Login.Response.V1", new object[1] { array[0] + ":!service" });
						}
						else
						{
							long playerId = PeerPlayerId(peer);
							if (playerId == 0L)
							{
								rpc.Invoke("Sagas.Login.Response.V1", new object[1] { array[0] + ":!identity" });
								((BaseUnityPlugin)this).Logger.LogWarning((object)"Sagas login delayed: owned player identity is not available yet.");
							}
							else if (!loginBusy.Add(rpc))
							{
								rpc.Invoke("Sagas.Login.Response.V1", new object[1] { array[0] + ":!busy" });
							}
							else
							{
								string id = Identity(playerId);
								service.TouchPresence(World, id, peer.m_playerName, (bool?)null);
								SagaService loginService = service;
								string nonce = array[0];
								bool revoke = array[1] == "revoke";
								string worldForLogin = World;
								Task.Run(delegate
								{
									string response;
									try
									{
										if (!loginService.Flush(10000))
										{
											throw new InvalidOperationException();
										}
										if (revoke)
										{
											loginService.Store.RevokePlayerLogin(worldForLogin, id);
										}
										response = (revoke ? "" : loginService.Store.IssuePlayerLogin(worldForLogin, id));
									}
									catch
									{
										response = "!storage";
									}
									committed.Enqueue(delegate
									{
										loginBusy.Remove(rpc);
										if (Object.op_Implicit((Object)(object)ZNet.instance) && World == worldForLogin && peer.IsReady() && PeerPlayerId(peer) == playerId)
										{
											rpc.Invoke("Sagas.Login.Response.V1", new object[1] { nonce + ":" + response });
											if (response == "!storage")
											{
												((BaseUnityPlugin)this).Logger.LogWarning((object)"Sagas login failed: credential storage unavailable.");
											}
										}
									});
								});
							}
						}
					}
				}
			});
			peer.m_rpc.Register<string>("Sagas.Login.Response.V1", (Action<ZRpc, string>)delegate(ZRpc rpc, string response)
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsServer() && ZNet.instance.GetServerPeer()?.m_rpc == rpc && response != null && response.Length <= 110)
				{
					int num = response.IndexOf(':');
					if (num == 32)
					{
						AcceptLogin(response.Substring(0, num), response.Substring(num + 1));
					}
				}
			});
		}

		private void AcceptLogin(string nonce, string credential)
		{
			if (nonce != loginNonce || Time.unscaledTime > loginUntil || World != loginWorld || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || Identity(Player.m_localPlayer.GetPlayerID()) != loginPlayer)
			{
				return;
			}
			loginNonce = "";
			if (credential.StartsWith("!", StringComparison.Ordinal))
			{
				LoginNotice(credential switch
				{
					"!busy" => "Your previous Sagas login request is still processing. Try again shortly.", 
					"!service" => "The server Sagas service is not ready.", 
					"!identity" => "Sagas is waiting for your character identity. Try again shortly.", 
					_ => "The server could not save your Sagas login. Ask the host to check the server log.", 
				});
			}
			else if (credential == "")
			{
				if (clipboardCredential != "" && GUIUtility.systemCopyBuffer == clipboardCredential)
				{
					GUIUtility.systemCopyBuffer = "";
				}
				clipboardCredential = "";
				LoginNotice("All personal Sagas logins revoked.");
			}
			else if (credential.Length == 70 && credential.StartsWith("sagas_", StringComparison.Ordinal))
			{
				GUIUtility.systemCopyBuffer = credential;
				clipboardCredential = credential;
				clipboardUntil = Time.unscaledTime + 120f;
				LoginNotice("Sagas login copied. Paste into website Login within 2 minutes. Other browser logins remain active.");
			}
		}

		internal static string Identity(long id)
		{
			if (id == 0L)
			{
				return "";
			}
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(World + ":" + id))).Replace("-", "").ToLowerInvariant()
				.Substring(0, 24);
		}

		internal unsafe static string EventId(string kind, ZDOID zdo)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			using SHA256 sHA = SHA256.Create();
			Encoding uTF = Encoding.UTF8;
			string world = World;
			ZDOID val = zdo;
			return kind + ":" + BitConverter.ToString(sHA.ComputeHash(uTF.GetBytes(world + ":" + ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString()))).Replace("-", "").ToLowerInvariant();
		}

		internal static string Localize(string value)
		{
			if (Localization.instance != null)
			{
				return Localization.instance.Localize(value);
			}
			return value;
		}

		private void Awake()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Expected O, but got Unknown
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Expected O, but got Unknown
			Instance = this;
			SetupMapDetails();
			SetupLogin();
			SetupWebsiteOverlay();
			SetupVersionEnforcement();
			requireToken = BindSetting("Server", "RequireViewerToken", value: false, "True: private viewing requires ViewerToken. False: anyone who can reach the website may view shared data without a token. Does not change network binding or player sharing preferences.");
			artwork = new RuntimeArt(Warn);
			serverName = BindSetting("Server", "DisplayName", "", "Website server name override; blank uses the Valheim server name, then the world name.");
			serverAddress = BindSetting("Server", "AdvertisedAddress", "", "Optional game-server IP/hostname and port displayed on the website. If WebsiteUrl is blank, a valid host here also supplies the website host (using the web listener port). No external public-IP lookup.");
			host = BindSetting("Server", "EnableWebsite", value: true, "Start the website only when hosting a world. RequireViewerToken controls public versus private viewing.");
			data = BindSetting("Server", "DataDirectory", Path.Combine(Paths.ConfigPath, "ValheimSagas"), "Persistent database path; back up separately from world saves.");
			prefix = BindSetting("Server", "ListenPrefix", "", new ConfigDescription("Advanced listener override. Leave blank to use WebsitePort: all interfaces on dedicated servers, loopback for local hosting. An existing saved prefix is preserved and overrides WebsitePort. Example: http://*:19908/ (trailing slash required). This is a bind address, not a browser URL.", (AcceptableValueBase)null, new object[1] { "Advanced" }));
			token = BindSetting("Server", "ViewerToken", Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N"), "Shared read-only viewing credential used when RequireViewerToken is true. Personal player logins are separate. Never synced to game clients.");
			lore = BindSetting("Lore", "EnableOpenRouter", value: true, "Send selected narrative facts to OpenRouter. Free routing by default; paid models require AllowPaidModels. No coordinates or account IDs.");
			key = BindSetting("Lore", "OpenRouterKey", "", "Server only. Prefer OPENROUTER_API_KEY environment variable.");
			model = BindSetting("Lore", "Model", "openrouter/free", "OpenRouter model ID or @preset/name from the key owner account. Defaults to openrouter/free. Paid routes require AllowPaidModels. Manage pricing/provider limits in your OpenRouter preset or account.");
			allowPaidLore = BindSetting("Lore", "AllowPaidModels", value: false, "Allow the host key to pay for personal and server sagas using Model. False enforces zero token prices, including presets. Restart host after changing.");
			daily = BindSetting("Lore", "DailyBudget", 20, "Maximum OpenRouter requests per UTC day, including retries; this is a request count, not a dollar budget.");
			cooldown = BindSetting("Lore", "CooldownMinutes", 180, "Minimum chapter interval per character.");
			milestones = BindSetting("Lore", "MilestoneEvents", 20, "Ordinary event count before a chapter; notable events may qualify earlier.");
			retention = BindSetting("Server", "RetentionDays", 0, "Zero retains all detailed history. Positive values prune old events; dedup ledger remains.");
			statisticsRetention = BindSetting("Server", "StatisticsRetentionDays", 0, "Zero keeps exact statistical facts forever. Positive values must be at least RetentionDays; older facts are deleted, dedup lineage remains.");
			shareProfile = BindSetting("Privacy", "ShareProfile", value: true, "Share statistics, gear and saga with website viewers.");
			shareMap = BindSetting("Privacy", "ShareMap", value: true, "Share personal exploration with website viewers.");
			sharePosition = BindSetting("Privacy", "SharePosition", value: true, new ConfigDescription("Website permission, normally left on. Use 'Visible to other players' on Valheim's map as your everyday position-sharing control. Turn this permission off only to hide your position from the website while still sharing it in-game. Existing choices are preserved.", (AcceptableValueBase)null, new object[2]
			{
				"Advanced",
				new DisplayNameAttribute("Allow website position sharing")
			}));
			harmony = new Harmony("org.valheimsagas.collector");
			harmony.PatchAll(typeof(SagasPlugin).Assembly);
			SetupConfigurationManager();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Valheim Sagas loaded; telemetry hooks installed. No external map dependency.");
		}

		private void StopService()
		{
			ResetMapDetails();
			slsCapture?.Dispose();
			slsCapture = null;
			string[] ids = (from x in online.Values.Concat(new string[1] { lastLocalId })
				where x != ""
				select x).Distinct().ToArray();
			string world = activeWorld;
			service = null;
			lastLocalId = "";
			serviceStartup.Retire(delegate(StartedService started)
			{
				string[] array = ids;
				foreach (string text in array)
				{
					started.Service.SetOffline(world, text);
				}
			});
		}

		private void OnDestroy()
		{
			artwork?.Clear();
			GuardLogin(ClearLoginClipboard);
			RuntimeTerrain.Clear();
			outbox?.Finish(pending.Values.ToArray());
			StopService();
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Instance = null;
		}

		private void Update()
		{
			GuardLogin(UpdateLogin);
			GuardLogin(UpdateWebsiteOverlay);
			artwork?.PumpPortrait(Player.m_localPlayer, shareProfile.Value, World);
			RunStage("terrain preparation", RuntimeTerrainShader.Pump);
			if (!RuntimeTerrainShader.Pending)
			{
				RunStage("item icon capture", delegate
				{
					artwork?.PumpIcons(shareProfile.Value);
				});
			}
			Action result;
			while (committed.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch
				{
				}
			}
			if (Time.unscaledTime >= nextTick)
			{
				nextTick = Time.unscaledTime + 3f;
				RunStage("world update", Tick);
			}
			RunStage("SLS capture slice", RefreshSls);
			RunStage("map pin capture slice", PumpMapDetails);
			if (Time.unscaledTime >= nextMapImport && Object.op_Implicit((Object)(object)Player.m_localPlayer) && World != "" && World == activeWorld)
			{
				nextMapImport = Time.unscaledTime + 0.25f;
				RunStage("exploration scan", delegate
				{
					SyncMap(Player.m_localPlayer);
				});
			}
			if (Time.unscaledTime >= nextUpload)
			{
				nextUpload = Time.unscaledTime + 0.1f;
				RunStage("paced uploads", PumpUploads);
			}
		}

		private void Warn(string stage, Exception e)
		{
			if (!nextWarning.TryGetValue(stage, out var value) || !(Time.unscaledTime < value))
			{
				nextWarning[stage] = Time.unscaledTime + 60f;
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Sagas " + stage + " failed (repeated errors suppressed for 60 seconds): " + e));
			}
		}

		private void RunStage(string stage, Action action)
		{
			long timestamp = Stopwatch.GetTimestamp();
			try
			{
				action();
			}
			catch (Exception e)
			{
				Warn(stage, e);
			}
			finally
			{
				double num = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency;
				if (num >= 50.0 && (!nextWarning.TryGetValue("slow " + stage, out var value) || Time.unscaledTime >= value))
				{
					nextWarning["slow " + stage] = Time.unscaledTime + 60f;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Sagas slow stage: " + stage + " took " + num.ToString("F1", CultureInfo.InvariantCulture) + " ms (timing only; repeated warnings limited to once per minute)."));
				}
			}
		}

		private string WebsiteServerName()
		{
			if (!string.IsNullOrWhiteSpace(serverName.Value))
			{
				return serverName.Value.Trim();
			}
			try
			{
				string text = AccessTools.Field(typeof(ZNet), "m_ServerName")?.GetValue(null) as string;
				if (!string.IsNullOrWhiteSpace(text))
				{
					return text.Trim();
				}
			}
			catch (Exception e)
			{
				Warn("server display name", e);
			}
			return ZNet.instance.GetWorldName() ?? "Valheim Sagas";
		}

		private void Tick()
		{
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			//IL_041c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_0437: Unknown result type (might be due to invalid IL or missing references)
			//IL_0443: Unknown result type (might be due to invalid IL or missing references)
			//IL_0454: Unknown result type (might be due to invalid IL or missing references)
			//IL_0464: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_0486: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_049e: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_052c: Unknown result type (might be due to invalid IL or missing references)
			//IL_053e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0562: Expected O, but got Unknown
			if (activeWorld != World || !Object.op_Implicit((Object)(object)ZNet.instance) || World == "")
			{
				telemetryBudget.Reset(Time.unscaledTime);
				retryAfter.Clear();
				outboundProfile = null;
			}
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || World == "")
			{
				RuntimeTerrain.Clear();
				outbox?.Finish(pending.Values.ToArray());
				outbox = null;
				StopService();
				activeWorld = "";
				registered.Clear();
				online.Clear();
				pending.Clear();
				outgoingWires.Clear();
				wireReceiver.Clear();
				fragmentRates.Clear();
				mediaTransfer.Clear();
				mediaCursors.Clear();
				return;
			}
			using StartupTrace startupTrace = new StartupTrace(activeWorld != World || startupTraceTicks < 2, "world", delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			if (activeWorld != World)
			{
				startupTraceTicks = 0;
				profileTraceRecorded = false;
				outbox?.Finish(pending.Values.ToArray());
				StopService();
				service = null;
				activeWorld = World;
				nextSlsRefresh = 0f;
				mediaTransfer.Clear();
				mediaCursors.Clear();
				knownCharacters.Clear();
				registered.Clear();
				online.Clear();
				pending.Clear();
				outgoingWires.Clear();
				wireReceiver.Clear();
				fragmentRates.Clear();
				sentCells.Clear();
				tileVersions.Clear();
				fullyMapped.Clear();
				RuntimeTerrain.Clear();
				pendingMedia.Clear();
				sentMedia.Clear();
				artwork?.Clear();
				mapCursor = 0;
				nearMapCursor = 0;
				historyMapTurn = false;
				importing = true;
				pendingMaps.Clear();
				journalId = "";
				outbox = null;
			}
			startupTraceTicks++;
			startupTrace.Mark("world reset");
			string[] characters;
			if (ZNet.instance.IsServer() && service == null && Time.unscaledTime >= nextServiceStart)
			{
				try
				{
					if (serviceStartup.Poll())
					{
						StartedService current = serviceStartup.Current;
						service = current.Service;
						characters = current.Characters;
						foreach (string item in characters)
						{
							knownCharacters.Add(item);
						}
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Sagas background service startup completed in " + current.Milliseconds.ToString("F1", CultureInfo.InvariantCulture) + " ms (worker time)."));
						LogWebsiteSetup();
					}
					else if (!serviceStartup.Busy)
					{
						SagaOptions options = new SagaOptions
						{
							ServerVersion = ((BaseUnityPlugin)this).Info.Metadata.Version.ToString(),
							DataDirectory = data.Value,
							WebDirectory = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "web"),
							ListenPrefix = (host.Value ? ListenAddress() : ""),
							ViewerToken = token.Value,
							RequireViewerToken = requireToken.Value,
							World = World,
							WorldName = ZNet.instance.GetWorldName(),
							ServerName = WebsiteServerName(),
							ServerAddress = serverAddress.Value,
							SlsInstalled = SlsAdapter.Installed,
							LoreEnabled = lore.Value,
							LoreModel = model.Value,
							LoreAllowPaid = allowPaidLore.Value,
							LoreUseAccountPricing = true,
							LoreDailyBudget = Math.Max(0, daily.Value),
							LoreCooldownMinutes = Math.Max(1, cooldown.Value),
							LoreMilestoneEvents = Math.Max(1, milestones.Value),
							RetentionDays = Math.Max(0, retention.Value),
							StatisticsRetentionDays = ((statisticsRetention.Value > 0) ? Math.Max(retention.Value, statisticsRetention.Value) : 0),
							Log = delegate(string message)
							{
								((BaseUnityPlugin)this).Logger.LogWarning((object)message);
							},
							OpenRouterKey = (Environment.GetEnvironmentVariable("OPENROUTER_API_KEY") ?? key.Value)
						};
						serviceStartup.Begin(delegate
						{
							//IL_000d: Unknown result type (might be due to invalid IL or missing references)
							//IL_0013: Expected O, but got Unknown
							Stopwatch stopwatch = Stopwatch.StartNew();
							SagaService val = new SagaService(options, (HttpClient)null);
							try
							{
								val.Start();
								return new StartedService
								{
									Service = val,
									Characters = (from p in val.Store.Players(options.World)
										select p.PlayerId).ToArray(),
									Milliseconds = stopwatch.Elapsed.TotalMilliseconds
								};
							}
							catch
							{
								val.Dispose();
								throw;
							}
						});
					}
				}
				catch (Exception e)
				{
					nextServiceStart = Time.unscaledTime + 60f;
					Warn("background service startup", e);
				}
			}
			startupTrace.Mark("service scheduling/adoption");
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!peer.IsReady() || !registered.Add(peer.m_rpc))
				{
					continue;
				}
				ZNetPeer captured = peer;
				RegisterLogin(peer);
				peer.m_rpc.Register<string>("Sagas.Wire.V4", (Action<ZRpc, string>)delegate(ZRpc rpc, string json)
				{
					ReceiveFragment(captured, rpc, json);
				});
				peer.m_rpc.Register<string>("Sagas.V1", (Action<ZRpc, string>)delegate(ZRpc rpc, string json)
				{
					Receive(captured, rpc, json);
				});
				peer.m_rpc.Register<string>("Sagas.Ack.V1", (Action<ZRpc, string>)delegate(ZRpc rpc, string id)
				{
					if (!ZNet.instance.IsServer() && ZNet.instance.GetServerPeer()?.m_rpc == rpc)
					{
						pending.Remove(id);
						pendingMaps.Remove(id);
						pendingMedia.Remove(id);
						pendingPinPackets.Remove(id);
					}
				});
			}
			startupTrace.Mark("peer registration");
			if (service != null)
			{
				HashSet<long> current3 = new HashSet<long>();
				foreach (ZNetPeer item2 in from p in ZNet.instance.GetPeers()
					where p.IsReady() && PeerPlayerId(p) != 0
					select p)
				{
					current3.Add(item2.m_uid);
					string text = Identity(PeerPlayerId(item2));
					knownCharacters.Add(text);
					service.TouchPresence(World, text, item2.m_playerName, (bool?)item2.m_publicRefPos);
					online[item2.m_uid] = text;
				}
				long[] array = online.Keys.Where((long id) => !current3.Contains(id)).ToArray();
				foreach (long num2 in array)
				{
					service.SetOffline(World, online[num2]);
					online.Remove(num2);
				}
			}
			startupTrace.Mark("presence");
			if (service != null && Object.op_Implicit((Object)(object)EnvMan.instance))
			{
				service.UpdateClock(WorldClock.Sample(World, EnvMan.instance.GetDay(), EnvMan.instance.GetDayFraction(), ZNet.instance.GetTimeSeconds(), (double)EnvMan.instance.m_dayLengthSec, (double)Time.timeScale, ZNet.instance.GetNrOfPlayers() > 0, EnvMan.instance.IsTimeSkipping()));
			}
			Player player = Player.m_localPlayer;
			string text2 = ((Object.op_Implicit((Object)(object)player) && player.GetPlayerID() != 0L) ? ((ZNet.instance.IsServer() ? "host-" : "") + Identity(player.GetPlayerID())) : (ZNet.instance.IsServer() ? "server" : ""));
			if (text2 != "" && journalId != text2)
			{
				outbox?.Finish(pending.Values.ToArray());
				if (journalId != "")
				{
					pending.Clear();
				}
				ResetMapDetails();
				sentCells.Clear();
				tileVersions.Clear();
				fullyMapped.Clear();
				RuntimeTerrain.Clear();
				pendingMedia.Clear();
				sentMedia.Clear();
				artwork?.Clear();
				pendingMaps.Clear();
				mapCursor = 0;
				nearMapCursor = 0;
				historyMapTurn = false;
				importing = true;
				journalId = text2;
				RunStage("outbox startup", delegate
				{
					outbox = new Outbox(data.Value, World + "-" + journalId, delegate(string message)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)message);
					});
					foreach (SagaEvent item3 in (from val in outbox.Load()
						where val.World == World && SagaService.ValidEvent(val)
						select val).Take(4096))
					{
						pending[item3.Id] = item3;
					}
				});
			}
			startupTrace.Mark("outbox initialization");
			if (Object.op_Implicit((Object)(object)player) && player.GetPlayerID() != 0L)
			{
				lastLocalId = Identity(player.GetPlayerID());
				knownCharacters.Add(lastLocalId);
				bool flag = ZNet.instance.IsReferencePositionPublic();
				string text3 = lastLocalId + ":" + sharePosition.Value + ":" + flag;
				if (positionLogKey != text3)
				{
					positionLogKey = text3;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Website position sharing: " + ((!sharePosition.Value) ? "hidden by Sagas 'Allow website position sharing' permission." : ((!flag) ? "hidden; enable 'Visible to other players' on Valheim's map to show your marker." : "enabled by Valheim's map visibility setting.")) + " Coordinates are not written to this log."));
				}
				RunStage("local presence", delegate
				{
					SagaService? obj = service;
					if (obj != null)
					{
						obj.TouchPresence(World, lastLocalId, player.GetPlayerName(), (bool?)ZNet.instance.IsReferencePositionPublic());
					}
				});
				RunStage("equipment snapshot", delegate
				{
					outboundProfile = new Packet
					{
						Player = Snapshot(player)
					};
				});
			}
			startupTrace.Mark("player snapshot and exploration");
			characters = (from x in retryAfter
				where x.Value < Time.unscaledTime - 60f
				select x.Key).ToArray();
			foreach (string text4 in characters)
			{
				retryAfter.Remove(text4);
			}
			RunStage("outbox save", delegate
			{
				outbox?.Save(pending.Values.ToArray());
			});
			startupTrace.Mark("outbox save");
		}

		private bool Send(Packet packet)
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || World != activeWorld || World == "")
			{
				return false;
			}
			if (packet.Player != null && (packet.Player.ShareProfile != shareProfile.Value || packet.Player.ShareMap != shareMap.Value || packet.Player.SharePins != sharePins.Value))
			{
				return false;
			}
			SagaEvent? obj = packet.Event;
			string text = ((obj != null) ? obj.Id : null) ?? packet.AckId;
			if (text != "" && retryAfter.TryGetValue(text, out var value) && Time.unscaledTime < value)
			{
				return false;
			}
			if (ZNet.instance.IsServer())
			{
				if (service == null)
				{
					return false;
				}
				Apply(packet, 0L, null);
				if (text != "")
				{
					retryAfter[text] = Time.unscaledTime + 30f;
				}
				return true;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null || !serverPeer.IsReady())
			{
				return false;
			}
			if (UploadQueue(serverPeer) >= 8192)
			{
				return false;
			}
			int lane = ((packet.Exploration != null || packet.Pins != null) ? 2 : ((packet.Media != null || packet.MediaChunk != null) ? 3 : ((packet.Player != null) ? 1 : 0)));
			string text2 = packet.Wire ?? (packet.Wire = JsonConvert.SerializeObject((object)packet));
			if (Encoding.UTF8.GetByteCount(text2) + 128 > 128000)
			{
				Warn("packet size", new InvalidOperationException("Sagas packet exceeded the bounded RPC size; upload not sent."));
				return false;
			}
			if (outgoingWires.ContainsKey(lane))
			{
				return false;
			}
			BeginWire(serverPeer, packet, text2, lane);
			if (text != "")
			{
				retryAfter[text] = Time.unscaledTime + 30f;
			}
			return true;
		}

		private void PumpUploads()
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || World != activeWorld || World == "")
			{
				return;
			}
			for (int i = 0; i < 4; i++)
			{
				switch ((uploadLane + i) % 4)
				{
				case 0:
					if (outboundProfile != null && Send(outboundProfile))
					{
						outboundProfile = null;
					}
					break;
				case 1:
				{
					SagaEvent[] array = pending.Values.ToArray();
					if (array.Length == 0)
					{
						break;
					}
					for (int j = 0; j < Math.Min(8, array.Length); j++)
					{
						if (Send(new Packet
						{
							Event = array[(retryCursor + j) % array.Length]
						}))
						{
							break;
						}
					}
					retryCursor = (retryCursor + 8) % array.Length;
					break;
				}
				case 2:
					if (shareMap.Value)
					{
						if (SendMapDetails())
						{
							break;
						}
						foreach (Packet value in pendingMaps.Values)
						{
							if (Send(value))
							{
								break;
							}
						}
					}
					else if (pendingMaps.Count > 0)
					{
						pendingMaps.Clear();
						tileVersions.Clear();
						fullyMapped.Clear();
						sentCells.Clear();
					}
					break;
				case 3:
					if (shareProfile.Value)
					{
						SendArtwork();
						break;
					}
					pendingMedia.Clear();
					mediaCursors.Clear();
					break;
				}
			}
			uploadLane = (uploadLane + 1) % 4;
			if (!ZNet.instance.IsServer())
			{
				PumpWire();
			}
		}

		private void SendArtwork()
		{
			mediaTransfer.Expire((double)Time.unscaledTime);
			string[] array = mediaCursors.Keys.Where((string x) => !pendingMedia.ContainsKey(x)).ToArray();
			foreach (string text in array)
			{
				mediaCursors.Remove(text);
			}
			int num2 = 4;
			Packet[] array2 = pendingMedia.Values.OrderBy(delegate(Packet x)
			{
				MediaUpload? media2 = x.Media;
				return (((media2 != null) ? media2.Kind : null) == "portrait") ? 1 : 0;
			}).ToArray();
			foreach (Packet packet in array2)
			{
				if (num2 <= 0)
				{
					break;
				}
				MediaUpload media = packet.Media;
				if (media == null)
				{
					continue;
				}
				if (ZNet.instance.IsServer() || !MediaTransfer.NeedsChunks(media))
				{
					if (Send(packet))
					{
						num2--;
					}
					continue;
				}
				int value;
				int num3 = (mediaCursors.TryGetValue(packet.AckId, out value) ? value : 0);
				int num4 = MediaTransfer.ChunkCount(media.Png.Length);
				int num5 = Math.Min(num2, num4);
				for (int num6 = 0; num6 < num5; num6++)
				{
					ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
					if (serverPeer == null)
					{
						break;
					}
					int num7 = Math.Min(65536, media.Png.Length - num3 * 65536);
					if (!telemetryBudget.Ready(3, (num7 + 2) / 3 * 4 + 1024, Time.unscaledTime, UploadQueue(serverPeer)) || (retryAfter.TryGetValue(packet.AckId, out var value2) && Time.unscaledTime < value2) || !Send(new Packet
					{
						Version = 2,
						MediaChunk = MediaTransfer.Slice(media, num3)
					}))
					{
						break;
					}
					num3 = (num3 + 1) % num4;
					num2--;
					if (num3 == 0)
					{
						retryAfter[packet.AckId] = Time.unscaledTime + 30f;
						break;
					}
				}
				mediaCursors[packet.AckId] = num3;
			}
		}

		private void Receive(ZNetPeer peer, ZRpc rpc, string json)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			if (service == null || !ZNet.instance.IsServer() || peer.m_rpc != rpc || !peer.IsReady() || PeerPlayerId(peer) == 0L || json.Length > 128000)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (!rates.TryGetValue(rpc, out (float, int) value) || unscaledTime - value.Item1 > 10f)
			{
				value = (unscaledTime, 0);
			}
			value.Item2++;
			rates[rpc] = value;
			if (value.Item2 > 160)
			{
				return;
			}
			try
			{
				Packet packet = JsonConvert.DeserializeObject<Packet>(json, new JsonSerializerSettings
				{
					MaxDepth = 12,
					TypeNameHandling = (TypeNameHandling)0
				});
				if (packet != null && ((packet.Version == 1 && packet.MediaChunk == null) || (packet.Version == 2 && packet.MediaChunk != null && packet.Media == null && packet.Event == null && packet.Player == null && packet.Exploration == null && packet.Pins == null)))
				{
					if (packet.Event != null && (packet.Event.Kind == "collect" || packet.Event.Kind == "pickup" || packet.Event.Kind == "death" || packet.Event.Kind == "bounty"))
					{
						packet.Event.PlayerName = peer.m_playerName;
					}
					if (packet.Player != null)
					{
						packet.Player.Name = peer.m_playerName;
						PlayerSnapshot? player = packet.Player;
						player.SharePosition &= peer.m_publicRefPos;
					}
					Apply(packet, PeerPlayerId(peer), rpc);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Rejected Sagas packet: " + ex.Message));
			}
		}

		private void Apply(Packet packet, long peerPlayer, ZRpc? rpc)
		{
			if (service == null || (packet.Pins != null && packet.Pins.World != World) || (packet.MediaChunk != null && packet.MediaChunk.World != World) || (packet.Media != null && packet.Media.World != World) || (packet.Player != null && packet.Player.World != World) || (packet.Exploration != null && packet.Exploration.World != World) || (packet.Event != null && packet.Event.World != World) || (packet.Event != null && (packet.Event.Kind == "join" || packet.Event.Kind == "leave")))
			{
				return;
			}
			string text = ((peerPlayer == 0L) ? Identity(Object.op_Implicit((Object)(object)Player.m_localPlayer) ? Player.m_localPlayer.GetPlayerID() : 0) : Identity(peerPlayer));
			if (packet.MediaChunk != null)
			{
				MediaUpload val = mediaTransfer.Accept(text, World, packet.MediaChunk, (double)Time.unscaledTime);
				if (val != null)
				{
					packet.Media = val;
					packet.AckId = "media:" + val.Id;
				}
			}
			if (packet.Media != null)
			{
				MediaUpload media = packet.Media;
				media.World = World;
				media.PlayerId = text;
				service.UploadMedia(media, (Action<bool>)delegate(bool ok)
				{
					if (ok)
					{
						committed.Enqueue(delegate
						{
							if (rpc != null)
							{
								rpc.Invoke("Sagas.Ack.V1", new object[1] { packet.AckId });
							}
							else
							{
								pendingMedia.Remove(packet.AckId);
								pendingPinPackets.Remove(packet.AckId);
							}
						});
					}
				});
			}
			if (packet.Player != null)
			{
				PlayerSnapshot player = packet.Player;
				player.World = World;
				player.PlayerId = text;
				player.Utc = DateTime.UtcNow;
				player.Online = true;
				player.NemesisScore = HostNemesisScore((peerPlayer == 0L && Object.op_Implicit((Object)(object)Player.m_localPlayer)) ? Player.m_localPlayer.GetPlayerID() : peerPlayer);
				if (!service.UpdatePlayer(player))
				{
					Warn("equipment validation", new InvalidOperationException("Snapshot rejected or storage queue full; presence and exploration continue."));
				}
			}
			if (packet.Pins != null)
			{
				packet.Pins.World = World;
				packet.Pins.PlayerId = text;
				service.UpdatePins(packet.Pins, (Action<bool>)delegate(bool ok)
				{
					if (ok)
					{
						committed.Enqueue(delegate
						{
							if (rpc != null)
							{
								rpc.Invoke("Sagas.Ack.V1", new object[1] { packet.AckId });
							}
							else
							{
								pendingPinPackets.Remove(packet.AckId);
							}
						});
					}
				});
			}
			if (packet.Exploration != null)
			{
				ExplorationBatch exploration = packet.Exploration;
				exploration.World = World;
				exploration.PlayerId = text;
				if (exploration.CellSize != 64 || exploration.Cells.Count > 128)
				{
					return;
				}
				service.Explore(exploration, (Action<bool>)delegate(bool ok)
				{
					if (ok)
					{
						committed.Enqueue(delegate
						{
							if (rpc != null)
							{
								rpc.Invoke("Sagas.Ack.V1", new object[1] { packet.AckId });
							}
							else
							{
								pendingMaps.Remove(packet.AckId);
							}
						});
					}
				});
			}
			if (packet.Event == null)
			{
				return;
			}
			SagaEvent e = packet.Event;
			e.World = World;
			SagaEvent obj = e;
			obj.NemesisBoss &= SlsAdapter.Installed && e.Kind == "kill";
			if (e.Kind == "collect" || e.Kind == "pickup" || e.Kind == "death" || e.Kind == "bounty")
			{
				if (e.PlayerId != text)
				{
					if (rpc != null)
					{
						rpc.Invoke("Sagas.Ack.V1", new object[1] { e.Id });
					}
					else
					{
						pending.Remove(e.Id);
					}
					return;
				}
				e.PlayerId = text;
			}
			if (e.Kind == "kill" || e.Kind == "drop")
			{
				foreach (ZNetPeer peer in ZNet.instance.GetPeers())
				{
					if (peer.IsReady() && PeerPlayerId(peer) != 0L)
					{
						knownCharacters.Add(Identity(PeerPlayerId(peer)));
					}
				}
				if (!knownCharacters.Contains(e.PlayerId))
				{
					e.PlayerId = "";
					e.PlayerName = "Unattributed";
				}
				e.Contributors = e.Contributors.Where(knownCharacters.Contains).ToList();
			}
			if (e.Id.Length > 160 || e.Amount < 1 || e.Amount > 10000 || e.Stars < 0 || e.Stars > 1000)
			{
				return;
			}
			service.TryEvent(e, (Action<bool>)delegate(bool ok)
			{
				if (ok)
				{
					committed.Enqueue(delegate
					{
						if (rpc != null)
						{
							rpc.Invoke("Sagas.Ack.V1", new object[1] { e.Id });
						}
						else
						{
							pending.Remove(e.Id);
						}
					});
				}
			});
		}

		internal void Record(SagaEvent e)
		{
			e.World = World;
			if (e.World == "" || e.Id == "" || !SagaService.ValidEvent(e))
			{
				return;
			}
			if (pending.Count < 4096)
			{
				if (!pending.ContainsKey(e.Id))
				{
					SagasNotifications.Notify(e, Identity(Object.op_Implicit((Object)(object)Player.m_localPlayer) ? Player.m_localPlayer.GetPlayerID() : 0));
				}
				pending[e.Id] = e;
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Sagas outbox full (4096 events): new event not retained; check server connection/storage.");
			}
		}

		private string QueueArt(RuntimeArt.Image? image)
		{
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			if (image == null)
			{
				return "";
			}
			if (image.Kind == "portrait")
			{
				string[] array = (from x in pendingMedia.Where<KeyValuePair<string, Packet>>(delegate(KeyValuePair<string, Packet> x)
					{
						MediaUpload? media = x.Value.Media;
						return ((media != null) ? media.Kind : null) == "portrait" && x.Value.Media.Id != image.Id;
					})
					select x.Key).ToArray();
				foreach (string text in array)
				{
					sentMedia.Remove(pendingMedia[text].Media.Id);
					pendingMedia.Remove(text);
				}
			}
			if ((!sentMedia.TryGetValue(image.Id, out var value) || Time.unscaledTime - value > 120f) && pendingMedia.Count < 64)
			{
				Packet packet = new Packet
				{
					AckId = "media:" + image.Id,
					Media = new MediaUpload
					{
						World = World,
						PlayerId = lastLocalId,
						Id = image.Id,
						Kind = image.Kind,
						Png = image.Png
					}
				};
				pendingMedia[packet.AckId] = packet;
				if (sentMedia.Count >= 256)
				{
					sentMedia.Remove(sentMedia.OrderBy<KeyValuePair<string, float>, float>((KeyValuePair<string, float> x) => x.Value).First().Key);
				}
				sentMedia[image.Id] = Time.unscaledTime;
			}
			return image.Id;
		}

		private PlayerSnapshot Snapshot(Player p)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Expected O, but got Unknown
			using StartupTrace startupTrace = new StartupTrace(!profileTraceRecorded, "equipment", delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			profileTraceRecorded = true;
			if (!shareProfile.Value)
			{
				pendingMedia.Clear();
				sentMedia.Clear();
				artwork?.Clear();
			}
			lastLocalId = Identity(p.GetPlayerID());
			knownCharacters.Add(lastLocalId);
			Vector3 position = ((Component)p).transform.position;
			PlayerSnapshot s = new PlayerSnapshot
			{
				World = World,
				PlayerId = Identity(p.GetPlayerID()),
				Name = p.GetPlayerName(),
				Online = true,
				ShareProfile = shareProfile.Value,
				ShareMap = shareMap.Value,
				SharePins = sharePins.Value,
				SharePosition = (sharePosition.Value && ZNet.instance.IsReferencePositionPublic()),
				X = position.x,
				Z = position.z
			};
			startupTrace.Mark("identity and permissions");
			foreach (ItemData item in JewelcraftingAdapter.Equipped(p))
			{
				GearItem val = Gear.Read(item);
				EquippedState.Apply(val, item, p);
				if (shareProfile.Value)
				{
					val.IconId = QueueArt(artwork?.TryIcon(item));
					JewelcraftingAdapter.Icons(val, (ItemData i) => QueueArt(artwork?.TryIcon(i)));
				}
				s.Gear.Add(val);
			}
			startupTrace.Mark("equipped metadata");
			if (shareProfile.Value)
			{
				RunStage("portrait capture", delegate
				{
					s.PortraitId = QueueArt(artwork?.TryPortrait(p, !pendingMedia.Values.Any(delegate(Packet x)
					{
						MediaUpload? media = x.Media;
						return ((media != null) ? media.Kind : null) == "portrait";
					})));
				});
				s.PortraitStatus = artwork?.Status ?? "waiting-for-player";
			}
			startupTrace.Mark("portrait scheduling");
			s.Hotbar = (List<GearItem>)(shareProfile.Value ? ((IList)HotbarCapture.Read(p, (ItemData item) => QueueArt(artwork?.TryIcon(item)))) : ((IList)new List<GearItem>()));
			startupTrace.Mark("hotbar metadata");
			s.EffectiveResistances = RuntimeArt.EffectiveResistances(p);
			startupTrace.Mark("resistances");
			s.EffectiveStats["Armor"] = ((Character)p).GetBodyArmor();
			s.EffectiveStats["Health"] = ((Character)p).GetHealth();
			s.EffectiveStats["Max health"] = ((Character)p).GetMaxHealth();
			s.EffectiveStats["Max stamina"] = ((Character)p).GetMaxStamina();
			s.EffectiveStats["Max eitr"] = ((Character)p).GetMaxEitr();
			if (shareProfile.Value)
			{
				s.EpicLootInstalled = EpicProgress.Installed;
				s.JewelcraftingInstalled = JewelcraftingAdapter.Installed;
				s.Gold = EpicProgress.CarriedGold(p);
			}
			startupTrace.Mark("effective stats and gold");
			return s;
		}

		private void SyncMap(Player p)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			if (!shareMap.Value || !Object.op_Implicit((Object)(object)Minimap.instance) || WorldGenerator.instance == null || pendingMaps.Count >= 2 || RuntimeTerrainShader.Pending)
			{
				return;
			}
			Minimap map = Minimap.instance;
			BitArray bits = exploredField.GetValue(map) as BitArray;
			if (bits == null)
			{
				return;
			}
			ExplorationBatch batch = new ExplorationBatch
			{
				World = World,
				PlayerId = Identity(p.GetPlayerID()),
				Imported = importing
			};
			int num = 256;
			int payloadBytes = 0;
			bool full = false;
			Vector3 position = ((Component)p).transform.position;
			historyMapTurn = !historyMapTurn;
			if (!historyMapTurn)
			{
				(int, int)[] array = ExplorationScan.Nearby(position.x, position.z).ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					if (full)
					{
						break;
					}
					(int, int) tuple = array[nearMapCursor++ % array.Length];
					AddCell(tuple.Item1, tuple.Item2);
				}
				nearMapCursor %= array.Length;
			}
			while (num-- > 0 && batch.Cells.Count < 64 && !full)
			{
				int cx = mapCursor % 320 - 160;
				int cz = mapCursor / 320 - 160;
				mapCursor++;
				if (mapCursor >= 102400)
				{
					mapCursor = 0;
					importing = false;
				}
				AddCell(cx, cz);
			}
			if (batch.Cells.Count > 0)
			{
				Packet packet = new Packet
				{
					Exploration = batch,
					AckId = "map:" + Guid.NewGuid().ToString("N")
				};
				pendingMaps[packet.AckId] = packet;
			}
			void AddCell(int num2, int num3)
			{
				//IL_012f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0134: Unknown result type (might be due to invalid IL or missing references)
				//IL_013b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0142: Unknown result type (might be due to invalid IL or missing references)
				//IL_014d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0158: Unknown result type (might be due to invalid IL or missing references)
				//IL_0164: Expected O, but got Unknown
				if (payloadBytes > 74000)
				{
					full = true;
				}
				else
				{
					string item = num2 + ":" + num3;
					if (!fullyMapped.Contains(item))
					{
						string text = RuntimeTerrain.Capture(map, bits, num2, num3);
						if (!(text == ""))
						{
							full = true;
							byte[] rgba = Convert.FromBase64String(text);
							using SHA256 sHA = SHA256.Create();
							string text2 = Convert.ToBase64String(sHA.ComputeHash(rgba));
							if (!tileVersions.TryGetValue(item, out string value) || !(value == text2))
							{
								payloadBytes += text.Length;
								if (payloadBytes > 74000)
								{
									full = true;
								}
								tileVersions[item] = text2;
								if (Enumerable.Range(0, rgba.Length / 4).All((int num4) => rgba[num4 * 4 + 3] == byte.MaxValue))
								{
									fullyMapped.Add(item);
								}
								batch.Cells.Add(new MapCell
								{
									X = num2,
									Z = num3,
									Biome = "Explored terrain",
									Height = 0f,
									TerrainPixels = text
								});
								sentCells.Add(item);
								full = true;
							}
							return;
						}
						if (RuntimeTerrainShader.Pending)
						{
							full = true;
						}
					}
				}
			}
		}

		private void RefreshSls()
		{
			if (service == null || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || slsWorld != World)
			{
				slsCapture?.Dispose();
				slsCapture = null;
				slsWorld = World;
				nextSlsRefresh = 0f;
				if (service == null || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
				{
					return;
				}
			}
			if (slsPublish != null)
			{
				if (!slsPublish.IsCompleted)
				{
					return;
				}
				if (slsPublish.IsFaulted || !slsPublish.Result)
				{
					Warn("optional SLS publish", (Exception)(((object)slsPublish.Exception) ?? ((object)new InvalidOperationException("SLS storage queue rejected the update."))));
				}
				slsPublish = null;
			}
			if (slsCapture == null && Time.unscaledTime < nextSlsRefresh)
			{
				return;
			}
			try
			{
				if (slsCapture == null)
				{
					slsCapture = SlsAdapter.BeginCapture();
				}
				if (slsCapture.Step())
				{
					SlsWorldState snapshot = slsCapture.State;
					slsCapture.Dispose();
					slsCapture = null;
					nextSlsRefresh = Time.unscaledTime + 15f;
					SagaService target = service;
					string world = slsWorld;
					slsPublish = Task.Run(() => target.UpdateSls(world, snapshot));
				}
			}
			catch (Exception e)
			{
				slsCapture?.Dispose();
				slsCapture = null;
				nextSlsRefresh = Time.unscaledTime + 15f;
				Warn("optional SLS integration", e);
			}
		}

		private float? HostNemesisScore(long playerId)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!SlsAdapter.NemesisEnabled || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
				{
					return null;
				}
				Player localPlayer = Player.m_localPlayer;
				if (Object.op_Implicit((Object)(object)localPlayer) && localPlayer.GetPlayerID() == playerId)
				{
					ZNetView component = ((Component)localPlayer).GetComponent<ZNetView>();
					return SlsAdapter.Score((component != null) ? component.GetZDO() : null);
				}
				if (ZDOMan.instance == null)
				{
					return null;
				}
				foreach (ZNetPeer peer in ZNet.instance.GetPeers())
				{
					if (peer.IsReady() && PeerPlayerId(peer) == playerId)
					{
						return SlsAdapter.Score(ZDOMan.instance.GetZDO(peer.m_characterID));
					}
				}
				return null;
			}
			catch (Exception e)
			{
				Warn("optional SLS score", e);
				return null;
			}
		}

		private void SetupVersionEnforcement()
		{
			requireMatchingVersion = BindSetting("Server", "RequireMatchingVersion", value: true, "Require joining players to install this exact Valheim Sagas version. Missing or mismatched clients are rejected before joining the world. False permits mixed/missing clients, whose telemetry may be unavailable.");
		}

		private void RegisterVersion(ZNet net, ZNetPeer peer)
		{
			if (!net.IsServer())
			{
				versionFailure = "";
			}
			peer.m_rpc.Register<string>("Sagas.Version.V1", (Action<ZRpc, string>)delegate(ZRpc rpc, string version)
			{
				if (Object.op_Implicit((Object)(object)net) && net.IsServer() && rpc == peer.m_rpc && !versionRejected.Contains(rpc))
				{
					if (!VersionPolicy.Valid(version))
					{
						RejectVersion(net, peer, VersionPolicy.Rejection(((BaseUnityPlugin)this).Info.Metadata.Version.ToString(), (string)null, true), understands: false);
					}
					else if (!peerVersions.ContainsKey(rpc) && peerVersions.Count >= 128)
					{
						RejectVersion(net, peer, "Valheim Sagas connection limit reached. Try again shortly.", understands: true);
					}
					else if (!peerVersions.ContainsKey(rpc))
					{
						peerVersions[rpc] = version;
						rpc.Invoke("Sagas.VersionReply.V1", new object[1] { ((BaseUnityPlugin)this).Info.Metadata.Version.ToString() + "|" + (requireMatchingVersion.Value ? "1" : "0") });
					}
				}
			});
			peer.m_rpc.Register<string>("Sagas.VersionReply.V1", (Action<ZRpc, string>)delegate(ZRpc rpc, string reply)
			{
				if (Object.op_Implicit((Object)(object)net) && !net.IsServer() && peer.m_server && rpc == peer.m_rpc && reply != null && reply.Length <= 64)
				{
					string[] array = reply.Split('|');
					if (array.Length == 2 && VersionPolicy.Valid(array[0]) && (!(array[1] != "0") || !(array[1] != "1")) && array[1] == "1" && array[0] != ((BaseUnityPlugin)this).Info.Metadata.Version.ToString())
					{
						FailVersionClient(net, peer, "Valheim Sagas version mismatch. Server: " + array[0] + "; your client: " + ((BaseUnityPlugin)this).Info.Metadata.Version?.ToString() + ". Install the same version as the server, then reconnect.");
					}
				}
			});
			peer.m_rpc.Register<string>("Sagas.VersionRejected.V1", (Action<ZRpc, string>)delegate(ZRpc rpc, string message)
			{
				if (Object.op_Implicit((Object)(object)net) && !net.IsServer() && peer.m_server && rpc == peer.m_rpc && message != null && message.Length <= 500)
				{
					FailVersionClient(net, peer, message.Replace("<", "‹").Replace(">", "›"));
				}
			});
		}

		private void FailVersionClient(ZNet net, ZNetPeer peer, string reason)
		{
			if (versionRejected.Add(peer.m_rpc))
			{
				versionFailure = reason;
				((BaseUnityPlugin)this).Logger.LogWarning((object)reason);
				ZNet.SetExternalError((ConnectionStatus)3);
				((MonoBehaviour)this).StartCoroutine(DisconnectVersionLater(net, peer));
			}
		}

		private bool CheckPeerVersion(ZNet net, ZRpc rpc)
		{
			if (!net.IsServer())
			{
				return versionFailure.Length == 0;
			}
			if (versionRejected.Contains(rpc))
			{
				return false;
			}
			peerVersions.TryGetValue(rpc, out string value);
			string text = VersionPolicy.Rejection(((BaseUnityPlugin)this).Info.Metadata.Version.ToString(), value, requireMatchingVersion.Value);
			if (text.Length == 0)
			{
				return true;
			}
			ZNetPeer val = net.GetPeers().Find((ZNetPeer p) => p.m_rpc == rpc);
			if (val != null)
			{
				RejectVersion(net, val, text, value != null);
			}
			return false;
		}

		private void RejectVersion(ZNet net, ZNetPeer peer, string reason, bool understands)
		{
			if (versionRejected.Add(peer.m_rpc))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)reason);
				if (understands)
				{
					peer.m_rpc.Invoke("Sagas.VersionRejected.V1", new object[1] { reason });
				}
				peer.m_rpc.Invoke("Error", new object[1] { 3 });
				if (versionRejected.Count <= 128)
				{
					((MonoBehaviour)this).StartCoroutine(DisconnectVersionLater(net, peer));
				}
				else
				{
					net.Disconnect(peer);
				}
			}
		}

		private IEnumerator DisconnectVersionLater(ZNet net, ZNetPeer peer)
		{
			yield return (object)new WaitForSecondsRealtime(1f);
			if (Object.op_Implicit((Object)(object)net) && net.GetPeers().Contains(peer))
			{
				net.Disconnect(peer);
			}
		}

		private string ListenAddress()
		{
			return WebsiteAddress.ListenPrefix(prefix.Value, websitePort.Value, Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated());
		}

		private string HostWebsiteAddress()
		{
			string text = WebsiteAddress.Validate(websiteUrl.Value);
			if (text != "")
			{
				return text;
			}
			return WebsiteAddress.FromHost(serverAddress.Value, WebsiteAddress.ListenPort(ListenAddress()));
		}

		private void LogWebsiteSetup()
		{
			if (!host.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Sagas website disabled by Server / EnableWebsite.");
				return;
			}
			string text = ListenAddress();
			int num = WebsiteAddress.ListenPort(text);
			SagaService? obj = service;
			if (obj == null || !obj.WebsiteListening)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Sagas website did not start. Check the earlier website-listener error, an available TCP web port, and host-panel allocation. Set WebsitePort, or correct the advanced ListenPrefix override.");
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Sagas website listening on TCP port " + num + "; viewing is " + (requireToken.Value ? "private (login required)." : "public (shared data only).")));
			if (!string.IsNullOrWhiteSpace(prefix.Value))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Advanced ListenPrefix is active and overrides WebsitePort. Clear ListenPrefix to use the simplified port setting.");
			}
			string text2 = WebsiteAddress.Validate(text);
			if (ZNet.instance.IsDedicated() && text2 != "" && new Uri(text2).IsLoopback)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"The dedicated website is bound to loopback: remote browsers cannot connect directly. Clear ListenPrefix to listen on all interfaces using WebsitePort, or keep it if a local reverse proxy is intentional.");
			}
			string text3 = HostWebsiteAddress();
			if (text3 != "")
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Sagas browser address: " + text3));
			}
			else if (!string.IsNullOrWhiteSpace(websiteUrl.Value))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"WebsiteUrl is invalid. Use a complete HTTP(S) browser URL without credentials, query or fragment.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Sagas direct-IP clients can open the server address on web port " + num + ". For relay/join-code connections or a custom domain, set Server / WebsiteUrl. The host panel/firewall must allow this TCP web port; Sagas does not open ports."));
			}
		}

		private void MigrateShortcutDefaults()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Internal", "ModernShortcutDefaults", false, new ConfigDescription("One-time migration of the original conflicting Sagas shortcuts; custom shortcuts are preserved.", (AcceptableValueBase)null, new object[1]
			{
				new HiddenShortcutMigration()
			}));
			if (val.Value)
			{
				return;
			}
			(ConfigEntry<KeyboardShortcut>, KeyCode, KeyCode)[] array = new(ConfigEntry<KeyboardShortcut>, KeyCode, KeyCode)[3]
			{
				(loginShortcut, (KeyCode)289, (KeyCode)277),
				(revokeShortcut, (KeyCode)290, (KeyCode)279),
				(websiteShortcut, (KeyCode)291, (KeyCode)278)
			};
			for (int i = 0; i < array.Length; i++)
			{
				(ConfigEntry<KeyboardShortcut>, KeyCode, KeyCode) tuple = array[i];
				if (((object)tuple.Item1.Value/*cast due to .constrained prefix*/).Equals((object)new KeyboardShortcut(tuple.Item2, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 })))
				{
					tuple.Item1.Value = new KeyboardShortcut(tuple.Item3, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 });
				}
			}
			val.Value = true;
		}

		private void SetupWebsiteOverlay()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			websiteShortcut = BindSetting<KeyboardShortcut>("Website Login", "OpenWebsite", new KeyboardShortcut((KeyCode)278, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Open Sagas in the Steam overlay. Requires Steam overlay enabled. KeyCode.None disables this shortcut.");
			MigrateShortcutDefaults();
			websiteOverride = BindSetting("Website Login", "WebsiteUrlOverride", "", "Optional personal HTTP(S) website URL, used instead of the host URL. No credentials in URLs.");
			websitePort = BindSetting("Server", "WebsitePort", 8877, new ConfigDescription("The TCP web port allocated by your host, for example 19908. Dedicated servers listen on all interfaces automatically; local hosts use loopback. The game port is separate. Advanced ListenPrefix overrides this setting.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 65535), Array.Empty<object>()));
			websiteUrl = BindSetting("Server", "WebsiteUrl", "", "Optional public HTTP(S) URL, such as https://sagas.example.com/ or http://74.112.78.28:19908/. Takes priority over automatic direct-join IP plus WebsitePort. Required when joining through a relay/join code without a usable direct IP, or using a proxy/external port mapping. Never include credentials. Does not change the listener port.");
		}

		private void UpdateWebsiteOverlay()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (websiteNonce != "" && Time.unscaledTime > websiteDeadline)
			{
				websiteNonce = "";
				LoginNotice("Sagas website request timed out. Set WebsiteUrlOverride if the host has not updated.");
			}
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return;
			}
			KeyboardShor