Decompiled source of WorldSnapshot v1.2.1

WorldSnapshot.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("WorldSnapshot")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+57f59672b47a28000b2bf52698b9aa8e1aeb8515")]
[assembly: AssemblyProduct("WorldSnapshot")]
[assembly: AssemblyTitle("WorldSnapshot")]
[assembly: AssemblyVersion("1.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WorldSnapshot
{
	[BepInPlugin("com.iterrorbilly.worldsnapshot", "World Snapshot", "1.2.1")]
	public class WorldSnapshotPlugin : BaseUnityPlugin
	{
		public enum NameSource
		{
			ServerName,
			WorldName
		}

		public const string GUID = "com.iterrorbilly.worldsnapshot";

		public const string NAME = "World Snapshot";

		public const string VERSION = "1.2.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> DwellSeconds;

		internal static ConfigEntry<NameSource> SnapshotNameSource;

		internal static readonly HashSet<Vector2s> Captured = new HashSet<Vector2s>();

		internal static readonly Dictionary<Vector2s, float> Dwell = new Dictionary<Vector2s, float>();

		private static long s_trackedWorldUid;

		private static bool s_tracking;

		internal static HashSet<Vector2s> FilterZones;

		internal static int FilterKept;

		internal static int FilterDropped;

		private Harmony _harmony;

		private float _tick;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			DwellSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DwellSeconds", 5f, new ConfigDescription("Seconds you must stay within sync range of a zone before it counts as fully captured. Raise it for very dense bases, lower it if you are just sailing past. Change in-game with 'snapdwell <seconds>'.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			SnapshotNameSource = ((BaseUnityPlugin)this).Config.Bind<NameSource>("General", "NameSource", NameSource.ServerName, "What the default snapshot name is based on. ServerName uses the server's title from the server browser (falls back to the world name if unknown, e.g. when joining by IP). WorldName uses the server's world file name.");
			if (!Snapshot.ResolveReflection(out var missing))
			{
				Log.LogError((object)("World Snapshot disabled: game API changed, could not find " + missing));
				return;
			}
			_harmony = new Harmony("com.iterrorbilly.worldsnapshot");
			_harmony.PatchAll(typeof(WorldSnapshotPlugin).Assembly);
			Log.LogInfo((object)"World Snapshot 1.2.1 loaded. Console: snapshot [name] | snapstatus | snapmark | snapreset | snapdwell <seconds>");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static void ResetTracking()
		{
			Captured.Clear();
			Dwell.Clear();
			s_tracking = false;
			s_trackedWorldUid = 0L;
		}

		private void Update()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || ZNet.World == null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				if (s_tracking && (Object)(object)instance == (Object)null)
				{
					ResetTracking();
				}
			}
			else if (!instance.IsServer())
			{
				if (!s_tracking || ZNet.World.m_uid != s_trackedWorldUid)
				{
					ResetTracking();
					s_tracking = true;
					s_trackedWorldUid = ZNet.World.m_uid;
				}
				_tick += Time.deltaTime;
				if (!(_tick < 0.5f))
				{
					float tick = _tick;
					_tick = 0f;
					AccumulateDwell(instance, tick, forceMark: false);
				}
			}
		}

		internal static void AccumulateDwell(ZNet znet, float dt, bool forceMark)
		{
			//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_000b: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			Vector2s zone = ZoneSystem.GetZone(znet.GetReferencePosition());
			SimulationDistance syncedSimulationDistance = znet.GetSyncedSimulationDistance();
			int nearSimulationDistance = ((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance;
			float value = DwellSeconds.Value;
			Vector2s val = default(Vector2s);
			for (int i = -nearSimulationDistance; i <= nearSimulationDistance; i++)
			{
				for (int j = -nearSimulationDistance; j <= nearSimulationDistance; j++)
				{
					((Vector2s)(ref val))..ctor(zone.x + j, zone.y + i);
					if (((j == 0 && i == 0) || ((SimulationDistance)(ref syncedSimulationDistance)).IsClassic || ZoneSystem.instance.ZonesWithinRadius(zone, val, nearSimulationDistance, false)) && !Captured.Contains(val))
					{
						Dwell.TryGetValue(val, out var value2);
						value2 += dt;
						if (forceMark || value2 >= value)
						{
							Captured.Add(val);
							Dwell.Remove(val);
						}
						else
						{
							Dwell[val] = value2;
						}
					}
				}
			}
		}
	}
	internal static class Snapshot
	{
		internal static string JoinedServerName = "";

		internal static ServerJoinData JoinedServer = ServerJoinData.None;

		private static FieldInfo f_ZNet_world;

		private static MethodInfo m_ZNet_SaveWorld;

		private static FieldInfo f_World_chunkedSave;

		private static FieldInfo f_ZDOMan_chunkSaveMapping;

		private static FieldInfo f_ZDOMan_dirtyPortalObjects;

		internal static FieldInfo f_ZDOMan_saveData;

		private static FieldInfo f_ZDOMan_objectsBySector;

		private static FieldInfo f_ZoneSystem_generatedZones;

		private static FieldInfo f_ZNet_sendSaveMessage;

		internal static string GetServerName()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(JoinedServerName) && ((ServerJoinData)(ref JoinedServer)).IsValid)
			{
				try
				{
					ServerMatchmakingData serverMatchmakingData = MultiBackendMatchmaking.GetServerMatchmakingData(JoinedServer, default(DateTime));
					if (!string.IsNullOrEmpty(serverMatchmakingData.m_serverName))
					{
						JoinedServerName = serverMatchmakingData.m_serverName;
					}
				}
				catch (Exception ex)
				{
					WorldSnapshotPlugin.Log.LogWarning((object)("Server name lookup failed: " + ex.Message));
				}
			}
			return JoinedServerName ?? "";
		}

		internal static bool ResolveReflection(out string missing)
		{
			f_ZNet_world = AccessTools.Field(typeof(ZNet), "m_world");
			f_ZNet_sendSaveMessage = AccessTools.Field(typeof(ZNet), "m_sendSaveMessage");
			m_ZNet_SaveWorld = AccessTools.Method(typeof(ZNet), "SaveWorld", new Type[1] { typeof(bool) }, (Type[])null);
			f_World_chunkedSave = AccessTools.Field(typeof(World), "m_chunkedSave");
			f_ZDOMan_chunkSaveMapping = AccessTools.Field(typeof(ZDOMan), "m_chunkSaveMapping");
			f_ZDOMan_dirtyPortalObjects = AccessTools.Field(typeof(ZDOMan), "m_dirtyPortalObjects");
			f_ZDOMan_saveData = AccessTools.Field(typeof(ZDOMan), "m_saveData");
			f_ZDOMan_objectsBySector = AccessTools.Field(typeof(ZDOMan), "m_objectsBySector");
			f_ZoneSystem_generatedZones = AccessTools.Field(typeof(ZoneSystem), "m_generatedZones");
			missing = null;
			if (f_ZNet_world == null)
			{
				missing = "ZNet.m_world";
			}
			else if (m_ZNet_SaveWorld == null)
			{
				missing = "ZNet.SaveWorld(bool)";
			}
			else if (f_World_chunkedSave == null)
			{
				missing = "World.m_chunkedSave";
			}
			else if (f_ZDOMan_chunkSaveMapping == null)
			{
				missing = "ZDOMan.m_chunkSaveMapping";
			}
			else if (f_ZDOMan_dirtyPortalObjects == null)
			{
				missing = "ZDOMan.m_dirtyPortalObjects";
			}
			else if (f_ZDOMan_saveData == null)
			{
				missing = "ZDOMan.m_saveData";
			}
			else if (f_ZDOMan_objectsBySector == null)
			{
				missing = "ZDOMan.m_objectsBySector";
			}
			else if (f_ZoneSystem_generatedZones == null)
			{
				missing = "ZoneSystem.m_generatedZones";
			}
			else if (f_ZNet_sendSaveMessage == null)
			{
				missing = "ZNet.m_sendSaveMessage";
			}
			return missing == null;
		}

		private static void Say(Terminal ctx, string msg)
		{
			WorldSnapshotPlugin.Log.LogInfo((object)msg);
			if (ctx != null)
			{
				ctx.AddString("[WorldSnapshot] " + msg);
			}
		}

		private static bool CheckConnected(Terminal ctx)
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.World == null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				Say(ctx, "Not in a world. Join the server first.");
				return false;
			}
			if (ZNet.instance.IsServer())
			{
				Say(ctx, "You are hosting this world yourself, so it is already in worlds_local. Nothing to snapshot.");
				return false;
			}
			return true;
		}

		private static int CountZdosIn(ICollection<Vector2s> zones)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (!(f_ZDOMan_objectsBySector.GetValue(ZDOMan.instance) is List<ZDO>[] array))
			{
				return 0;
			}
			int num = 0;
			foreach (Vector2s zone in zones)
			{
				uint sector = ZoneSystem.SectorToIndex(zone).Sector;
				if (sector < array.Length && array[sector] != null)
				{
					num += array[sector].Count;
				}
			}
			return num;
		}

		internal static void Status(ConsoleEventArgs args)
		{
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			Terminal context = args.Context;
			if (CheckConnected(context))
			{
				World world = ZNet.World;
				int count = WorldSnapshotPlugin.Dwell.Count;
				string serverName = GetServerName();
				Say(context, string.Format("Server '{0}', world file '{1}', seed '{2}' ({3})", string.IsNullOrEmpty(serverName) ? "(name unknown)" : serverName, world.m_name, world.m_seedName, world.m_seed));
				Say(context, "Default snapshot name would be '" + DefaultName(world) + "'");
				Say(context, $"Zones captured: {WorldSnapshotPlugin.Captured.Count}  (pending: {count}, threshold {WorldSnapshotPlugin.DwellSeconds.Value:0}s)");
				Say(context, $"ZDOs held by client: {ZDOMan.instance.NrOfObjects()} total, {CountZdosIn(WorldSnapshotPlugin.Captured)} inside captured zones");
				SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance();
				Say(context, $"Sync range: {((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance} zone(s) around you");
			}
		}

		internal static void Mark(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if (CheckConnected(context))
			{
				int count = WorldSnapshotPlugin.Captured.Count;
				WorldSnapshotPlugin.AccumulateDwell(ZNet.instance, 0f, forceMark: true);
				Say(context, $"Marked {WorldSnapshotPlugin.Captured.Count - count} zone(s) around you as captured ({WorldSnapshotPlugin.Captured.Count} total).");
			}
		}

		internal static void Reset(ConsoleEventArgs args)
		{
			WorldSnapshotPlugin.ResetTracking();
			Say(args.Context, "Captured zone list cleared.");
		}

		internal static void SetDwell(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			ConfigEntry<float> dwellSeconds = WorldSnapshotPlugin.DwellSeconds;
			if (args.Length < 2)
			{
				Say(context, $"Dwell time is {dwellSeconds.Value:0.#} s. Usage: snapdwell <seconds>  (0 to 600, decimals allowed, default 5)");
				return;
			}
			if (!float.TryParse(args.Args[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || result < 0f || result > 600f)
			{
				Say(context, "'" + args.Args[1] + "' is not valid. Use a number of seconds from 0 to 600, for example: snapdwell 5");
				return;
			}
			dwellSeconds.Value = result;
			Say(context, $"Dwell time set to {result:0.#} s and saved to the config. Zones already pending keep their progress.");
		}

		private static bool WorldExists(string name)
		{
			string worldsSaveRootPath = SaveSystem.GetWorldsSaveRootPath((FileSource)2);
			string saveDirectory = World.GetSaveDirectory((FileSource)2, name);
			if ((!Directory.Exists(saveDirectory) || !Directory.EnumerateFileSystemEntries(saveDirectory).Any()) && !File.Exists(Path.Combine(worldsSaveRootPath, name + ".fwl")))
			{
				return File.Exists(Path.Combine(worldsSaveRootPath, name + ".db"));
			}
			return true;
		}

		private static string Sanitize(string s)
		{
			char[] bad = Path.GetInvalidFileNameChars();
			return new string((from c in s.Trim()
				select (!bad.Contains(c)) ? c : '_').ToArray()).Trim();
		}

		private static string DefaultName(World src)
		{
			string text = "";
			if (WorldSnapshotPlugin.SnapshotNameSource.Value == WorldSnapshotPlugin.NameSource.ServerName)
			{
				text = Sanitize(GetServerName());
			}
			if (string.IsNullOrEmpty(text))
			{
				text = Sanitize(src.m_name);
			}
			return text + "-snapshot";
		}

		internal static void Run(ConsoleEventArgs args)
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Invalid comparison between Unknown and I4
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Expected O, but got Unknown
			Terminal context = args.Context;
			if (!CheckConnected(context))
			{
				return;
			}
			if (ZNet.instance.IsSaving())
			{
				Say(context, "A save is already running, try again in a moment.");
				return;
			}
			if ((Object)(object)PersistentEventSystem.instance == (Object)null || (Object)(object)RandEventSystem.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				Say(context, "Game systems not ready yet.");
				return;
			}
			HashSet<Vector2s> hashSet = new HashSet<Vector2s>(WorldSnapshotPlugin.Captured);
			if (hashSet.Count == 0)
			{
				Say(context, "No zones captured yet. Stand around for a bit, or run 'snapmark' to force the zones around you.");
				return;
			}
			World world = ZNet.World;
			bool flag = args.Length > 1;
			string text = (flag ? Sanitize(string.Join(" ", args.Args.Skip(1))) : DefaultName(world));
			if (string.IsNullOrEmpty(text))
			{
				Say(context, "Invalid name.");
				return;
			}
			if (WorldExists(text))
			{
				if (flag)
				{
					Say(context, "A world named '" + text + "' already exists in worlds_local. Pick another name: snapshot <name>");
					return;
				}
				string text2 = text;
				int num = 2;
				while (WorldExists(text))
				{
					text = text2 + "-" + num;
					num++;
				}
			}
			string dir = World.GetSaveDirectory((FileSource)2, text);
			World val = new World
			{
				m_worldName = text,
				m_name = text,
				m_seedName = world.m_seedName,
				m_seed = world.m_seed,
				m_uid = world.m_uid,
				m_worldGenVersion = world.m_worldGenVersion,
				m_fileSource = (FileSource)2,
				m_needsDB = true,
				m_worldVersion = (World)42
			};
			f_World_chunkedSave.SetValue(val, true);
			HashSet<string> hashSet2 = new HashSet<string>(world.m_startingGlobalKeys.Select((string k) => k.ToLowerInvariant()));
			string text3 = default(string);
			GlobalKeys val2 = default(GlobalKeys);
			foreach (string globalKey in ZoneSystem.instance.GetGlobalKeys())
			{
				ZoneSystem.GetKeyValue(globalKey, ref text3, ref val2);
				if ((int)val2 < 41)
				{
					hashSet2.Add(globalKey.ToLowerInvariant());
				}
			}
			val.m_startingGlobalKeys.AddRange(hashSet2);
			ZDOMan instance = ZDOMan.instance;
			ZoneSystem instance2 = ZoneSystem.instance;
			f_ZDOMan_chunkSaveMapping.SetValue(instance, (object?)new ChunkSaveMapping());
			if (f_ZDOMan_dirtyPortalObjects.GetValue(instance) is bool[] array)
			{
				for (int num2 = 0; num2 < array.Length; num2++)
				{
					array[num2] = true;
				}
			}
			HashSet<Vector2s> hashSet3 = (HashSet<Vector2s>)f_ZoneSystem_generatedZones.GetValue(instance2);
			HashSet<Vector2s> other = new HashSet<Vector2s>(hashSet3);
			hashSet3.UnionWith(hashSet);
			uint saveNumber = SaveSystem.GetSaveNumber();
			SaveSystem.SetSaveNumber(0u);
			object value = f_ZNet_world.GetValue(null);
			f_ZNet_world.SetValue(null, val);
			WorldSnapshotPlugin.FilterZones = hashSet;
			WorldSnapshotPlugin.FilterKept = 0;
			WorldSnapshotPlugin.FilterDropped = 0;
			Say(context, $"Writing snapshot '{text}' from {hashSet.Count} zone(s)...");
			Exception ex = null;
			Stopwatch stopwatch = Stopwatch.StartNew();
			try
			{
				m_ZNet_SaveWorld.Invoke(ZNet.instance, new object[1] { true });
			}
			catch (TargetInvocationException ex2)
			{
				ex = ex2.InnerException ?? ex2;
			}
			catch (Exception ex3)
			{
				ex = ex3;
			}
			finally
			{
				WorldSnapshotPlugin.FilterZones = null;
				f_ZNet_world.SetValue(null, value);
				f_ZNet_sendSaveMessage.SetValue(ZNet.instance, 0f);
				try
				{
					ZNet.WorldSaveFinished?.Invoke();
				}
				catch (Exception ex4)
				{
					WorldSnapshotPlugin.Log.LogWarning((object)("WorldSaveFinished listener threw: " + ex4.Message));
				}
				hashSet3.Clear();
				hashSet3.UnionWith(other);
				SaveSystem.SetSaveNumber(saveNumber);
			}
			if (ex != null)
			{
				Say(context, "Snapshot FAILED: " + ex.GetType().Name + ": " + ex.Message);
				WorldSnapshotPlugin.Log.LogError((object)ex);
				return;
			}
			List<string> list = new string[4] { "_main.1.fwl2", "_main.1.db2", "_main.1.chunks", "_main.1.ok" }.Where((string f) => !File.Exists(Path.Combine(dir, f))).ToList();
			int num3 = (Directory.Exists(dir) ? Directory.GetFiles(dir, "*.chunk").Length : 0);
			if (list.Count > 0)
			{
				Say(context, "Snapshot wrote to " + dir + " but these files are missing: " + string.Join(", ", list) + ". Check the Player.log for 'Error saving world'.");
				return;
			}
			Say(context, $"Done in {stopwatch.ElapsedMilliseconds} ms: {WorldSnapshotPlugin.FilterKept} ZDOs kept in {hashSet.Count} zones, {WorldSnapshotPlugin.FilterDropped} dropped (outside captured zones), {num3} chunk file(s).");
			Say(context, "Saved to " + dir);
			Say(context, "Host it with: valheim_server.exe -world \"" + text + "\"  (or pick '" + text + "' in Start Game > Select World)");
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "JoinServer")]
	internal static class FejdStartup_JoinServer_Patch
	{
		private static readonly FieldInfo s_joinServer = AccessTools.Field(typeof(FejdStartup), "m_joinServer");

		private static void Prefix(FejdStartup __instance)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (s_joinServer == null)
				{
					return;
				}
				ServerJoinData val = (ServerJoinData)s_joinServer.GetValue(__instance);
				if (((ServerJoinData)(ref val)).IsValid)
				{
					Snapshot.JoinedServer = val;
					Snapshot.JoinedServerName = "";
					ServerMatchmakingData serverMatchmakingData = MultiBackendMatchmaking.GetServerMatchmakingData(val, default(DateTime));
					if (!string.IsNullOrEmpty(serverMatchmakingData.m_serverName))
					{
						Snapshot.JoinedServerName = serverMatchmakingData.m_serverName;
						WorldSnapshotPlugin.Log.LogInfo((object)("Joining server '" + serverMatchmakingData.m_serverName + "'"));
					}
				}
			}
			catch (Exception ex)
			{
				WorldSnapshotPlugin.Log.LogWarning((object)("Could not read the server name at join time: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class Terminal_InitTerminal_Patch
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Run;

			public static ConsoleEvent <1>__Status;

			public static ConsoleEvent <2>__Mark;

			public static ConsoleEvent <3>__Reset;

			public static ConsoleEvent <4>__SetDwell;
		}

		private static bool s_registered;

		private static void Postfix()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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_0068: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			if (!s_registered)
			{
				s_registered = true;
				object obj = <>O.<0>__Run;
				if (obj == null)
				{
					ConsoleEvent val = Snapshot.Run;
					<>O.<0>__Run = val;
					obj = (object)val;
				}
				new ConsoleCommand("snapshot", "[name] - World Snapshot: save the zones captured from this server as a new local world", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj2 = <>O.<1>__Status;
				if (obj2 == null)
				{
					ConsoleEvent val2 = Snapshot.Status;
					<>O.<1>__Status = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("snapstatus", "World Snapshot: show captured zones and ZDO counts", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj3 = <>O.<2>__Mark;
				if (obj3 == null)
				{
					ConsoleEvent val3 = Snapshot.Mark;
					<>O.<2>__Mark = val3;
					obj3 = (object)val3;
				}
				new ConsoleCommand("snapmark", "World Snapshot: mark the zones around you as captured right now", (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj4 = <>O.<3>__Reset;
				if (obj4 == null)
				{
					ConsoleEvent val4 = Snapshot.Reset;
					<>O.<3>__Reset = val4;
					obj4 = (object)val4;
				}
				new ConsoleCommand("snapreset", "World Snapshot: forget all captured zones", (ConsoleEvent)obj4, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj5 = <>O.<4>__SetDwell;
				if (obj5 == null)
				{
					ConsoleEvent val5 = Snapshot.SetDwell;
					<>O.<4>__SetDwell = val5;
					obj5 = (object)val5;
				}
				new ConsoleCommand("snapdwell", "[seconds 0-600] - World Snapshot: show or set how long a zone must stay in range to count as captured (default 5)", (ConsoleEvent)obj5, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "PrepareSave")]
	internal static class ZDOMan_PrepareSave_Patch
	{
		private static FieldInfo s_objectsByChunk;

		private static void Postfix(ZDOMan __instance)
		{
			HashSet<Vector2s> zones = WorldSnapshotPlugin.FilterZones;
			if (zones == null)
			{
				return;
			}
			object value = Snapshot.f_ZDOMan_saveData.GetValue(__instance);
			if (value == null)
			{
				return;
			}
			if (s_objectsByChunk == null)
			{
				s_objectsByChunk = AccessTools.Field(value.GetType(), "m_objectsByChunk");
			}
			if (!(s_objectsByChunk?.GetValue(value) is List<Tuple<ChunkIndex, List<ZDO>>> list))
			{
				WorldSnapshotPlugin.Log.LogError((object)"Could not reach ZDOMan.SaveData.m_objectsByChunk; snapshot will contain ALL zones.");
				return;
			}
			int kept = 0;
			int dropped = 0;
			foreach (Tuple<ChunkIndex, List<ZDO>> item in list)
			{
				item.Item2.RemoveAll(delegate(ZDO zdo)
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					if (zones.Contains(zdo.GetSector()))
					{
						kept++;
						return false;
					}
					dropped++;
					zdo.Reset();
					return true;
				});
			}
			list.RemoveAll((Tuple<ChunkIndex, List<ZDO>> c) => c.Item2.Count == 0);
			WorldSnapshotPlugin.FilterKept = kept;
			WorldSnapshotPlugin.FilterDropped = dropped;
		}
	}
}