Decompiled source of SharedExploration v1.1.1

plugins/SharedExploration.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("SharedExploration")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+d033a2820f76aef7e0f9dd2fe17218f922a31337")]
[assembly: AssemblyProduct("SharedExploration")]
[assembly: AssemblyTitle("SharedExploration")]
[assembly: AssemblyVersion("1.1.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 SharedExploration
{
	[BepInPlugin("Tie.SharedExploration", "SharedExploration", "1.1.1")]
	public sealed class SharedExplorationPlugin : BaseUnityPlugin
	{
		public const string Guid = "Tie.SharedExploration";

		private const string OldGuid = "local.sharedexploration";

		public const string Version = "1.1.1";

		private const int SharedMapVersion = 3;

		private const int Header = 8;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<float> _sampleInterval;

		private ConfigEntry<float> _tableInterval;

		private ConfigEntry<float> _exploreRadius;

		private int _size;

		private float _pixelSize;

		private float _gameRadius;

		private byte[] _explored;

		private int _revision;

		private int _savedRevision;

		private string _saveFile;

		private string[] _tablePrefabs;

		private readonly Dictionary<ZDOID, (uint DataRevision, int Revision)> _tables = new Dictionary<ZDOID, (uint, int)>();

		private float _sampleTimer;

		private float _tableTimer;

		private bool _updating;

		private int _tableCount;

		private int _written;

		private void Awake()
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.Combine(Paths.ConfigPath, "local.sharedexploration.cfg");
			if (File.Exists(text) && !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
			{
				try
				{
					File.Move(text, ((BaseUnityPlugin)this).Config.ConfigFilePath);
					((BaseUnityPlugin)this).Config.Reload();
				}
				catch (IOException)
				{
				}
			}
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Add the areas players explore to every map table.");
			_sampleInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SampleIntervalSeconds", 2f, "How often player positions are recorded (the game explores every 2 s).");
			_tableInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MapTableUpdateSeconds", 30f, "How often map tables are updated with the recorded areas.");
			_exploreRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ExploreRadius", 0f, "Radius in metres revealed around each player. 0 = the game's own radius.");
			new ConsoleCommand("mapexplore", "[update] - shared exploration status; 'update' writes map tables now; use through 'server mapexplore'", new ConsoleEvent(Run), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private void Update()
		{
			if (_enabled.Value && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZDOMan.instance != null && !((Object)(object)ZNetScene.instance == (Object)null) && (_explored != null || Init()))
			{
				_sampleTimer += Time.deltaTime;
				if (_sampleTimer >= Mathf.Max(0.5f, _sampleInterval.Value))
				{
					_sampleTimer = 0f;
					Sample();
				}
				_tableTimer += Time.deltaTime;
				if (_tableTimer >= Mathf.Max(5f, _tableInterval.Value) && !_updating)
				{
					_tableTimer = 0f;
					((MonoBehaviour)this).StartCoroutine(UpdateTables());
				}
			}
		}

		private bool Init()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			_size = instance.m_textureSize;
			_pixelSize = instance.m_pixelSize;
			_gameRadius = instance.m_exploreRadius;
			_tablePrefabs = (from p in ZNetScene.instance.m_prefabs
				where (Object)(object)p != (Object)null && (Object)(object)p.GetComponent<MapTable>() != (Object)null
				select ((Object)p).name).ToArray();
			_explored = new byte[_size * _size];
			string text = string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars()));
			_saveFile = Path.Combine(Paths.ConfigPath, "SharedExploration", text + ".explored.gz");
			Load();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Map {0}x{1} px at {2} m/px, explore radius {3} m, map tables: {4}", _size, _size, _pixelSize, Radius(), string.Join(", ", _tablePrefabs)));
			return true;
		}

		private float Radius()
		{
			if (!(_exploreRadius.Value > 0f))
			{
				return _gameRadius;
			}
			return _exploreRadius.Value;
		}

		private void Sample()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!((ZDOID)(ref peer.m_characterID)).IsNone())
				{
					Explore(peer.m_refPos, Radius());
				}
			}
		}

		private void Explore(Vector3 p, float radius)
		{
			//IL_0018: 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_007d: 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)
			int num = (int)Mathf.Ceil(radius / _pixelSize);
			int num2 = _size / 2;
			int num3 = Utils.RoundToInt(p.x / _pixelSize + (float)num2);
			int num4 = Utils.RoundToInt(p.z / _pixelSize + (float)num2);
			bool flag = false;
			for (int i = num4 - num; i <= num4 + num; i++)
			{
				if (i < 0 || i >= _size)
				{
					continue;
				}
				for (int j = num3 - num; j <= num3 + num; j++)
				{
					if (j < 0 || j >= _size)
					{
						continue;
					}
					Vector2 val = new Vector2((float)(j - num3), (float)(i - num4));
					if (!(((Vector2)(ref val)).magnitude > (float)num))
					{
						int num5 = i * _size + j;
						if (_explored[num5] == 0)
						{
							_explored[num5] = 1;
							flag = true;
						}
					}
				}
			}
			if (flag)
			{
				_revision++;
			}
		}

		private IEnumerator UpdateTables()
		{
			_updating = true;
			try
			{
				List<ZDO> zdos = new List<ZDO>();
				string[] tablePrefabs = _tablePrefabs;
				foreach (string prefab in tablePrefabs)
				{
					int index = 0;
					while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative(prefab, zdos, ref index))
					{
						yield return null;
					}
				}
				_tableCount = zdos.Count;
				_written = 0;
				for (int i = 0; i < 2; i++)
				{
					foreach (ZDO item in zdos)
					{
						if (item.IsValid() && UpdateTable(item))
						{
							yield return null;
						}
					}
				}
				foreach (ZDOID item2 in _tables.Keys.Where((ZDOID id) => !zdos.Any((ZDO z) => z.m_uid == id)).ToList())
				{
					_tables.Remove(item2);
				}
				if (_written > 0)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Updated {_written} of {zdos.Count} map table(s); {Explored():N0} explored map pixels shared.");
				}
				Save();
			}
			finally
			{
				_updating = false;
			}
		}

		private bool UpdateTable(ZDO zdo)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			if (_tables.TryGetValue(zdo.m_uid, out (uint, int) value) && value.Item1 == zdo.DataRevision && value.Item2 == _revision)
			{
				return false;
			}
			int num = _size * _size;
			byte[] byteArray = zdo.GetByteArray(ZDOVars.s_data, (byte[])null);
			byte[] array;
			if (byteArray == null)
			{
				array = new byte[8 + num + 4];
				BitConverter.GetBytes(3).CopyTo(array, 0);
				BitConverter.GetBytes(num).CopyTo(array, 4);
			}
			else
			{
				array = Utils.Decompress(byteArray);
				int num2 = ((array.Length >= 8) ? BitConverter.ToInt32(array, 0) : (-1));
				int num3 = ((array.Length >= 8) ? BitConverter.ToInt32(array, 4) : (-1));
				if (num2 != 3 || num3 != num || array.Length < 8 + num)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Map table at {zdo.GetPosition():F0}: unsupported data (version {num2}, {num3} px); left unchanged.");
					_tables[zdo.m_uid] = (zdo.DataRevision, _revision);
					return true;
				}
			}
			bool flag = false;
			bool flag2 = false;
			for (int i = 0; i < num; i++)
			{
				if (array[8 + i] != 0)
				{
					if (_explored[i] == 0)
					{
						_explored[i] = 1;
						flag = true;
					}
				}
				else if (_explored[i] != 0)
				{
					array[8 + i] = 1;
					flag2 = true;
				}
			}
			if (flag)
			{
				_revision++;
			}
			if (flag2)
			{
				zdo.Set(ZDOVars.s_data, Utils.Compress(array));
				_written++;
			}
			_tables[zdo.m_uid] = (zdo.DataRevision, _revision);
			return true;
		}

		private void Load()
		{
			try
			{
				if (File.Exists(_saveFile))
				{
					byte[] array = Utils.Decompress(File.ReadAllBytes(_saveFile));
					if (array.Length != _explored.Length)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)$"{_saveFile} is for a {Mathf.Sqrt((float)array.Length):0} px map; ignored.");
						return;
					}
					array.CopyTo(_explored, 0);
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {Explored():N0} explored map pixels from {_saveFile}");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not load " + _saveFile + ": " + ex.Message));
			}
		}

		private void Save()
		{
			if (_explored == null || _savedRevision == _revision)
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(_saveFile));
				string text = _saveFile + ".tmp";
				File.WriteAllBytes(text, Utils.Compress(_explored));
				if (File.Exists(_saveFile))
				{
					File.Delete(_saveFile);
				}
				File.Move(text, _saveFile);
				_savedRevision = _revision;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not save " + _saveFile + ": " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			Save();
		}

		private int Explored()
		{
			int num = 0;
			byte[] explored = _explored;
			foreach (byte b in explored)
			{
				num += b;
			}
			return num;
		}

		private void Run(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				Print(context, "mapexplore must run on the server: type 'server mapexplore' instead.");
				return;
			}
			if (_explored == null)
			{
				Print(context, "Shared exploration has not started yet (disabled, or the world is still loading).");
				return;
			}
			if (args.Args.Skip(1).Any((string a) => a.Equals("update", StringComparison.OrdinalIgnoreCase)))
			{
				_tableTimer = float.MaxValue;
				Print(context, "Map tables will be updated within a second.");
			}
			Print(context, $"Shared exploration: {(double)Explored() * 100.0 / (double)_explored.Length:0.00}% of the map explored, {_tableCount} map table(s), updated every {_tableInterval.Value:0} s.");
		}

		private static void Print(Terminal output, string text)
		{
			object obj = ((object)output) ?? ((object)Console.instance);
			if (obj != null)
			{
				((Terminal)obj).AddString(text);
			}
		}
	}
}