Decompiled source of MapFix v0.2.1

plugins/MuckMapFix.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MuckMapFix.Model;
using UnityEngine;
using UnityEngine.AI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckMapFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Fixes Muck's blank map (M) on Linux: draws the island texture through the default UI material.")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+1911ecd07d605294f346388086fc942f4c18d4fa")]
[assembly: AssemblyProduct("Map Fix")]
[assembly: AssemblyTitle("MuckMapFix")]
[assembly: InternalsVisibleTo("MuckMapFix.Tests")]
[assembly: AssemblyVersion("0.2.1.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckMapFix
{
	[HarmonyPatch(typeof(Map), "GenerateMap")]
	internal static class GenerateMapPatch
	{
		private static readonly FieldInfo MapRenderField = AccessTools.Field(typeof(Map), "mapRender");

		[HarmonyPostfix]
		private static void Postfix(Map __instance)
		{
			try
			{
				Apply(__instance);
			}
			catch (Exception arg)
			{
				MapFixPlugin.Log.LogError((object)$"Map Fix could not redraw the map, so it stays vanilla: {arg}");
			}
		}

		private static void Apply(Map map)
		{
			bool flag = (Object)(object)map != (Object)null;
			Object val = (Object)(flag ? /*isinst with value type is only supported in some contexts*/: null);
			Material val2 = (flag ? map.mapTextureMaterial : null);
			Texture materialTexture = (((Object)(object)val2 != (Object)null) ? val2.mainTexture : null);
			MapFixChoice<Texture2D> mapFixChoice = MapFixRules.Choose<Texture2D>(MapFixPlugin.Cfg.Enabled.Value, flag, val != (Object)null, materialTexture, IsVanillaMapTexture);
			if (mapFixChoice.Skip == MapFixSkip.Disabled)
			{
				return;
			}
			if (mapFixChoice.Skip != MapFixSkip.None)
			{
				MapFixPlugin.Log.LogWarning((object)$"Map Fix left the map vanilla: {mapFixChoice.Skip}.");
				return;
			}
			PropertyInfo propertyInfo = AccessTools.Property(((object)val).GetType(), "texture");
			PropertyInfo propertyInfo2 = AccessTools.Property(((object)val).GetType(), "material");
			if (propertyInfo == null || propertyInfo2 == null)
			{
				MapFixPlugin.Log.LogWarning((object)("Map Fix left the map vanilla: " + ((object)val).GetType().FullName + " has no texture or material property."));
				return;
			}
			Texture2D texture = mapFixChoice.Texture;
			byte[] rawTextureData = texture.GetRawTextureData();
			MapFixRules.MakeOpaque(rawTextureData);
			texture.LoadRawTextureData(rawTextureData);
			((Texture)texture).filterMode = (FilterMode)0;
			texture.Apply(false);
			propertyInfo.SetValue(val, texture);
			propertyInfo2.SetValue(val, null);
			MapFixPlugin.Log.LogInfo((object)$"Map Fix drew the {((Texture)texture).width}x{((Texture)texture).height} map texture through the default UI material.");
		}

		private static bool IsVanillaMapTexture(Texture2D texture)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			if ((Object)(object)texture != (Object)null && ((Texture)texture).isReadable && (int)texture.format == 4)
			{
				return ((Texture)texture).mipmapCount == 1;
			}
			return false;
		}
	}
	internal sealed class MapFixConfig
	{
		public readonly ConfigEntry<bool> Enabled;

		public readonly ConfigEntry<bool> MobPositionFix;

		public MapFixConfig(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("General", "Enabled", true, "Draw the map (M) through the default UI material so the island shows on Linux. Off leaves the map exactly as vanilla. Only changes what this player sees.");
			MobPositionFix = cfg.Bind<bool>("Mobs", "PositionFix", true, "On a client, move each enemy all the way to the host's position, and jump it there when it is more than 4 m off. Vanilla only corrects enemies on one axis, so they drift and other players see them somewhere else. The host is unaffected.");
		}
	}
	[BepInPlugin("evan.muck.mapfix", "Map Fix", "0.2.1")]
	public class MapFixPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.mapfix";

		public const string Name = "Map Fix";

		public const string Version = "0.2.1";

		internal static ManualLogSource Log;

		internal static MapFixConfig Cfg;

		private static readonly HashSet<string> Logged = new HashSet<string>();

		private void Awake()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new MapFixConfig(((BaseUnityPlugin)this).Config);
			Harmony val = new Harmony("evan.muck.mapfix");
			try
			{
				val.CreateClassProcessor(typeof(GenerateMapPatch)).Patch();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Map Fix failed to patch Map.GenerateMap, so the map stays vanilla: {arg}");
			}
			try
			{
				val.CreateClassProcessor(typeof(MobPositionPatches)).Patch();
			}
			catch (Exception arg2)
			{
				val.Unpatch((MethodBase)AccessTools.Method(typeof(Mob), "SetPosition", (Type[])null, (Type[])null), (HarmonyPatchType)0, "evan.muck.mapfix");
				val.Unpatch((MethodBase)AccessTools.Method(typeof(Mob), "UpdateOffsetPosition", (Type[])null, (Type[])null), (HarmonyPatchType)0, "evan.muck.mapfix");
				Log.LogError((object)$"Map Fix failed to patch Mob position sync, so enemies sync as in vanilla: {arg2}");
			}
			try
			{
				val.CreateClassProcessor(typeof(StaleInteractPatch)).Patch();
			}
			catch (Exception arg3)
			{
				Log.LogError((object)$"Map Fix failed to patch DetectInteractables.Update, so the interact prompt stays vanilla: {arg3}");
			}
			Log.LogInfo((object)("Map Fix 0.2.1 loaded. Map redraw " + (Cfg.Enabled.Value ? "on" : "off") + ", enemy position fix " + (Cfg.MobPositionFix.Value ? "on" : "off") + "."));
		}

		internal static void LogOnce(string key, string message)
		{
			if (Logged.Add(key))
			{
				Log.LogError((object)message);
			}
		}
	}
	[HarmonyPatch]
	internal static class MobPositionPatches
	{
		private static readonly FieldRef<Mob, Vector3> OffsetField = AccessTools.FieldRefAccess<Mob, Vector3>("offsetPosition");

		private static readonly FieldRef<Mob, float> SyncSpeed = AccessTools.FieldRefAccess<Mob, float>("syncSpeed");

		private static bool Active
		{
			get
			{
				if (MapFixPlugin.Cfg != null && MapFixPlugin.Cfg.MobPositionFix.Value)
				{
					return !LocalClient.serverOwner;
				}
				return false;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Mob), "SetPosition")]
		private static bool SetPositionPrefix(Mob __instance, Vector3 nextPosition)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: 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_006b: 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)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				return true;
			}
			try
			{
				Vector3 val = nextPosition - ((Component)__instance).transform.position;
				if (MobSyncRules.ShouldSnap(((Vector3)(ref val)).magnitude))
				{
					NavMeshAgent agent = __instance.agent;
					if ((Object)(object)agent != (Object)null && ((Behaviour)agent).enabled && agent.isOnNavMesh)
					{
						agent.Warp(nextPosition);
					}
					else
					{
						((Component)__instance).transform.position = nextPosition;
					}
					OffsetField.Invoke(__instance) = Vector3.zero;
					return false;
				}
				OffsetField.Invoke(__instance) = val;
				return false;
			}
			catch (Exception arg)
			{
				MapFixPlugin.LogOnce("set-position", $"Map Fix: mob position sync failed, vanilla handles it. {arg}");
				return true;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Mob), "UpdateOffsetPosition")]
		private static bool UpdateOffsetPositionPrefix(Mob __instance)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_002f: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				return true;
			}
			Vector3 val = OffsetField.Invoke(__instance);
			if (val == Vector3.zero)
			{
				return false;
			}
			(Offset Move, Offset Left) tuple = MobSyncRules.Glide(new Offset(val.x, val.y, val.z), SyncSpeed.Invoke(__instance), Time.deltaTime);
			Offset item = tuple.Move;
			Offset item2 = tuple.Left;
			Transform transform = ((Component)__instance).transform;
			transform.position += new Vector3(item.X, item.Y, item.Z);
			OffsetField.Invoke(__instance) = new Vector3(item2.X, item2.Y, item2.Z);
			return false;
		}
	}
	[HarmonyPatch(typeof(DetectInteractables), "Update")]
	internal static class StaleInteractPatch
	{
		private static readonly FieldInfo CurrentCollider = AccessTools.Field(typeof(DetectInteractables), "currentCollider");

		private static void Prefix(DetectInteractables __instance)
		{
			if (__instance.currentInteractable == null)
			{
				CurrentCollider.SetValue(__instance, null);
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckMapFix";

		public const string PLUGIN_NAME = "Map Fix";

		public const string PLUGIN_VERSION = "0.2.1";
	}
}
namespace MuckMapFix.Model
{
	internal enum MapFixSkip
	{
		None,
		Disabled,
		NoMap,
		NoMapRender,
		NoTexture
	}
	internal readonly struct MapFixChoice<T> where T : class
	{
		public readonly MapFixSkip Skip;

		public readonly T Texture;

		public MapFixChoice(MapFixSkip skip, T texture)
		{
			Skip = skip;
			Texture = texture;
		}
	}
	internal static class MapFixRules
	{
		public static MapFixChoice<T> Choose<T>(bool enabled, bool hasMap, bool hasMapRender, object materialTexture, Func<T, bool> usable) where T : class
		{
			if (!enabled)
			{
				return new MapFixChoice<T>(MapFixSkip.Disabled, null);
			}
			if (!hasMap)
			{
				return new MapFixChoice<T>(MapFixSkip.NoMap, null);
			}
			if (!hasMapRender)
			{
				return new MapFixChoice<T>(MapFixSkip.NoMapRender, null);
			}
			if (!(materialTexture is T val) || !usable(val))
			{
				return new MapFixChoice<T>(MapFixSkip.NoTexture, null);
			}
			return new MapFixChoice<T>(MapFixSkip.None, val);
		}

		public static void MakeOpaque(byte[] rgba32)
		{
			for (int i = 3; i < rgba32.Length; i += 4)
			{
				rgba32[i] = byte.MaxValue;
			}
		}
	}
	internal readonly struct Offset
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z);

		public Offset(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public Offset Scale(float s)
		{
			return new Offset(X * s, Y * s, Z * s);
		}
	}
	internal static class MobSyncRules
	{
		public const float SnapDistance = 4f;

		public static bool ShouldSnap(float distance)
		{
			return distance > 4f;
		}

		public static (Offset Move, Offset Left) Glide(Offset remaining, float speed, float deltaTime)
		{
			float length = remaining.Length;
			if (length <= 0f || speed <= 0f || deltaTime <= 0f)
			{
				return (Move: new Offset(0f, 0f, 0f), Left: remaining);
			}
			float num = Math.Min(length, speed * deltaTime);
			Offset item = remaining.Scale(num / length);
			return (Move: item, Left: new Offset(remaining.X - item.X, remaining.Y - item.Y, remaining.Z - item.Z));
		}
	}
}