Decompiled source of ZoneSavior v1.0.1

ZoneSavior.dll

Decompiled 6 days ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ZoneSavior")]
[assembly: AssemblyDescription("Dedicated-server zone archiving, zone bundle restore, zone UI, and per-zone WearNTear limits for Valheim.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("ZoneSavior")]
[assembly: AssemblyCopyright("Copyright 2026 sighsorry")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B276E8F6-7F9F-4A0F-8E7D-757383FF7CB1")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[CompilerGenerated]
internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T>
	{
		object IEnumerator.Current => _item;

		T IEnumerator<T>.Current => _item;

		public Enumerator(T item)
		{
			_item = item;
		}

		bool IEnumerator.MoveNext()
		{
			if (!_moveNextCalled)
			{
				return _moveNextCalled = true;
			}
			return false;
		}

		void IEnumerator.Reset()
		{
			_moveNextCalled = false;
		}

		void IDisposable.Dispose()
		{
		}
	}

	int ICollection.Count => 1;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => 1;

	T IReadOnlyList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
	}

	int ICollection<T>.Count => 1;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlySingleElementList(T item)
	{
		_item = item;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection.CopyTo(Array array, int index)
	{
		array.SetValue(_item, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return EqualityComparer<T>.Default.Equals(_item, (T)value);
	}

	int IList.IndexOf(object value)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, (T)value))
		{
			return -1;
		}
		return 0;
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return EqualityComparer<T>.Default.Equals(_item, item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		array[arrayIndex] = _item;
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, item))
		{
			return -1;
		}
		return 0;
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class ExtensionMarkerAttribute : Attribute
	{
		private readonly string <Name>k__BackingField;

		public string Name => <Name>k__BackingField;

		public ExtensionMarkerAttribute(string name)
		{
			<Name>k__BackingField = name;
		}
	}
}
namespace ZoneSavior
{
	internal static class AutoArchiveCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__HandleCommand;

			public static Action<long, ZPackage> <1>__RPC_HandleRequest;

			public static Action<long, ZPackage> <2>__RPC_HandleResult;

			public static Func<char, bool> <3>__IsDigit;
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass24_0
		{
			public ZoneBundleCommandResult result;

			internal void <ExecuteRestoreAsync>b__0(ZoneBundleCommandResult value)
			{
				result = value;
			}
		}

		[CompilerGenerated]
		private sealed class <ExecuteRestoreAsync>d__24 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string tag;

			public Action<AutoArchiveCommandResult> onComplete;

			private <>c__DisplayClass24_0 <>8__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ExecuteRestoreAsync>d__24(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass24_0();
					<>8__1.result = ZoneBundleCommandResult.Fail("Archive restore failed before it started.");
					<>2__current = ZoneBundleCommands.RestoreTagToOriginalZonesAsync(tag, delegate(ZoneBundleCommandResult value)
					{
						<>8__1.result = value;
					});
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					onComplete(<>8__1.result.Success ? AutoArchiveCommandResult.Ok(new <>z__ReadOnlySingleElementList<string>(<>8__1.result.Message)) : AutoArchiveCommandResult.Fail(<>8__1.result.Message));
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const string ScanCommand = "zs_archive_scan";

		private const string StatusCommand = "zs_archive_status";

		private const string PlayerCommand = "zs_archive_player";

		private const string ListCommand = "zs_archive_list";

		private const string MarkSeenCommand = "zs_archive_mark_seen";

		private const string IgnorePlayerCommand = "zs_archive_ignore_player";

		private const string RestoreCommand = "zs_archive_restore";

		private const string ScheduleCommand = "zs_archive_schedule";

		private const string DebugZoneCommand = "zs_archive_debug_zone";

		private const string RequestRpcName = "sighsorry.ZoneSavior_AutoArchiveCommandRequest";

		private const string ResultRpcName = "sighsorry.ZoneSavior_AutoArchiveCommandResult";

		private static readonly Regex ZoneSpecPattern = new Regex("^\\s*\\(\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*\\)\\s*$", RegexOptions.Compiled);

		private static ManualLogSource _logger = null;

		private static bool _initialized;

		private static readonly ZoneRpcRegistrar RpcRegistrar = new ZoneRpcRegistrar();

		public static void Initialize(ManualLogSource logger)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_00aa: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00d5: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			//IL_0146: 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_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: 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_0171: Expected O, but got Unknown
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Expected O, but got Unknown
			//IL_01e2: 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_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Expected O, but got Unknown
			if (!_initialized)
			{
				_initialized = true;
				_logger = logger;
				object obj = <>O.<0>__HandleCommand;
				if (obj == null)
				{
					ConsoleEvent val = HandleCommand;
					<>O.<0>__HandleCommand = val;
					obj = (object)val;
				}
				new ConsoleCommand("zs_archive_scan", "[dry|save|reset] - Runs an auto archive scan.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj2 = <>O.<0>__HandleCommand;
				if (obj2 == null)
				{
					ConsoleEvent val2 = HandleCommand;
					<>O.<0>__HandleCommand = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("zs_archive_status", "- Prints auto archive activity status.", (ConsoleEvent)obj2, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj3 = <>O.<0>__HandleCommand;
				if (obj3 == null)
				{
					ConsoleEvent val3 = HandleCommand;
					<>O.<0>__HandleCommand = val3;
					obj3 = (object)val3;
				}
				new ConsoleCommand("zs_archive_player", "steamID [dry|save|reset] - Runs an auto archive scan filtered to one Steam owner.", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj4 = <>O.<0>__HandleCommand;
				if (obj4 == null)
				{
					ConsoleEvent val4 = HandleCommand;
					<>O.<0>__HandleCommand = val4;
					obj4 = (object)val4;
				}
				new ConsoleCommand("zs_archive_list", "- Lists recent auto archive runs.", (ConsoleEvent)obj4, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj5 = <>O.<0>__HandleCommand;
				if (obj5 == null)
				{
					ConsoleEvent val5 = HandleCommand;
					<>O.<0>__HandleCommand = val5;
					obj5 = (object)val5;
				}
				new ConsoleCommand("zs_archive_mark_seen", "playerID - Marks a playerID as seen now.", (ConsoleEvent)obj5, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj6 = <>O.<0>__HandleCommand;
				if (obj6 == null)
				{
					ConsoleEvent val6 = HandleCommand;
					<>O.<0>__HandleCommand = val6;
					obj6 = (object)val6;
				}
				new ConsoleCommand("zs_archive_ignore_player", "playerID [on|off] - Protects or unprotects a playerID from auto archive.", (ConsoleEvent)obj6, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj7 = <>O.<0>__HandleCommand;
				if (obj7 == null)
				{
					ConsoleEvent val7 = HandleCommand;
					<>O.<0>__HandleCommand = val7;
					obj7 = (object)val7;
				}
				new ConsoleCommand("zs_archive_restore", "tag - Restores an archived tag to its original zones.", (ConsoleEvent)obj7, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj8 = <>O.<0>__HandleCommand;
				if (obj8 == null)
				{
					ConsoleEvent val8 = HandleCommand;
					<>O.<0>__HandleCommand = val8;
					obj8 = (object)val8;
				}
				new ConsoleCommand("zs_archive_schedule", "[status|now|clear|last yyyy-MM-dd HH:mm|next yyyy-MM-dd HH:mm] - Shows or adjusts the automatic archive scan schedule anchor. Local server time is used unless the date includes Z or an offset.", (ConsoleEvent)obj8, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj9 = <>O.<0>__HandleCommand;
				if (obj9 == null)
				{
					ConsoleEvent val9 = HandleCommand;
					<>O.<0>__HandleCommand = val9;
					obj9 = (object)val9;
				}
				new ConsoleCommand("zs_archive_debug_zone", "(x,z) - Writes a YAML report explaining auto archive eligibility for one zone.", (ConsoleEvent)obj9, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				RegisterRpcs();
			}
		}

		internal static void RegisterRpcs()
		{
			RpcRegistrar.EnsureRegistered(delegate(ZRoutedRpc routedRpc)
			{
				routedRpc.Register<ZPackage>("sighsorry.ZoneSavior_AutoArchiveCommandRequest", (Action<long, ZPackage>)RPC_HandleRequest);
				routedRpc.Register<ZPackage>("sighsorry.ZoneSavior_AutoArchiveCommandResult", (Action<long, ZPackage>)RPC_HandleResult);
			});
		}

		private static void HandleCommand(ConsoleEventArgs args)
		{
			EnsureCommandReady();
			DispatchRequest(new AutoArchiveCommandRequest
			{
				Command = ((args.Args.Length != 0) ? args.Args[0] : ""),
				Args = args.Args.ToList()
			}, args.Context);
		}

		private static void DispatchRequest(AutoArchiveCommandRequest request, Terminal? context)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			Terminal context2 = context;
			if (ZNet.instance.IsServer())
			{
				StartRequest(request, delegate(AutoArchiveCommandResult result)
				{
					ShowResult(result, context2);
				});
				return;
			}
			RegisterRpcs();
			ZPackage val = new ZPackage();
			val.Write(ZoneBundleSerialization.Serialize(request));
			ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "sighsorry.ZoneSavior_AutoArchiveCommandRequest", new object[1] { val });
			Terminal obj = context2;
			if (obj != null)
			{
				obj.AddString(request.Command + " request sent to server.");
			}
		}

		private static void RPC_HandleRequest(long sender, ZPackage package)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				if (!IsAuthorizedSender(sender))
				{
					SendResult(sender, AutoArchiveCommandResult.Fail("Admin only."));
					return;
				}
				StartRequest(ZoneBundleSerialization.Deserialize<AutoArchiveCommandRequest>(package.ReadString()), delegate(AutoArchiveCommandResult result)
				{
					SendResult(sender, result);
				});
			}
			catch (Exception ex)
			{
				_logger.LogError((object)$"Auto archive command RPC failed: {ex}");
				SendResult(sender, AutoArchiveCommandResult.Fail(ex.Message));
			}
		}

		private static void SendResult(long target, AutoArchiveCommandResult result)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Expected O, but got Unknown
			if (ZRoutedRpc.instance != null)
			{
				ZPackage val = new ZPackage();
				val.Write(ZoneBundleSerialization.Serialize(result));
				ZRoutedRpc.instance.InvokeRoutedRPC(target, "sighsorry.ZoneSavior_AutoArchiveCommandResult", new object[1] { val });
			}
		}

		private static void RPC_HandleResult(long sender, ZPackage package)
		{
			if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer())
			{
				ShowResult(ZoneBundleSerialization.Deserialize<AutoArchiveCommandResult>(package.ReadString()), (Terminal?)(object)Console.instance);
			}
		}

		private static void StartRequest(AutoArchiveCommandRequest request, Action<AutoArchiveCommandResult> onComplete)
		{
			if ((Object)(object)ZoneSaviorPlugin.Instance != (Object)null && TryGetRestoreTag(request, out string tag, out AutoArchiveCommandResult validationResult))
			{
				if (validationResult != null)
				{
					onComplete(validationResult);
				}
				else
				{
					((MonoBehaviour)ZoneSaviorPlugin.Instance).StartCoroutine(ExecuteRestoreAsync(tag, onComplete));
				}
			}
			else
			{
				onComplete(ExecuteRequest(request));
			}
		}

		private static bool TryGetRestoreTag(AutoArchiveCommandRequest request, out string tag, out AutoArchiveCommandResult? validationResult)
		{
			tag = "";
			validationResult = null;
			string text = request.Command;
			string[] array = request.Args?.ToArray() ?? Array.Empty<string>();
			if (string.IsNullOrWhiteSpace(text) && array.Length != 0)
			{
				text = array[0];
			}
			if (!string.Equals(text, "zs_archive_restore", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (array.Length < 2)
			{
				validationResult = AutoArchiveCommandResult.Fail("Syntax: zs_archive_restore tag");
				return true;
			}
			tag = array[1];
			return true;
		}

		[IteratorStateMachine(typeof(<ExecuteRestoreAsync>d__24))]
		private static IEnumerator ExecuteRestoreAsync(string tag, Action<AutoArchiveCommandResult> onComplete)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ExecuteRestoreAsync>d__24(0)
			{
				tag = tag,
				onComplete = onComplete
			};
		}

		private static AutoArchiveCommandResult ExecuteRequest(AutoArchiveCommandRequest request)
		{
			List<string> messages = new List<string>();
			try
			{
				string text = request.Command;
				string[] array = request.Args?.ToArray() ?? Array.Empty<string>();
				if (string.IsNullOrWhiteSpace(text) && array.Length != 0)
				{
					text = array[0];
				}
				switch (text)
				{
				case "zs_archive_scan":
					ExecuteScan(array, messages);
					break;
				case "zs_archive_status":
					ExecuteStatus(messages);
					break;
				case "zs_archive_player":
					ExecutePlayer(array, messages);
					break;
				case "zs_archive_list":
					ExecuteList(messages);
					break;
				case "zs_archive_mark_seen":
					ExecuteMarkSeen(array, messages);
					break;
				case "zs_archive_ignore_player":
					ExecuteIgnorePlayer(array, messages);
					break;
				case "zs_archive_restore":
					ExecuteRestore(array, messages);
					break;
				case "zs_archive_schedule":
					ExecuteSchedule(array, messages);
					break;
				case "zs_archive_debug_zone":
					ExecuteDebugZone(array, messages);
					break;
				default:
					throw new InvalidOperationException("Unsupported auto archive command '" + text + "'.");
				}
				return AutoArchiveCommandResult.Ok(messages);
			}
			catch (Exception ex)
			{
				_logger.LogError((object)$"Auto archive command '{request.Command}' failed: {ex}");
				return AutoArchiveCommandResult.Fail(ex.Message);
			}
		}

		private static void ExecuteScan(string[] args, List<string> messages)
		{
			ParseMode(args.Skip(1), out var dryRun, out var reset);
			if (!AutoArchiveService.QueueManualScan(dryRun, reset))
			{
				messages.Add("Auto archive scan could not be started. World may not be ready or another scan is running.");
			}
			else
			{
				messages.Add("Auto archive scan started.");
			}
		}

		private static void ExecuteStatus(List<string> messages)
		{
			AutoArchiveState state = AutoArchiveStore.State;
			int num = state.Players.Sum((PlayerActivityRecord player) => player.PlayerIds.Count);
			string arg = (AutoArchiveConfig.Enabled ? $"enabled, interval: {AutoArchiveConfig.ScanIntervalMinutes} minute(s), next auto scan: {FormatDate(GetNextAutoScanUtc(state.LastAutoScanUtc))}" : "disabled (Scan Interval Minutes = 0)");
			messages.Add($"Auto archive automatic scan: {arg}, dry run: {AutoArchiveConfig.DryRun}, reset after save: {AutoArchiveConfig.ResetAfterSave}.");
			messages.Add($"Players: {state.Players.Count} platform record(s), {num} playerID(s), ignored: {state.IgnoredPlayerIds.Count}.");
			messages.Add($"Runs: {state.Runs.Count}, last scan: {FormatDate(state.LastScanUtc)}, last auto scan: {FormatDate(state.LastAutoScanUtc)}.");
			messages.Add("Activity file: " + AutoArchiveStore.FilePath);
		}

		private static void ExecutePlayer(string[] args, List<string> messages)
		{
			if (args.Length < 2)
			{
				throw new InvalidOperationException("Syntax: zs_archive_player steamID [dry|save|reset]");
			}
			string targetLabel;
			List<long> targetPlayerIds = ResolveTargetPlayerIds(args[1], out targetLabel);
			ParseMode(args.Skip(2), out var dryRun, out var reset);
			if (!AutoArchiveService.QueueManualScan(dryRun, reset, targetPlayerIds))
			{
				messages.Add("Auto archive player scan could not be started. World may not be ready or another scan is running.");
			}
			else
			{
				messages.Add("Auto archive scan started for " + targetLabel + ".");
			}
		}

		private static void ExecuteList(List<string> messages)
		{
			List<ArchiveRunRecord> list = AutoArchiveStore.State.Runs.OrderByDescending((ArchiveRunRecord run) => run.CreatedUtc).Take(10).ToList();
			if (list.Count == 0)
			{
				messages.Add("No auto archive runs recorded.");
				return;
			}
			foreach (ArchiveRunRecord item in list)
			{
				messages.Add($"{item.RunId}: dry={item.DryRun}, reset={item.ResetAfterSave}, candidates={item.CandidateZones}, processed={item.ProcessedZones}, clusters={item.Clusters.Count}");
				foreach (ArchiveClusterRecord item2 in item.Clusters.Take(5))
				{
					string text = (string.IsNullOrWhiteSpace(item2.Tag) ? "-" : item2.Tag);
					messages.Add($"  {item2.Status} {text}: zones={item2.Zones.Count}, pieces={item2.PieceCount}, reason={item2.Reason}");
				}
			}
		}

		private static void ExecuteMarkSeen(string[] args, List<string> messages)
		{
			if (args.Length < 2 || !long.TryParse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				throw new InvalidOperationException("Syntax: zs_archive_mark_seen playerID");
			}
			AutoArchiveStore.RecordManualPlayerSeen(result, DateTime.UtcNow);
			AutoArchiveStore.Flush(force: true);
			messages.Add($"Marked playerID {result} as seen now.");
		}

		private static void ExecuteIgnorePlayer(string[] args, List<string> messages)
		{
			if (args.Length < 2 || !long.TryParse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				throw new InvalidOperationException("Syntax: zs_archive_ignore_player playerID [on|off]");
			}
			bool flag = args.Length < 3 || !string.Equals(args[2], "off", StringComparison.OrdinalIgnoreCase);
			AutoArchiveStore.SetIgnored(result, flag);
			AutoArchiveStore.Flush(force: true);
			messages.Add(flag ? $"playerID {result} is now protected from auto archive." : $"playerID {result} is no longer ignored.");
		}

		private static void ExecuteRestore(string[] args, List<string> messages)
		{
			if (args.Length < 2)
			{
				throw new InvalidOperationException("Syntax: zs_archive_restore tag");
			}
			ZoneBundleCommandResult zoneBundleCommandResult = ZoneBundleCommands.RestoreTagToOriginalZones(args[1]);
			messages.Add(zoneBundleCommandResult.Message);
		}

		private static void ExecuteSchedule(string[] args, List<string> messages)
		{
			if (args.Length < 2 || string.Equals(args[1], "status", StringComparison.OrdinalIgnoreCase))
			{
				messages.Add(BuildScheduleMessage());
				return;
			}
			string a = args[1];
			if (string.Equals(a, "now", StringComparison.OrdinalIgnoreCase))
			{
				AutoArchiveStore.SetLastAutoScanUtc(DateTime.UtcNow);
				AutoArchiveStore.Flush(force: true);
				messages.Add(BuildScheduleMessage());
				return;
			}
			if (string.Equals(a, "clear", StringComparison.OrdinalIgnoreCase))
			{
				AutoArchiveStore.SetLastAutoScanUtc(DateTime.MinValue);
				AutoArchiveStore.Flush(force: true);
				messages.Add(BuildScheduleMessage());
				return;
			}
			if (args.Length < 3)
			{
				throw new InvalidOperationException("Syntax: zs_archive_schedule [status|now|clear|last yyyy-MM-dd HH:mm|next yyyy-MM-dd HH:mm]");
			}
			DateTime dateTime = ParseScheduleDate(args.Skip(2));
			if (string.Equals(a, "last", StringComparison.OrdinalIgnoreCase))
			{
				AutoArchiveStore.SetLastAutoScanUtc(dateTime);
				AutoArchiveStore.Flush(force: true);
				messages.Add(BuildScheduleMessage());
				return;
			}
			if (string.Equals(a, "next", StringComparison.OrdinalIgnoreCase))
			{
				if (!AutoArchiveConfig.Enabled)
				{
					throw new InvalidOperationException("Automatic auto archive scans are disabled because Scan Interval Minutes is 0.");
				}
				AutoArchiveStore.SetLastAutoScanUtc(dateTime - TimeSpan.FromMinutes(AutoArchiveConfig.ScanIntervalMinutes));
				AutoArchiveStore.Flush(force: true);
				messages.Add(BuildScheduleMessage());
				return;
			}
			throw new InvalidOperationException("Syntax: zs_archive_schedule [status|now|clear|last yyyy-MM-dd HH:mm|next yyyy-MM-dd HH:mm]");
		}

		private static void ExecuteDebugZone(string[] args, List<string> messages)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Vector2i val = ParseZoneSpec(args.Skip(1));
			AutoArchiveZoneDebugReport autoArchiveZoneDebugReport = BuildZoneDebugReport(val);
			string text = WriteZoneDebugReport(autoArchiveZoneDebugReport);
			messages.Add($"Archive debug zone ({val.x},{val.y}): zdo={autoArchiveZoneDebugReport.Summary.TotalZdos}, candidatePieces={autoArchiveZoneDebugReport.Summary.AutoArchiveCandidatePieces}, creators={autoArchiveZoneDebugReport.Summary.CandidateCreators}, wouldCandidate={autoArchiveZoneDebugReport.Summary.WouldBeCandidateZone}.");
			messages.Add("Reason: " + autoArchiveZoneDebugReport.Summary.Reason);
			messages.Add("Wrote YAML: " + text);
		}

		private static string BuildScheduleMessage()
		{
			DateTime lastAutoScanUtc = AutoArchiveStore.State.LastAutoScanUtc;
			if (!AutoArchiveConfig.Enabled)
			{
				return "Auto archive schedule: disabled (Scan Interval Minutes = 0), last auto scan=" + FormatDate(lastAutoScanUtc) + ".";
			}
			return $"Auto archive schedule: interval={AutoArchiveConfig.ScanIntervalMinutes} minute(s), last auto scan={FormatDate(lastAutoScanUtc)}, next auto scan={FormatDate(GetNextAutoScanUtc(lastAutoScanUtc))}.";
		}

		private static AutoArchiveZoneDebugReport BuildZoneDebugReport(Vector2i zone)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			if (ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				throw new InvalidOperationException("World ZDO systems are not ready.");
			}
			List<ZDO> list = new List<ZDO>();
			ZDOMan.instance.FindObjects(zone, list);
			DateTime utcNow = DateTime.UtcNow;
			AutoArchiveZoneDebugReport autoArchiveZoneDebugReport = new AutoArchiveZoneDebugReport();
			ZNet instance = ZNet.instance;
			autoArchiveZoneDebugReport.World = ((instance != null) ? instance.GetWorldName() : null) ?? "unknown";
			autoArchiveZoneDebugReport.CreatedAt = ZoneSaviorTimestamp.Format(utcNow);
			autoArchiveZoneDebugReport.Zone = new ZoneBundleZone
			{
				X = zone.x,
				Z = zone.y
			};
			autoArchiveZoneDebugReport.Settings = new AutoArchiveZoneDebugSettings
			{
				DryRun = AutoArchiveConfig.DryRun,
				ResetAfterSave = AutoArchiveConfig.ResetAfterSave,
				InactiveDays = AutoArchiveConfig.InactiveDays,
				UnknownOwnerGraceDays = AutoArchiveConfig.UnknownOwnerGraceDays,
				MinimumPiecesPerCluster = AutoArchiveConfig.MinimumPiecesPerCluster,
				SmallClusterAction = AutoArchiveConfig.SmallClusterAction.ToString(),
				MaxZonesPerRun = AutoArchiveConfig.MaxZonesPerRun
			};
			AutoArchiveZoneDebugReport autoArchiveZoneDebugReport2 = autoArchiveZoneDebugReport;
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			foreach (ZDO item in list)
			{
				if (item != null && item.IsValid() && hashSet.Add(item.m_uid))
				{
					AutoArchiveZoneDebugObject autoArchiveZoneDebugObject = BuildZoneDebugObject(zone, item);
					autoArchiveZoneDebugReport2.Objects.Add(autoArchiveZoneDebugObject);
					AddExclusionCounts(autoArchiveZoneDebugReport2.ExclusionCounts, autoArchiveZoneDebugObject.ExclusionReasons);
				}
			}
			List<long> list2 = (from playerId in (from entry in autoArchiveZoneDebugReport2.Objects
					where entry.AutoArchiveCandidatePiece
					select entry.CreatorPlayerId into playerId
					where playerId != 0
					select playerId).Distinct()
				orderby playerId
				select playerId).ToList();
			autoArchiveZoneDebugReport2.Creators = list2.Select((long playerId) => BuildCreatorDebug(playerId, utcNow)).ToList();
			bool flag = autoArchiveZoneDebugReport2.Creators.Count > 0 && autoArchiveZoneDebugReport2.Creators.All((AutoArchiveZoneDebugCreator creator) => creator.Eligible);
			autoArchiveZoneDebugReport2.Summary = new AutoArchiveZoneDebugSummary
			{
				TotalZdos = autoArchiveZoneDebugReport2.Objects.Count,
				InRequestedZone = autoArchiveZoneDebugReport2.Objects.Count((AutoArchiveZoneDebugObject entry) => entry.InRequestedZone),
				WearNTear = autoArchiveZoneDebugReport2.Objects.Count((AutoArchiveZoneDebugObject entry) => entry.HasWearNTear),
				PlayerBuildRecipe = autoArchiveZoneDebugReport2.Objects.Count((AutoArchiveZoneDebugObject entry) => entry.HasBuildRecipe),
				AutoArchiveCandidatePieces = autoArchiveZoneDebugReport2.Objects.Count((AutoArchiveZoneDebugObject entry) => entry.AutoArchiveCandidatePiece),
				CandidateCreators = list2.Count,
				ObjectDbReady = ((Object)(object)ObjectDB.instance != (Object)null),
				WouldBeCandidateZone = (autoArchiveZoneDebugReport2.Objects.Any((AutoArchiveZoneDebugObject entry) => entry.AutoArchiveCandidatePiece) && flag)
			};
			autoArchiveZoneDebugReport2.Summary.Reason = BuildDebugSummaryReason(autoArchiveZoneDebugReport2, flag);
			return autoArchiveZoneDebugReport2;
		}

		private static AutoArchiveZoneDebugObject BuildZoneDebugObject(Vector2i requestedZone, ZDO zdo)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = zdo.GetPosition();
			Vector2i zone = ZoneSystem.GetZone(position);
			GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab());
			bool flag = (Object)(object)prefab != (Object)null && Object.op_Implicit((Object)(object)prefab);
			bool flag2 = flag && (Object)(object)prefab.GetComponent<ZNetView>() != (Object)null;
			bool flag3 = flag && (Object)(object)prefab.GetComponent<WearNTear>() != (Object)null;
			bool hasPiece = flag && (Object)(object)prefab.GetComponent<Piece>() != (Object)null;
			bool flag4 = flag && ZoneBlueprintCommands.HasBuildRecipe(prefab);
			bool flag5 = zone.x == requestedZone.x && zone.y == requestedZone.y;
			long @long = zdo.GetLong(ZDOVars.s_creator, 0L);
			List<string> list = new List<string>();
			if (!flag5)
			{
				list.Add("outside_requested_zone");
			}
			if (!flag)
			{
				list.Add("prefab_missing");
			}
			if (flag && !flag2)
			{
				list.Add("no_znetview");
			}
			if (!flag3)
			{
				list.Add("not_wearntear");
			}
			if (@long == 0L)
			{
				list.Add("creatorless_or_missing_creator");
			}
			if (flag3 && !flag4)
			{
				list.Add("no_player_build_recipe_or_resource_cost");
			}
			bool flag6 = flag5 && @long != 0 && flag3 && flag4;
			if (flag6)
			{
				list.Clear();
			}
			AutoArchiveZoneDebugObject autoArchiveZoneDebugObject = new AutoArchiveZoneDebugObject();
			autoArchiveZoneDebugObject.ZdoId = ((object)(ZDOID)(ref zdo.m_uid)).ToString();
			autoArchiveZoneDebugObject.PrefabHash = zdo.GetPrefab();
			autoArchiveZoneDebugObject.Prefab = (flag ? Utils.GetPrefabName(prefab) : "");
			autoArchiveZoneDebugObject.Position = new float[3]
			{
				Round(position.x),
				Round(position.y),
				Round(position.z)
			};
			autoArchiveZoneDebugObject.ObjectZone = new ZoneBundleZone
			{
				X = zone.x,
				Z = zone.y
			};
			autoArchiveZoneDebugObject.CreatorPlayerId = @long;
			autoArchiveZoneDebugObject.CreatorName = zdo.GetString(ZDOVars.s_creatorName, "");
			autoArchiveZoneDebugObject.HasPrefab = flag;
			autoArchiveZoneDebugObject.HasZNetView = flag2;
			autoArchiveZoneDebugObject.HasWearNTear = flag3;
			autoArchiveZoneDebugObject.HasPiece = hasPiece;
			autoArchiveZoneDebugObject.HasBuildRecipe = flag4;
			autoArchiveZoneDebugObject.InRequestedZone = flag5;
			autoArchiveZoneDebugObject.AutoArchiveCandidatePiece = flag6;
			autoArchiveZoneDebugObject.ExclusionReasons = list;
			return autoArchiveZoneDebugObject;
		}

		private static AutoArchiveZoneDebugCreator BuildCreatorDebug(long playerId, DateTime utcNow)
		{
			AutoArchiveCreatorEligibility autoArchiveCreatorEligibility = AutoArchiveStore.EvaluateCreatorArchiveEligibility(playerId, utcNow, AutoArchiveConfig.InactiveDays, AutoArchiveConfig.UnknownOwnerGraceDays, recordUnknownPlayer: false);
			return new AutoArchiveZoneDebugCreator
			{
				PlayerId = autoArchiveCreatorEligibility.PlayerId,
				PlatformId = autoArchiveCreatorEligibility.PlatformId,
				Names = autoArchiveCreatorEligibility.Names,
				RecordedInActivity = autoArchiveCreatorEligibility.RecordedInActivity,
				UnknownActivityRecord = autoArchiveCreatorEligibility.UnknownActivityRecord,
				Ignored = autoArchiveCreatorEligibility.Ignored,
				Protected = autoArchiveCreatorEligibility.Protected,
				Eligible = autoArchiveCreatorEligibility.Eligible,
				Reason = autoArchiveCreatorEligibility.Reason
			};
		}

		private static string BuildDebugSummaryReason(AutoArchiveZoneDebugReport report, bool allCreatorsEligible)
		{
			if (report.Summary.AutoArchiveCandidatePieces == 0)
			{
				return "No WearNTear with a non-zero creator and a registered player build recipe was found in this zone.";
			}
			if (!allCreatorsEligible)
			{
				string text = string.Join("; ", from creator in report.Creators
					where !creator.Eligible
					select creator.Reason);
				return "At least one candidate creator is not archive-eligible: " + text;
			}
			if (report.Summary.AutoArchiveCandidatePieces < AutoArchiveConfig.MinimumPiecesPerCluster)
			{
				return $"Zone has candidate pieces, but this single-zone piece count is below Minimum Pieces Per Cluster ({report.Summary.AutoArchiveCandidatePieces}/{AutoArchiveConfig.MinimumPiecesPerCluster}). Cluster adjacency may still change the final action.";
			}
			return "This zone would be an auto archive candidate before cluster adjacency and max-zones-per-run checks.";
		}

		private static string WriteZoneDebugReport(AutoArchiveZoneDebugReport report)
		{
			string text = Path.Combine(ZoneSaviorPlugin.DataStorageFullPath, "Diagnostics");
			Directory.CreateDirectory(text);
			string path = string.Format("archive_debug_zone_{0}_{1}_{2}.yml", report.Zone.X, report.Zone.Z, DateTime.UtcNow.ToString("yyyyMMdd_HHmmss", CultureInfo.InvariantCulture));
			string text2 = Path.Combine(text, path);
			File.WriteAllText(text2, ZoneBundleSerialization.Serialize(report));
			return text2;
		}

		private static Vector2i ParseZoneSpec(IEnumerable<string> values)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			string input = string.Join(" ", values).Trim();
			Match match = ZoneSpecPattern.Match(input);
			if (!match.Success || !int.TryParse(match.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				throw new InvalidOperationException("Syntax: zs_archive_debug_zone (x,z)");
			}
			return new Vector2i(result, result2);
		}

		private static void AddExclusionCounts(Dictionary<string, int> counts, IEnumerable<string> reasons)
		{
			foreach (string reason in reasons)
			{
				counts.TryGetValue(reason, out var value);
				counts[reason] = value + 1;
			}
		}

		private static float Round(float value)
		{
			return Mathf.Round(value * 1000f) / 1000f;
		}

		private static void ParseMode(IEnumerable<string> args, out bool? dryRun, out bool? reset)
		{
			dryRun = null;
			reset = null;
			foreach (string arg in args)
			{
				if (string.Equals(arg, "dry", StringComparison.OrdinalIgnoreCase) || string.Equals(arg, "dry-run", StringComparison.OrdinalIgnoreCase))
				{
					dryRun = true;
					reset = false;
					continue;
				}
				if (string.Equals(arg, "save", StringComparison.OrdinalIgnoreCase))
				{
					dryRun = false;
					reset = false;
					continue;
				}
				if (string.Equals(arg, "reset", StringComparison.OrdinalIgnoreCase))
				{
					dryRun = false;
					reset = true;
					continue;
				}
				throw new InvalidOperationException("Unknown archive mode '" + arg + "'. Use dry, save, or reset.");
			}
		}

		private static List<long> ResolveTargetPlayerIds(string target, out string targetLabel)
		{
			if (AutoArchiveStore.TryGetPlayerIdsBySteamId(target, out List<long> playerIds, out string normalizedSteamId))
			{
				targetLabel = "steamID " + normalizedSteamId + " (playerID " + string.Join(", ", playerIds) + ")";
				return playerIds;
			}
			if (IsLikelySteamId(target))
			{
				throw new InvalidOperationException("No known playerID is linked to SteamID " + target + ". The player must have joined while ZoneSavior activity tracking was active.");
			}
			throw new InvalidOperationException("Syntax: zs_archive_player steamID [dry|save|reset]");
		}

		private static bool IsLikelySteamId(string target)
		{
			if (string.IsNullOrWhiteSpace(target))
			{
				return false;
			}
			string text = target.Trim();
			if (text.StartsWith("steam:", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring("steam:".Length);
			}
			return new string(text.Where(char.IsDigit).ToArray()).Length >= 15;
		}

		private static void EnsureCommandReady()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				throw new InvalidOperationException("World is not ready.");
			}
			if (!ZNet.instance.IsServer() && ZRoutedRpc.instance == null)
			{
				throw new InvalidOperationException("Server RPC is not ready.");
			}
			if (!ZNet.instance.IsServer() || (ZNet.instance.IsServer() && (Object)(object)Player.m_localPlayer == (Object)null) || ZNet.instance.LocalPlayerIsAdminOrHost())
			{
				return;
			}
			throw new InvalidOperationException("Admin only.");
		}

		private static bool IsAuthorizedSender(long sender)
		{
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			object obj;
			if (peer == null)
			{
				obj = null;
			}
			else
			{
				ZRpc rpc = peer.m_rpc;
				if (rpc == null)
				{
					obj = null;
				}
				else
				{
					ISocket socket = rpc.m_socket;
					obj = ((socket != null) ? socket.GetHostName() : null);
				}
			}
			if (obj == null)
			{
				obj = "";
			}
			string text = (string)obj;
			if (text.Length > 0)
			{
				return ZNet.instance.IsAdmin(text);
			}
			return false;
		}

		private static void ShowResult(AutoArchiveCommandResult result, Terminal? terminal)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			MessageType val = (MessageType)(result.Success ? 1 : 2);
			foreach (string item in (result.Messages.Count > 0) ? result.Messages : new List<string>(1) { result.Success ? "Done." : "Command failed." })
			{
				_logger.LogInfo((object)item);
				if (terminal != null)
				{
					terminal.AddString(item);
				}
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					((Character)Player.m_localPlayer).Message(val, item, 0, (Sprite)null);
				}
			}
		}

		private static string FormatDate(DateTime value)
		{
			if (!(value == DateTime.MinValue))
			{
				return ZoneSaviorTimestamp.Format(value);
			}
			return "-";
		}

		private static DateTime GetNextAutoScanUtc(DateTime lastAutoScanUtc)
		{
			if (AutoArchiveConfig.Enabled && !(lastAutoScanUtc == DateTime.MinValue))
			{
				return DateTime.SpecifyKind(lastAutoScanUtc, DateTimeKind.Utc) + TimeSpan.FromMinutes(AutoArchiveConfig.ScanIntervalMinutes);
			}
			return DateTime.MinValue;
		}

		private static DateTime ParseScheduleDate(IEnumerable<string> values)
		{
			string text = string.Join(" ", values).Trim();
			if (string.IsNullOrWhiteSpace(text))
			{
				throw new InvalidOperationException("Missing schedule date.");
			}
			DateTimeStyles styles = ((text.EndsWith("Z", StringComparison.OrdinalIgnoreCase) || HasTimezoneOffset(text)) ? (DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal) : (DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeLocal));
			if (!DateTime.TryParse(text, CultureInfo.InvariantCulture, styles, out var result) && !DateTime.TryParse(text, CultureInfo.CurrentCulture, styles, out result))
			{
				throw new InvalidOperationException("Invalid schedule date '" + text + "'. Use local server time like 2026-05-02 15:00, or UTC like 2026-05-02T06:00:00Z.");
			}
			return DateTime.SpecifyKind(result, DateTimeKind.Utc);
		}

		private static bool HasTimezoneOffset(string value)
		{
			string text = value.Trim();
			if (text.Length < 6)
			{
				return false;
			}
			int num = text.Length - 6;
			char c = text[num];
			if ((c == '+' || c == '-') && char.IsDigit(text[num + 1]) && char.IsDigit(text[num + 2]) && text[num + 3] == ':' && char.IsDigit(text[num + 4]))
			{
				return char.IsDigit(text[num + 5]);
			}
			return false;
		}
	}
	internal sealed class AutoArchiveCommandRequest
	{
		public string Command { get; set; } = "";


		public List<string> Args { get; set; } = new List<string>();

	}
	internal sealed class AutoArchiveCommandResult
	{
		public bool Success { get; set; }

		public List<string> Messages { get; set; } = new List<string>();


		public static AutoArchiveCommandResult Ok(IEnumerable<string> messages)
		{
			return new AutoArchiveCommandResult
			{
				Success = true,
				Messages = messages.ToList()
			};
		}

		public static AutoArchiveCommandResult Fail(string message)
		{
			return new AutoArchiveCommandResult
			{
				Success = false,
				Messages = new List<string>(1) { message }
			};
		}
	}
	internal enum AutoArchiveSmallClusterAction
	{
		Skip,
		SaveAnyway,
		ResetWithoutSave
	}
	internal sealed class AutoArchiveState
	{
		public int Version { get; set; } = 1;


		[YamlIgnore]
		public DateTime LastScanUtc { get; set; } = DateTime.MinValue;


		public string LastScanAt
		{
			get
			{
				return ZoneSaviorTimestamp.Format(LastScanUtc);
			}
			set
			{
				LastScanUtc = ZoneSaviorTimestamp.ParseUtc(value);
			}
		}

		[YamlIgnore]
		public DateTime LastAutoScanUtc { get; set; } = DateTime.MinValue;


		public string LastAutoScanAt
		{
			get
			{
				return ZoneSaviorTimestamp.Format(LastAutoScanUtc);
			}
			set
			{
				LastAutoScanUtc = ZoneSaviorTimestamp.ParseUtc(value);
			}
		}

		public List<PlayerActivityRecord> Players { get; set; } = new List<PlayerActivityRecord>();


		public List<ArchiveRunRecord> Runs { get; set; } = new List<ArchiveRunRecord>();


		public List<long> IgnoredPlayerIds { get; set; } = new List<long>();

	}
	internal sealed class PlayerActivityRecord
	{
		public string PlatformId { get; set; } = "";


		public List<long> PlayerIds { get; set; } = new List<long>();


		public List<string> Names { get; set; } = new List<string>();


		[YamlIgnore]
		public DateTime FirstSeenUtc { get; set; } = DateTime.MinValue;


		public string FirstSeenAt
		{
			get
			{
				return ZoneSaviorTimestamp.Format(FirstSeenUtc);
			}
			set
			{
				FirstSeenUtc = ZoneSaviorTimestamp.ParseUtc(value);
			}
		}

		[YamlIgnore]
		public DateTime LastSeenUtc { get; set; } = DateTime.MinValue;


		public string LastSeenAt
		{
			get
			{
				return ZoneSaviorTimestamp.Format(LastSeenUtc);
			}
			set
			{
				LastSeenUtc = ZoneSaviorTimestamp.ParseUtc(value);
			}
		}
	}
	internal sealed class ArchiveRunRecord
	{
		public string RunId { get; set; } = "";


		[YamlIgnore]
		public DateTime CreatedUtc { get; set; } = DateTime.MinValue;


		public string CreatedAt
		{
			get
			{
				return ZoneSaviorTimestamp.Format(CreatedUtc);
			}
			set
			{
				CreatedUtc = ZoneSaviorTimestamp.ParseUtc(value);
			}
		}

		public bool Manual { get; set; }

		public bool DryRun { get; set; }

		public bool ResetAfterSave { get; set; }

		public List<long> TargetPlayerIds { get; set; } = new List<long>();


		public int ScannedZdos { get; set; }

		public int StructureZdos { get; set; }

		public int CandidateZones { get; set; }

		public int ProcessedZones { get; set; }

		public List<ArchiveClusterRecord> Clusters { get; set; } = new List<ArchiveClusterRecord>();


		public List<string> Messages { get; set; } = new List<string>();

	}
	internal sealed class ArchiveClusterRecord
	{
		public string Tag { get; set; } = "";


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


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


		public int PieceCount { get; set; }

		public int TerrainLoaded { get; set; }

		public int TerrainCaptured { get; set; }

		public List<long> Creators { get; set; } = new List<long>();


		public List<ZoneBundleZone> Zones { get; set; } = new List<ZoneBundleZone>();

	}
	internal sealed class AutoArchiveScanOptions
	{
		public bool Manual { get; set; }

		public bool DryRun { get; set; }

		public bool ResetAfterSave { get; set; }

		public List<long> TargetPlayerIds { get; set; } = new List<long>();

	}
	internal sealed class AutoArchiveCreatorEligibility
	{
		public long PlayerId { get; set; }

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


		public List<string> Names { get; set; } = new List<string>();


		public bool RecordedInActivity { get; set; }

		public bool UnknownActivityRecord { get; set; }

		public bool Ignored { get; set; }

		public bool Protected { get; set; }

		public bool Eligible { get; set; }

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

	}
	internal sealed class ZoneBundleArchiveResult
	{
		public bool Success { get; set; }

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


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


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


		public int ZoneCount { get; set; }

		public int EntryCount { get; set; }

		public int MonsterCount { get; set; }

		public int TerrainLoaded { get; set; }

		public int TerrainCaptured { get; set; }
	}
	internal sealed class ZoneBundleResetResult
	{
		public bool Success { get; set; }

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


		public int ZoneCount { get; set; }

		public int RemovedCount { get; set; }

		public int RemainingWearNTearCount { get; set; }
	}
	internal sealed class AutoArchiveZoneDebugReport
	{
		public int Version { get; set; } = 1;


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


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


		public ZoneBundleZone Zone { get; set; } = new ZoneBundleZone();


		public AutoArchiveZoneDebugSettings Settings { get; set; } = new AutoArchiveZoneDebugSettings();


		public AutoArchiveZoneDebugSummary Summary { get; set; } = new AutoArchiveZoneDebugSummary();


		public List<AutoArchiveZoneDebugCreator> Creators { get; set; } = new List<AutoArchiveZoneDebugCreator>();


		public Dictionary<string, int> ExclusionCounts { get; set; } = new Dictionary<string, int>();


		public List<AutoArchiveZoneDebugObject> Objects { get; set; } = new List<AutoArchiveZoneDebugObject>();

	}
	internal sealed class AutoArchiveZoneDebugSettings
	{
		public bool DryRun { get; set; }

		public bool ResetAfterSave { get; set; }

		public int InactiveDays { get; set; }

		public int UnknownOwnerGraceDays { get; set; }

		public int MinimumPiecesPerCluster { get; set; }

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


		public int MaxZonesPerRun { get; set; }
	}
	internal sealed class AutoArchiveZoneDebugSummary
	{
		public int TotalZdos { get; set; }

		public int InRequestedZone { get; set; }

		public int WearNTear { get; set; }

		public int PlayerBuildRecipe { get; set; }

		public int AutoArchiveCandidatePieces { get; set; }

		public int CandidateCreators { get; set; }

		public bool ObjectDbReady { get; set; }

		public bool WouldBeCandidateZone { get; set; }

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

	}
	internal sealed class AutoArchiveZoneDebugCreator
	{
		public long PlayerId { get; set; }

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


		public List<string> Names { get; set; } = new List<string>();


		public bool RecordedInActivity { get; set; }

		public bool UnknownActivityRecord { get; set; }

		public bool Ignored { get; set; }

		public bool Protected { get; set; }

		public bool Eligible { get; set; }

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

	}
	internal sealed class AutoArchiveZoneDebugObject
	{
		public string ZdoId { get; set; } = "";


		public int PrefabHash { get; set; }

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


		public float[] Position { get; set; } = new float[3];


		public ZoneBundleZone ObjectZone { get; set; } = new ZoneBundleZone();


		public long CreatorPlayerId { get; set; }

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


		public bool HasPrefab { get; set; }

		public bool HasZNetView { get; set; }

		public bool HasWearNTear { get; set; }

		public bool HasPiece { get; set; }

		public bool HasBuildRecipe { get; set; }

		public bool InRequestedZone { get; set; }

		public bool AutoArchiveCandidatePiece { get; set; }

		public List<string> ExclusionReasons { get; set; } = new List<string>();

	}
	internal static class AutoArchiveScanner
	{
		private sealed class AutoArchiveZoneInfo
		{
			public Vector2i Zone { get; }

			public int PieceCount { get; set; }

			public HashSet<long> Creators { get; } = new HashSet<long>();


			public Dictionary<long, int> CreatorPieceCounts { get; } = new Dictionary<long, int>();


			public AutoArchiveZoneInfo(Vector2i zone)
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				Zone = zone;
			}

			public void AddCreator(long creator)
			{
				PieceCount++;
				Creators.Add(creator);
				CreatorPieceCounts.TryGetValue(creator, out var value);
				CreatorPieceCounts[creator] = value + 1;
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass3_0
		{
			public ZoneBundleArchiveResult saveResult;

			internal void <Run>b__4(ZoneBundleArchiveResult result)
			{
				saveResult = result;
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass3_1
		{
			public ZoneBundleResetResult resetResult;

			internal void <Run>b__5(ZoneBundleResetResult result)
			{
				resetResult = result;
			}
		}

		[CompilerGenerated]
		private sealed class <Run>d__3 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public AutoArchiveScanOptions options;

			public Action<ArchiveRunRecord> onComplete;

			private <>c__DisplayClass3_0 <>8__1;

			private <>c__DisplayClass3_1 <>8__2;

			private DateTime <utcNow>5__2;

			private ArchiveRunRecord <run>5__3;

			private Dictionary<Vector2i, AutoArchiveZoneInfo> <zoneInfos>5__4;

			private Dictionary<Vector2i, AutoArchiveZoneInfo> <candidates>5__5;

			private int <processedZones>5__6;

			private int <clusterIndex>5__7;

			private IEnumerator<List<Vector2i>> <>7__wrap7;

			private List<Vector2i> <cluster>5__9;

			private ArchiveClusterRecord <record>5__10;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <Run>d__3(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || (uint)(num - 2) <= 5u)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>8__1 = null;
				<>8__2 = null;
				<run>5__3 = null;
				<zoneInfos>5__4 = null;
				<candidates>5__5 = null;
				<>7__wrap7 = null;
				<cluster>5__9 = null;
				<record>5__10 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<utcNow>5__2 = DateTime.UtcNow;
						<run>5__3 = new ArchiveRunRecord
						{
							RunId = <utcNow>5__2.ToString("yyyyMMdd_HHmmss", CultureInfo.InvariantCulture),
							CreatedUtc = <utcNow>5__2,
							Manual = options.Manual,
							DryRun = options.DryRun,
							ResetAfterSave = options.ResetAfterSave,
							TargetPlayerIds = options.TargetPlayerIds.ToList()
						};
						if (!IsWorldReady())
						{
							<run>5__3.Messages.Add("World is not ready.");
							onComplete(<run>5__3);
							return false;
						}
						PlayerActivityTracker.TrackOnlinePlayers(<utcNow>5__2);
						<zoneInfos>5__4 = new Dictionary<Vector2i, AutoArchiveZoneInfo>();
						<>2__current = ScanZdosBySector(<utcNow>5__2, <zoneInfos>5__4, <run>5__3);
						<>1__state = 1;
						return true;
					case 1:
					{
						<>1__state = -1;
						<candidates>5__5 = BuildCandidateZones(<zoneInfos>5__4, <utcNow>5__2, options.TargetPlayerIds, <run>5__3);
						<run>5__3.CandidateZones = <candidates>5__5.Count;
						List<List<Vector2i>> source = BuildClusters(<candidates>5__5);
						<processedZones>5__6 = 0;
						<clusterIndex>5__7 = 0;
						<>7__wrap7 = (from cluster in source
							orderby cluster.Min((Vector2i zone) => zone.x), cluster.Min((Vector2i zone) => zone.y)
							select cluster).GetEnumerator();
						<>1__state = -3;
						break;
					}
					case 2:
						<>1__state = -3;
						break;
					case 3:
						<>1__state = -3;
						if (<>8__1.saveResult == null)
						{
							<record>5__10.Status = "failed";
							<record>5__10.Reason = "save failed: archive coroutine did not return a result";
							<run>5__3.Clusters.Add(<record>5__10);
							_logger.LogError((object)("Auto archive save failed for tag '" + <record>5__10.Tag + "': archive coroutine did not return a result."));
							<>2__current = null;
							<>1__state = 4;
							return true;
						}
						<record>5__10.TerrainLoaded = <>8__1.saveResult.TerrainLoaded;
						<record>5__10.TerrainCaptured = <>8__1.saveResult.TerrainCaptured;
						if (!<>8__1.saveResult.Success)
						{
							<record>5__10.Status = "failed";
							<record>5__10.Reason = <>8__1.saveResult.Message;
							<run>5__3.Clusters.Add(<record>5__10);
							break;
						}
						<record>5__10.Status = "saved";
						<record>5__10.Reason = <>8__1.saveResult.Message;
						if (options.ResetAfterSave)
						{
							if (!AutoArchiveConfig.RequireLoadedTerrainForReset || <>8__1.saveResult.TerrainCaptured >= <cluster>5__9.Count)
							{
								<>8__2 = new <>c__DisplayClass3_1();
								<>8__2.resetResult = null;
								<>2__current = ZoneBundleCommands.ResetGeneratedZonesAsync(<cluster>5__9, delegate(ZoneBundleResetResult result)
								{
									<>8__2.resetResult = result;
								});
								<>1__state = 5;
								return true;
							}
							<record>5__10.Status = "saved-reset-skipped";
							<record>5__10.Reason = $"saved, but reset skipped because usable terrain contacts were captured for {<>8__1.saveResult.TerrainCaptured}/{<cluster>5__9.Count} zones";
						}
						goto IL_08c2;
					case 4:
						<>1__state = -3;
						break;
					case 5:
						<>1__state = -3;
						if (<>8__2.resetResult == null)
						{
							<record>5__10.Status = "saved-reset-failed";
							<record>5__10.Reason = "reset failed: reset coroutine did not return a result";
							<run>5__3.Clusters.Add(<record>5__10);
							_logger.LogError((object)("Auto archive reset failed for tag '" + <record>5__10.Tag + "': reset coroutine did not return a result."));
							<>2__current = null;
							<>1__state = 6;
							return true;
						}
						<record>5__10.Status = (<>8__2.resetResult.Success ? "reset" : "saved-reset-failed");
						<record>5__10.Reason = <>8__2.resetResult.Message;
						<>8__2 = null;
						goto IL_08c2;
					case 6:
						<>1__state = -3;
						break;
					case 7:
						{
							<>1__state = -3;
							<>8__1 = null;
							<record>5__10 = null;
							<cluster>5__9 = null;
							break;
						}
						IL_08c2:
						<run>5__3.Clusters.Add(<record>5__10);
						<processedZones>5__6 += <cluster>5__9.Count;
						<>2__current = null;
						<>1__state = 7;
						return true;
					}
					while (<>7__wrap7.MoveNext())
					{
						<cluster>5__9 = <>7__wrap7.Current;
						<>8__1 = new <>c__DisplayClass3_0();
						<clusterIndex>5__7++;
						<record>5__10 = BuildClusterRecord(<cluster>5__9, <candidates>5__5);
						bool flag = <record>5__10.PieceCount < AutoArchiveConfig.MinimumPiecesPerCluster;
						if (flag && AutoArchiveConfig.SmallClusterAction == AutoArchiveSmallClusterAction.Skip)
						{
							<record>5__10.Status = "skipped";
							<record>5__10.Reason = $"piece count {<record>5__10.PieceCount} is below minimum {AutoArchiveConfig.MinimumPiecesPerCluster}";
							<run>5__3.Clusters.Add(<record>5__10);
							continue;
						}
						if (<processedZones>5__6 + <cluster>5__9.Count > AutoArchiveConfig.MaxZonesPerRun)
						{
							<record>5__10.Status = "skipped";
							<record>5__10.Reason = $"max zones per run would be exceeded ({<processedZones>5__6 + <cluster>5__9.Count}/{AutoArchiveConfig.MaxZonesPerRun})";
							<run>5__3.Clusters.Add(<record>5__10);
							continue;
						}
						if (!AllCreatorsEligible(<record>5__10.Creators, <utcNow>5__2, out string reason))
						{
							<record>5__10.Status = "skipped";
							<record>5__10.Reason = reason;
							<run>5__3.Clusters.Add(<record>5__10);
							continue;
						}
						if (flag && AutoArchiveConfig.SmallClusterAction == AutoArchiveSmallClusterAction.ResetWithoutSave)
						{
							if (!options.ResetAfterSave)
							{
								<record>5__10.Status = "skipped";
								<record>5__10.Reason = $"piece count {<record>5__10.PieceCount} is below minimum {AutoArchiveConfig.MinimumPiecesPerCluster}; reset mode is not enabled";
								<run>5__3.Clusters.Add(<record>5__10);
								continue;
							}
							if (options.DryRun)
							{
								<record>5__10.Status = "dry-run-reset-without-save";
								<record>5__10.Reason = $"small inactive cluster would be reset without saving ({<record>5__10.PieceCount}/{AutoArchiveConfig.MinimumPiecesPerCluster} pieces)";
								<run>5__3.Clusters.Add(<record>5__10);
								<processedZones>5__6 += <cluster>5__9.Count;
								continue;
							}
							ZoneBundleResetResult zoneBundleResetResult = ZoneBundleCommands.ResetGeneratedZones(<cluster>5__9);
							<record>5__10.Status = (zoneBundleResetResult.Success ? "reset-without-save" : "reset-without-save-failed");
							<record>5__10.Reason = zoneBundleResetResult.Message;
							<run>5__3.Clusters.Add(<record>5__10);
							<processedZones>5__6 += <cluster>5__9.Count;
							<>2__current = null;
							<>1__state = 2;
							return true;
						}
						<record>5__10.Tag = ZoneBundleCommands.MakeUniqueAutoArchiveTag(BuildTag(<clusterIndex>5__7, <cluster>5__9, <candidates>5__5));
						if (options.DryRun)
						{
							<record>5__10.Status = "dry-run";
							<record>5__10.Reason = "candidate only; dry run is enabled";
							<run>5__3.Clusters.Add(<record>5__10);
							<processedZones>5__6 += <cluster>5__9.Count;
							continue;
						}
						<>8__1.saveResult = null;
						<>2__current = ZoneBundleCommands.SaveZonesAsync(<cluster>5__9, <record>5__10.Tag, delegate(ZoneBundleArchiveResult result)
						{
							<>8__1.saveResult = result;
						});
						<>1__state = 3;
						return true;
					}
					<>m__Finally1();
					<>7__wrap7 = null;
					<run>5__3.ProcessedZones = <processedZones>5__6;
					onComplete(<run>5__3);
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>7__wrap7 != null)
				{
					<>7__wrap7.Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ScanZdosBySector>d__5 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public DateTime utcNow;

			public Dictionary<Vector2i, AutoArchiveZoneInfo> zoneInfos;

			public ArchiveRunRecord run;

			private int <batchSize>5__2;

			private int <processedSinceYield>5__3;

			private List<ZDO>[] <sectors>5__4;

			private int <sectorIndex>5__5;

			private List<ZDO> <sector>5__6;

			private int <objectIndex>5__7;

			private List<List<ZDO>>.Enumerator <>7__wrap7;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ScanZdosBySector>d__5(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 2)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<sectors>5__4 = null;
				<sector>5__6 = null;
				<>7__wrap7 = default(List<List<ZDO>>.Enumerator);
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					List<List<ZDO>> list;
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<batchSize>5__2 = Math.Max(1, AutoArchiveConfig.ScannerBatchSize);
						<processedSinceYield>5__3 = 0;
						<sectors>5__4 = ZDOMan.instance.m_objectsBySector;
						<sectorIndex>5__5 = 0;
						goto IL_0138;
					case 1:
						<>1__state = -1;
						goto IL_00fb;
					case 2:
						{
							<>1__state = -3;
							goto IL_022d;
						}
						IL_0138:
						if (<sectors>5__4 != null && <sectorIndex>5__5 < <sectors>5__4.Length)
						{
							<sector>5__6 = <sectors>5__4[<sectorIndex>5__5];
							if (<sector>5__6 != null && <sector>5__6.Count != 0)
							{
								<objectIndex>5__7 = 0;
								goto IL_010b;
							}
							goto IL_0128;
						}
						list = ZDOMan.instance.m_objectsByOutsideSector?.Values.ToList() ?? new List<List<ZDO>>();
						<>7__wrap7 = list.GetEnumerator();
						<>1__state = -3;
						goto IL_025a;
						IL_00fb:
						<objectIndex>5__7++;
						goto IL_010b;
						IL_0128:
						<sectorIndex>5__5++;
						goto IL_0138;
						IL_025a:
						do
						{
							if (<>7__wrap7.MoveNext())
							{
								<sector>5__6 = <>7__wrap7.Current;
								continue;
							}
							<>m__Finally1();
							<>7__wrap7 = default(List<List<ZDO>>.Enumerator);
							return false;
						}
						while (<sector>5__6 == null || <sector>5__6.Count == 0);
						<sectorIndex>5__5 = 0;
						goto IL_023d;
						IL_010b:
						if (<objectIndex>5__7 < <sector>5__6.Count)
						{
							ProcessScanZdo(<sector>5__6[<objectIndex>5__7], utcNow, zoneInfos, run);
							<processedSinceYield>5__3++;
							if (<processedSinceYield>5__3 >= <batchSize>5__2)
							{
								<processedSinceYield>5__3 = 0;
								<>2__current = null;
								<>1__state = 1;
								return true;
							}
							goto IL_00fb;
						}
						<sector>5__6 = null;
						goto IL_0128;
						IL_022d:
						<sectorIndex>5__5++;
						goto IL_023d;
						IL_023d:
						if (<sectorIndex>5__5 < <sector>5__6.Count)
						{
							ProcessScanZdo(<sector>5__6[<sectorIndex>5__5], utcNow, zoneInfos, run);
							<processedSinceYield>5__3++;
							if (<processedSinceYield>5__3 >= <batchSize>5__2)
							{
								<processedSinceYield>5__3 = 0;
								<>2__current = null;
								<>1__state = 2;
								return true;
							}
							goto IL_022d;
						}
						<sector>5__6 = null;
						goto IL_025a;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				((IDisposable)<>7__wrap7).Dispose();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly Vector2i[] NeighborOffsets = (Vector2i[])(object)new Vector2i[8]
		{
			new Vector2i(-1, -1),
			new Vector2i(0, -1),
			new Vector2i(1, -1),
			new Vector2i(-1, 0),
			new Vector2i(1, 0),
			new Vector2i(-1, 1),
			new Vector2i(0, 1),
			new Vector2i(1, 1)
		};

		private static ManualLogSource _logger = null;

		public static void Initialize(ManualLogSource logger)
		{
			_logger = logger;
		}

		[IteratorStateMachine(typeof(<Run>d__3))]
		public static IEnumerator Run(AutoArchiveScanOptions options, Action<ArchiveRunRecord> onComplete)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <Run>d__3(0)
			{
				options = options,
				onComplete = onComplete
			};
		}

		private static bool TryReadPlayerStructure(ZDO zdo, DateTime utcNow, out Vector2i zone, out long creator)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			zone = default(Vector2i);
			creator = 0L;
			if (zdo == null || !zdo.IsValid())
			{
				return false;
			}
			creator = zdo.GetLong(ZDOVars.s_creator, 0L);
			if (creator == 0L)
			{
				return false;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab());
			if ((Object)(object)prefab == (Object)null || !Object.op_Implicit((Object)(object)prefab) || (Object)(object)prefab.GetComponent<WearNTear>() == (Object)null || !ZoneBlueprintCommands.HasBuildRecipe(prefab))
			{
				return false;
			}
			string @string = zdo.GetString(ZDOVars.s_creatorName, "");
			AutoArchiveStore.RecordUnknownPlayer(creator, utcNow, @string);
			zone = ZoneSystem.GetZone(zdo.GetPosition());
			return true;
		}

		[IteratorStateMachine(typeof(<ScanZdosBySector>d__5))]
		private static IEnumerator ScanZdosBySector(DateTime utcNow, Dictionary<Vector2i, AutoArchiveZoneInfo> zoneInfos, ArchiveRunRecord run)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ScanZdosBySector>d__5(0)
			{
				utcNow = utcNow,
				zoneInfos = zoneInfos,
				run = run
			};
		}

		private static void ProcessScanZdo(ZDO zdo, DateTime utcNow, Dictionary<Vector2i, AutoArchiveZoneInfo> zoneInfos, ArchiveRunRecord run)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			run.ScannedZdos++;
			if (TryReadPlayerStructure(zdo, utcNow, out var zone, out var creator))
			{
				run.StructureZdos++;
				if (!zoneInfos.TryGetValue(zone, out AutoArchiveZoneInfo value))
				{
					value = (zoneInfos[zone] = new AutoArchiveZoneInfo(zone));
				}
				value.AddCreator(creator);
			}
		}

		private static Dictionary<Vector2i, AutoArchiveZoneInfo> BuildCandidateZones(Dictionary<Vector2i, AutoArchiveZoneInfo> zoneInfos, DateTime utcNow, IReadOnlyCollection<long> targetPlayerIds, ArchiveRunRecord run)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Vector2i, AutoArchiveZoneInfo> dictionary = new Dictionary<Vector2i, AutoArchiveZoneInfo>();
			foreach (AutoArchiveZoneInfo value in zoneInfos.Values)
			{
				if (targetPlayerIds.Count <= 0 || value.Creators.Any(((IEnumerable<long>)targetPlayerIds).Contains<long>))
				{
					if (!AllCreatorsEligible(value.Creators, utcNow, out string reason))
					{
						run.Messages.Add($"Skipped zone ({value.Zone.x},{value.Zone.y}): {reason}");
					}
					else
					{
						dictionary[value.Zone] = value;
					}
				}
			}
			return dictionary;
		}

		private static List<List<Vector2i>> BuildClusters(Dictionary<Vector2i, AutoArchiveZoneInfo> candidates)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			List<List<Vector2i>> list = new List<List<Vector2i>>();
			HashSet<Vector2i> hashSet = candidates.Keys.ToHashSet();
			Vector2i item2 = default(Vector2i);
			while (hashSet.Count > 0)
			{
				Vector2i item = hashSet.First();
				hashSet.Remove(item);
				List<Vector2i> list2 = new List<Vector2i>();
				Queue<Vector2i> queue = new Queue<Vector2i>();
				queue.Enqueue(item);
				while (queue.Count > 0)
				{
					Vector2i val = queue.Dequeue();
					list2.Add(val);
					Vector2i[] neighborOffsets = NeighborOffsets;
					foreach (Vector2i val2 in neighborOffsets)
					{
						((Vector2i)(ref item2))..ctor(val.x + val2.x, val.y + val2.y);
						if (hashSet.Contains(item2))
						{
							hashSet.Remove(item2);
							queue.Enqueue(item2);
						}
					}
				}
				list.Add(list2);
			}
			return list;
		}

		private static ArchiveClusterRecord BuildClusterRecord(List<Vector2i> cluster, Dictionary<Vector2i, AutoArchiveZoneInfo> candidates)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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)
			ArchiveClusterRecord archiveClusterRecord = new ArchiveClusterRecord
			{
				Zones = (from zone in cluster
					orderby zone.y, zone.x
					select new ZoneBundleZone
					{
						X = zone.x,
						Z = zone.y
					}).ToList()
			};
			HashSet<long> hashSet = new HashSet<long>();
			foreach (Vector2i item in cluster)
			{
				AutoArchiveZoneInfo autoArchiveZoneInfo = candidates[item];
				archiveClusterRecord.PieceCount += autoArchiveZoneInfo.PieceCount;
				foreach (long creator in autoArchiveZoneInfo.Creators)
				{
					hashSet.Add(creator);
				}
			}
			archiveClusterRecord.Creators = hashSet.OrderBy((long id) => id).ToList();
			return archiveClusterRecord;
		}

		private static bool AllCreatorsEligible(IEnumerable<long> creators, DateTime utcNow, out string reason)
		{
			foreach (long creator in creators)
			{
				if (!AutoArchiveStore.IsCreatorArchiveEligible(creator, utcNow, AutoArchiveConfig.InactiveDays, AutoArchiveConfig.UnknownOwnerGraceDays, out reason))
				{
					return false;
				}
			}
			reason = "";
			return true;
		}

		private static string BuildTag(int index, List<Vector2i> cluster, Dictionary<Vector2i, AutoArchiveZoneInfo> candidates)
		{
			string arg = BuildOwnerSegment(BuildCreatorPieceCounts(cluster, candidates));
			return $"auto_{arg}_c{index:D3}";
		}

		private static Dictionary<long, int> BuildCreatorPieceCounts(List<Vector2i> cluster, Dictionary<Vector2i, AutoArchiveZoneInfo> candidates)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<long, int> dictionary = new Dictionary<long, int>();
			foreach (Vector2i item in cluster)
			{
				foreach (KeyValuePair<long, int> creatorPieceCount in candidates[item].CreatorPieceCounts)
				{
					dictionary.TryGetValue(creatorPieceCount.Key, out var value);
					dictionary[creatorPieceCount.Key] = value + creatorPieceCount.Value;
				}
			}
			return dictionary;
		}

		private static string BuildOwnerSegment(IReadOnlyDictionary<long, int> creatorPieceCounts)
		{
			IReadOnlyDictionary<long, int> creatorPieceCounts2 = creatorPieceCounts;
			List<long> list = (from creator in creatorPieceCounts2.Keys.Where((long creator) => creator != 0).Distinct()
				orderby creator
				select creator).ToList();
			if (list.Count == 0)
			{
				return "unknown";
			}
			int value;
			string text = BuildOwnerToken((from owner in list
				orderby creatorPieceCounts2.TryGetValue(owner, out value) ? value : 0 descending, owner
				select owner).First());
			if (list.Count == 1)
			{
				return text;
			}
			return $"{text}_plus{list.Count - 1}_{BuildOwnerHash(list)}";
		}

		private static string BuildOwnerToken(long playerId)
		{
			string text = ResolvePlayerName(playerId);
			string text2 = ResolveSteamId(playerId);
			if (!string.Equals(text2, "unknown", StringComparison.Ordinal))
			{
				return text + "_s" + text2;
			}
			return text;
		}

		private static string ResolvePlayerName(long playerId)
		{
			if (!AutoArchiveStore.TryGetPlayerRecord(playerId, out PlayerActivityRecord record))
			{
				return "unknown";
			}
			return SanitizeTagToken(record.Names.LastOrDefault((string candidate) => !string.IsNullOrWhiteSpace(candidate) && !string.Equals(candidate, "unknown", StringComparison.OrdinalIgnoreCase) && !string.Equals(candidate, "manual", StringComparison.OrdinalIgnoreCase)) ?? "unknown");
		}

		private static string ResolveSteamId(long playerId)
		{
			if (!AutoArchiveStore.TryGetPlayerRecord(playerId, out PlayerActivityRecord record) || string.IsNullOrWhiteSpace(record.PlatformId) || !record.PlatformId.StartsWith("steam:", StringComparison.OrdinalIgnoreCase))
			{
				return "unknown";
			}
			string source = record.PlatformId.Substring("steam:".Length);
			string text = new string(source.Where(char.IsDigit).ToArray());
			if (text.Length >= 15)
			{
				return text;
			}
			string text2 = new string(source.Where((char character) => char.IsLetterOrDigit(character) || character == '-' || character == '_').ToArray());
			if (!string.IsNullOrWhiteSpace(text2))
			{
				return text2;
			}
			return "unknown";
		}

		private static string BuildOwnerHash(IEnumerable<long> ownerIds)
		{
			uint num = 2166136261u;
			foreach (long ownerId in ownerIds)
			{
				byte[] bytes = BitConverter.GetBytes(ownerId);
				foreach (byte b in bytes)
				{
					num ^= b;
					num *= 16777619;
				}
			}
			return num.ToString("x8", CultureInfo.InvariantCulture);
		}

		private static string SanitizeTagToken(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "unknown";
			}
			char[] invalidChars = Path.GetInvalidFileNameChars();
			string text = new string(value.Select(delegate(char character)
			{
				if (invalidChars.Contains(character))
				{
					return '_';
				}
				if (char.IsWhiteSpace(character))
				{
					return '_';
				}
				return (!char.IsLetterOrDigit(character) && character != '-' && character != '_') ? '_' : character;
			}).ToArray());
			text = text.Trim(new char[1] { '_' });
			while (text.Contains("__"))
			{
				text = text.Replace("__", "_");
			}
			if (text.Length > 32)
			{
				text = text.Substring(0, 32).Trim(new char[1] { '_' });
			}
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return "unknown";
		}

		private static bool IsWorldReady()
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZDOMan.instance != null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				return (Object)(object)ZoneSystem.instance != (Object)null;
			}
			return false;
		}

		private static string FormatDate(DateTime value)
		{
			if (!(value == DateTime.MinValue))
			{
				return value.ToString("O", CultureInfo.InvariantCulture);
			}
			return "-";
		}
	}
	internal static class AutoArchiveService
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass13_0
		{
			public ArchiveRunRecord completed;

			internal void <RunScan>b__0(ArchiveRunRecord run)
			{
				completed = run;
			}
		}

		[CompilerGenerated]
		private sealed class <RunScan>d__13 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public AutoArchiveScanOptions options;

			private <>c__DisplayClass13_0 <>8__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RunScan>d__13(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass13_0();
					<>8__1.completed = null;
					<>2__current = AutoArchiveScanner.Run(options, delegate(ArchiveRunRecord run)
					{
						<>8__1.completed = run;
					});
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (<>8__1.completed != null)
					{
						AutoArchiveStore.RecordRun(<>8__1.completed);
						AutoArchiveStore.Flush(force: true);
						_logger.LogInfo((object)$"Auto archive scan finished: {<>8__1.completed.CandidateZones} candidate zone(s), {<>8__1.completed.ProcessedZones} processed zone(s), {<>8__1.completed.Clusters.Count} cluster record(s).");
					}
					_scanRunning = false;
					_scanCoroutine = null;
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly TimeSpan TrackInterval = TimeSpan.FromMinutes(10.0);

		private static readonly TimeSpan FlushInterval = TimeSpan.FromMinutes(5.0);

		private static ManualLogSource _logger = null;

		private static bool _initialized;

		private static bool _scanRunning;

		private static DateTime _lastTrackUtc = DateTime.MinValue;

		private static DateTime _lastFlushUtc = DateTime.MinValue;

		private static Coroutine? _scanCoroutine;

		public static void Initialize(ManualLogSource logger)
		{
			if (!_initialized)
			{
				_initialized = true;
				_logger = logger;
				PlayerActivityTracker.Initialize(logger);
				AutoArchiveScanner.Initialize(logger);
			}
		}

		public static void Update()
		{
			if (!IsServerReady())
			{
				return;
			}
			DateTime utcNow = DateTime.UtcNow;
			if (utcNow - _lastTrackUtc >= TrackInterval)
			{
				_lastTrackUtc = utcNow;
				PlayerActivityTracker.TrackOnlinePlayers(utcNow);
			}
			if (utcNow - _lastFlushUtc >= FlushInterval)
			{
				_lastFlushUtc = utcNow;
				AutoArchiveStore.Flush();
			}
			if (AutoArchiveConfig.Enabled && !_scanRunning)
			{
				TimeSpan timeSpan = TimeSpan.FromMinutes(AutoArchiveConfig.ScanIntervalMinutes);
				DateTime dateTime = DateTime.SpecifyKind(AutoArchiveStore.State.LastAutoScanUtc, DateTimeKind.Utc);
				if (!(dateTime != DateTime.MinValue) || !(utcNow - dateTime < timeSpan))
				{
					QueueScan(new AutoArchiveScanOptions
					{
						Manual = false,
						DryRun = AutoArchiveConfig.DryRun,
						ResetAfterSave = AutoArchiveConfig.ResetAfterSave
					});
				}
			}
		}

		public static bool QueueManualScan(bool? dryRunOverride = null, bool? resetAfterSaveOverride = null, IEnumerable<long>? targetPlayerIds = null)
		{
			if (!IsServerReady() || _scanRunning)
			{
				return false;
			}
			QueueScan(new AutoArchiveScanOptions
			{
				Manual = true,
				DryRun = (dryRunOverride ?? AutoArchiveConfig.DryRun),
				ResetAfterSave = (resetAfterSaveOverride ?? AutoArchiveConfig.ResetAfterSave),
				TargetPlayerIds = ((targetPlayerIds == null) ? new List<long>() : new List<long>(targetPlayerIds))
			});
			return true;
		}

		public static void Shutdown()
		{
			if (_scanCoroutine != null && (Object)(object)ZoneSaviorPlugin.Instance != (Object)null)
			{
				((MonoBehaviour)ZoneSaviorPlugin.Instance).StopCoroutine(_scanCoroutine);
				_scanCoroutine = null;
			}
			_scanRunning = false;
		}

		private static void QueueScan(AutoArchiveScanOptions options)
		{
			_scanRunning = true;
			_logger.LogInfo((object)$"Starting auto archive scan (manual: {options.Manual}, dry run: {options.DryRun}, reset: {options.ResetAfterSave}, targets: {options.TargetPlayerIds.Count}).");
			_scanCoroutine = ((MonoBehaviour)ZoneSaviorPlugin.Instance).StartCoroutine(RunScan(options));
		}

		[IteratorStateMachine(typeof(<RunScan>d__13))]
		private static IEnumerator RunScan(AutoArchiveScanOptions options)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <RunScan>d__13(0)
			{
				options = options
			};
		}

		private static bool IsServerReady()
		{
			if ((Object)(object)ZoneSaviorPlugin.Instance != (Object)null && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZDOMan.instance != null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				return (Object)(object)ZoneSystem.instance != (Object)null;
			}
			return false;
		}
	}
	internal static class AutoArchiveStore
	{
		private const string UnknownPlatformPrefix = "unknown:";

		private const string ManualPlatformPrefix = "manual:";

		private const int MaxRunHistory = 50;

		private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();

		private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();

		private static readonly object Sync = new object();

		private static ManualLogSource _logger = null;

		private static bool _initialized;

		private static bool _dirty;

		private static DateTime _lastFlushUtc = DateTime.MinValue;

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

		private static readonly Dictionary<long, PlayerActivityRecord> PlayersById = new Dictionary<long, PlayerActivityRecord>();

		public static string FilePath => Path.Combine(ZoneSaviorPlugin.DataStorageFullPath, "activity.yml");

		public static AutoArchiveState State { get; private set; } = new AutoArchiveState();


		public static void Initialize(ManualLogSource logger)
		{
			if (!_initialized)
			{
				_initialized = true;
				_logger = logger;
				Load();
			}
		}

		public static void Load()
		{
			lock (Sync)
			{
				if (!File.Exists(FilePath))
				{
					State = new AutoArchiveState();
					RebuildIndexes();
					_dirty = true;
					Flush(force: true);
					return;
				}
				try
				{
					string input = File.ReadAllText(FilePath);
					State = Deserializer.Deserialize<AutoArchiveState>(input) ?? new AutoArchiveState();
					AutoArchiveState state = State;
					if (state.Players == null)
					{
						List<PlayerActivityRecord> list2 = (state.Players = new List<PlayerActivityRecord>());
					}
					state = State;
					if (state.Runs == null)
					{
						List<ArchiveRunRecord> list4 = (state.Runs = new List<ArchiveRunRecord>());
					}
					state = State;
					if (state.IgnoredPlayerIds == null)
					{
						List<long> list6 = (state.IgnoredPlayerIds = new List<long>());
					}
					RebuildIndexes();
					_dirty = false;
				}
				catch (Exception arg)
				{
					_logger.LogError((object)$"Failed to load auto archive activity file, using a blank state: {arg}");
					State = new AutoArchiveState();
					RebuildIndexes();
					_dirty = true;
				}
			}
		}

		public static void Flush(bool force = false)
		{
			lock (Sync)
			{
				if (_dirty || force)
				{
					DateTime utcNow = DateTime.UtcNow;
					if (force || !(utcNow - _lastFlushUtc < TimeSpan.FromSeconds(60.0)))
					{
						Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
						File.WriteAllText(FilePath, Serializer.Serialize(State));
						_lastFlushUtc = utcNow;
						_dirty = false;
					}
				}
			}
		}

		public static void RecordPlayerSeen(string platformId, long playerId, string name, DateTime utcNow)
		{
			if (string.IsNullOrWhiteSpace(platformId))
			{
				platformId = ((playerId != 0L) ? UnknownPlatformId(playerId) : "unknown");
			}
			lock (Sync)
			{
				PlayerActivityRecord value;
				PlayerActivityRecord playerActivityRecord = (PlayersByPlatform.TryGetValue(platformId, out value) ? value : null);
				if (playerActivityRecord == null)
				{
					playerActivityRecord = new PlayerActivityRecord
					{
						PlatformId = platformId,
						FirstSeenUtc = utcNow
					};
					State.Players.Add(playerActivityRecord);
					IndexPlayer(playerActivityRecord);
				}
				if (playerActivityRecord.FirstSeenUtc == DateTime.MinValue)
				{
					playerActivityRecord.FirstSeenUtc = utcNow;
				}
				playerActivityRecord.LastSeenUtc = utcNow;
				AddDistinct(playerActivityRecord.Names, name);
				if (playerId != 0L)
				{
					AddDistinct(playerActivityRecord.PlayerIds, playerId);
					PlayersById[playerId] = playerActivityRecord;
					MergePlayerIdRecords(playerActivityRecord, playerId);
				}
				_dirty = true;
			}
		}

		public static void RecordManualPlayerSeen(long playerId, DateTime utcNow)
		{
			lock (Sync)
			{
				PlayerActivityRecord value;
				PlayerActivityRecord playerActivityRecord = (PlayersById.TryGetValue(playerId, out value) ? value : new PlayerActivityRecord
				{
					PlatformId = ManualPlatformId(playerId),
					FirstSeenUtc = utcNow,
					PlayerIds = new List<long>(1) { playerId }
				});
				if (!State.Players.Contains(playerActivityRecord))
				{
					State.Players.Add(playerActivityRecord);
				}
				if (IsUnknown(playerActivityRecord))
				{
					playerActivityRecord.PlatformId = ManualPlatformId(playerId);
				}
				if (playerActivityRecord.FirstSeenUtc == DateTime.MinValue)
				{
					playerActivityRecord.FirstSeenUtc = utcNow;
				}
				playerActivityRecord.LastSeenUtc = utcNow;
				AddDistinct(playerActivityRecord.Names, "manual");
				RebuildPlayerIndexes();
				_dirty = true;
			}
		}

		public static void RecordUnknownPlayer(long playerId, DateTime utcNow, string observedName = "")
		{
			if (playerId == 0L)
			{
				return;
			}
			lock (Sync)
			{
				if (PlayersById.TryGetValue(playerId, out PlayerActivityRecord value))
				{
					if (AddObservedName(value, observedName))
					{
						_dirty = true;
					}
				}
				else
				{
					GetOrCreateUnknownPlayer(playerId, utcNow, observedName);
					_dirty = true;
				}
			}
		}

		public static bool TryGetPlayerRecord(long playerId, out PlayerActivityRecord record)
		{
			lock (Sync)
			{
				record = (PlayersById.TryGetValue(playerId, out PlayerActivityRecord value) ? value : null);
				return record != null;
			}
		}

		public static bool TryGetPlayerIdsBySteamId(string steamId, out List<long> playerIds, out string normalizedSteamId)
		{
			normalizedSteamId = NormalizeSteamId(steamId);
			playerIds = new List<long>();
			if (string.IsNullOrWhiteSpace(normalizedSteamId))
			{
				return false;
			}
			lock (Sync)
			{
				foreach (PlayerActivityRecord player in State.Players)
				{
					if (!TryNormalizeSteamPlatformId(player.PlatformId, out string steamId2) || !string.Equals(steamId2, normalizedSteamId, StringComparison.Ordinal))
					{
						continue;
					}
					foreach (long item in player.PlayerIds.Where((long id) => id != 0))
					{
						AddDistinct(playerIds, item);
					}
				}
			}
			return playerIds.Count > 0;
		}

		public static bool IsIgnored(long playerId)
		{
			lock (Sync)
			{
				return State.IgnoredPlayerIds.Contains(playerId);
			}
		}

		public static void SetIgnored(long playerId, bool ignored)
		{
			lock (Sync)
			{
				if (ignored)
				{
					AddDistinct(State.IgnoredPlayerIds, playerId);
				}
				else
				{
					State.IgnoredPlayerIds.Remove(playerId);
				}
				_dirty = true;
			}
		}

		public static AutoArchiveCreatorEligibility EvaluateCreatorArchiveEligibility(long playerId, DateTime utcNow, int inactiveDays, int unknownGraceDays, bool recordUnknownPlayer, string observedName = "")
		{
			AutoArchiveCreatorEligibility autoArchiveCreatorEligibility = new AutoArchiveCreatorEligibility
			{
				PlayerId = playerId,
				Ignored = (playerId != 0L && IsIgnored(playerId))
			};
			if (playerId == 0L)
			{
				autoArchiveCreatorEligibility.Reason = "creatorless";
				return autoArchiveCreatorEligibility;
			}
			if (ZoneLimitConfiguration.IsArchiveProtected(playerId, out string reason))
			{
				autoArchiveCreatorEligibility.Protected = true;
				autoArchiveCreatorEligibility.Reason = reason;
				return autoArchiveCreatorEligibility;
			}
			if (autoArchiveCreatorEligibility.Ignored)
			{
				autoArchiveCreatorEligibility.Reason = $"player {playerId} is ignored";
				return autoArchiveCreatorEligibility;
			}
			if (!TryGetPlayerRecord(playerId, out PlayerActivityRecord record))
			{
				autoArchiveCreatorEligibility.PlatformId = UnknownPlatformId(playerId);
				autoArchiveCreatorEligibility.Names = (string.IsNullOrWhiteSpace(observedName) ? new List<string>(1) { "not_recorded_in_activity" } : new List<string>(1) { observedName.Trim() });
				autoArchiveCreatorEligibility.UnknownActivityRecord = true;
				if (recordUnknownPlayer)
				{
					RecordUnknownPlayer(playerId, utcNow, observedName);
					autoArchiveCreatorEligibility.Reason = $"player {playerId} was first discovered by scanner";
				}
				else
				{
					autoArchiveCreatorEligibility.Reason = $"player {playerId} is not recorded in activity";
				}
				return autoArchiveCreatorEligibility;
			}
			autoArchiveCreatorEligibility.RecordedInActivity = true;
			autoArchiveCreatorEligibility.PlatformId = record.PlatformId;
			autoArchiveCreatorEligibility.Names = record.Names?.ToList() ?? new List<string>();
			bool flag2 = (autoArchiveCreatorEligibility.UnknownActivityRecord = IsUnknown(record));
			DateTime dateTime = (flag2 ? record.FirstSeenUtc : record.LastSeenUtc);
			int num = (flag2 ? unknownGraceDays : inactiveDays);
			if (dateTime == DateTime.MinValue)
			{
				autoArchiveCreatorEligibility.Reason = $"player {playerId} has no usable activity timestamp";
				return autoArchiveCreatorEligibility;
			}
			TimeSpan timeSpan = utcNow - DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
			if (timeSpan.TotalDays < (double)num)
			{
				autoArchiveCreatorEligibility.Reason = (flag2 ? $"unknown player {playerId} first seen {timeSpan.TotalDays:F1}/{num} days ago" : $"player {playerId} last seen {timeSpan.TotalDays:F1}/{num} days ago");
				return autoArchiveCreatorEligibility;
			}
			autoArchiveCreatorEligibility.Eligible = true;
			autoArchiveCreatorEligibility.Reason = (flag2 ? $"unknown player {playerId} grace expired" : $"player {playerId} inactive for {timeSpan.TotalDays:F1} days");
			return autoArchiveCreatorEligibility;
		}

		public static bool IsCreatorArchiveEligible(long playerId, DateTime utcNow, int inactiveDays, int unknownGraceDays, out string reason)
		{
			AutoArchiveCreatorEligibility autoArchiveCreatorEligibility = EvaluateCreatorArchiveEligibility(playerId, utcNow, inactiveDays, unknownGraceDays, recordUnknownPlayer: true);
			reason = autoArchiveCreatorEligibility.Reason;
			return autoArchiveCreatorEligibility.Eligible;
		}

		public static void RecordRun(ArchiveRunRecord run)
		{
			lock (Sync)
			{
				State.LastScanUtc = run.CreatedUtc;
				if (!run.Manual)
				{
					State.LastAutoScanUtc = run.CreatedUtc;
				}
				State.Runs.Add(run);
				if (State.Runs.Count > 50)
				{
					State.Runs.RemoveRange(0, State.Runs.Count - 50);
				}
				_dirty = true;
			}
		}

		public static void SetLastAutoScanUtc(DateTime value)
		{
			lock (Sync)
			{
				State.LastAutoScanUtc = ((value == DateTime.MinValue) ? DateTime.MinValue : DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc));
				_dirty = true;
			}
		}

		private static PlayerActivityRecord GetOrCreateUnknownPlayer(long playerId, DateTime utcNow, string observedName = "")
		{
			string text = UnknownPlatformId(playerId);
			if (PlayersByPlatform.TryGetValue(text, out PlayerActivityRecord value))
			{
				AddObservedName(value, observedName);
				return value;
			}
			List<string> list = new List<string>();
			AddObservedName(list, observedName);
			if (list.Count == 0)
			{
				list.Add("unknown");
			}
			value = new PlayerActivityRecord
			{
				PlatformId = text,
				FirstSeenUtc = utcNow,
				LastSeenUtc = DateTime.MinValue,
				PlayerIds = new List<long>(1) { playerId },
				Names = list
			};
			State.Players.Add(value);
			IndexPlayer(value);
			return value;
		}

		private static bool AddObservedName(PlayerActivityRecord record, string observedName)
		{
			return AddObservedName(record.Names, observedName);
		}

		private static bool AddObservedName(List<string> names, string observedName)
		{
			if (string.IsNullOrWhiteSpace(observedName) || string.Equals(observedName, "unknown", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string item = observedName.Trim();
			if (names.Contains(item))
			{
				return false;
			}
			names.Add(item);
			return true;
		}

		private static void MergePlayerIdRecords(PlayerActivityRecord target, long playerId)
		{
			PlayerActivityRecord target2 = target;
			foreach (PlayerActivityRecord item in State.Players.Where((PlayerActivityRecord player) => player != target2 && player.PlayerIds.Contains(playerId)).ToList())
			{
				foreach (long playerId2 in item.PlayerIds)
				{
					AddDistinct(target2.PlayerIds, playerId2);
				}
				foreach (string name in item.Names)
				{
					AddDistinct(target2.Names, name);
				}
				if (target2.FirstSeenUtc == DateTime.MinValue || item.FirstSeenUtc < target2.FirstSeenUtc)
				{
					target2.FirstSeenUtc = item.FirstSeenUtc;
				}
				State.Players.Remove(item);
			}
			RebuildPlayerIndexes();
		}

		private static void RebuildIndexes()
		{
			RebuildPlayerIndexes();
		}

		private static void RebuildPlayerIndexes()
		{