Decompiled source of SeaLegs v1.0.0

SeaLegs.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SeaLegs")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Azumatt")]
[assembly: AssemblyProduct("SeaLegs")]
[assembly: AssemblyCopyright("Copyright ©  2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B24BE9E0-1351-4951-AE19-B645B64CA6C5")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
	[CompilerGenerated]
	[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
	{
		public ExtensionMarkerAttribute(string name)
		{
		}
	}
}
namespace SeaLegs
{
	internal sealed class LocalBodyDriver : MonoBehaviour
	{
		private Player _player;

		private OtherPlayer _other;

		private bool _wasOnBoat;

		internal void Init(Player player)
		{
			_player = player;
			_other = player.Other;
			_wasOnBoat = player.Movement.OnBoat;
			Sync();
		}

		private void Update()
		{
			if (Object.op_Implicit((Object)(object)_player) && Object.op_Implicit((Object)(object)_other) && SeaLegsPlugin.IsEnabled)
			{
				Sync();
			}
		}

		private void Sync()
		{
			//IL_0075: 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)
			//IL_0080: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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)
			//IL_00b7: 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_00dd: 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)
			if (Object.op_Implicit((Object)(object)_player.CamObject) && Object.op_Implicit((Object)(object)_player.Transform) && Object.op_Implicit((Object)(object)_other._transform))
			{
				bool onBoat = _player.Movement.OnBoat;
				if (onBoat != _wasOnBoat)
				{
					_wasOnBoat = onBoat;
					ClearVelSamples();
				}
				_other.OnBoat = onBoat;
				Vector3 localEulerAngles = _player.CamObject.localEulerAngles;
				Quaternion val = Quaternion.Euler(0f, localEulerAngles.y, 0f);
				_other._transform.position = _player.Transform.position + val * SeaLegsPlugin.BodyOffset;
				_other._transform.rotation = val;
				_other._camProxy.localRotation = Quaternion.Euler(localEulerAngles.x, 0f, 0f);
			}
		}

		private void ClearVelSamples()
		{
			_other._velSamples.Clear();
		}
	}
	internal sealed class ShadowHeadFollow : MonoBehaviour
	{
		private Transform _head;

		internal void Follow(Transform head)
		{
			_head = head;
		}

		private void LateUpdate()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_head))
			{
				((Component)this).transform.localPosition = _head.localPosition;
				((Component)this).transform.localRotation = _head.localRotation;
			}
		}
	}
	[BepInPlugin("Azumatt.SeaLegs", "SeaLegs", "1.0.0")]
	public class SeaLegsPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		internal const string ModName = "SeaLegs";

		internal const string ModVersion = "1.0.0";

		internal const string Author = "Azumatt";

		private const string ModGUID = "Azumatt.SeaLegs";

		private static string ConfigFileName = "Azumatt.SeaLegs.cfg";

		private static string ConfigFileFullPath;

		private readonly Harmony _harmony = new Harmony("Azumatt.SeaLegs");

		public static readonly ManualLogSource SeaLegsLogger;

		internal static ConfigEntry<Toggle> ModEnabled;

		internal static ConfigEntry<string> BodyOffsetText;

		internal static ConfigEntry<string> ComponentAllowList;

		private static string _offsetRaw;

		private static Vector3 _offset;

		internal static bool IsEnabled
		{
			get
			{
				ConfigEntry<Toggle> modEnabled = ModEnabled;
				if (modEnabled != null)
				{
					return modEnabled.Value == Toggle.On;
				}
				return false;
			}
		}

		internal static Vector3 BodyOffset
		{
			get
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: 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_0018: Unknown result type (might be due to invalid IL or missing references)
				string value = BodyOffsetText.Value;
				if (value == _offsetRaw)
				{
					return _offset;
				}
				_offsetRaw = value;
				_offset = ParseVector(value);
				return _offset;
			}
		}

		public void Awake()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			ModEnabled = config("1 - General", "Enabled", Toggle.On, "Turn the whole mod off without unloading it. Takes effect next time you load into a world.");
			BodyOffsetText = config("2 - Body", "Body Offset", "0, 0, -0.2", "x, y, z nudge for the whole body, in body space, metres. Positive z is the way you are facing. The default sits it back far enough that you are not standing in your own collar. Big values not recommended.");
			ComponentAllowList = config("3 - Advanced", "Components To Enable", "OtherPlayer,PlayerBody,PlayerLegs,PlayerArms,IK", "Comma separated. These are the components the game switches off on your own player. Anything not in this list stays off.");
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			SetupWatcher();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
				((BaseUnityPlugin)this).Config.Save();
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.Save();
		}

		private void SetupWatcher()
		{
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			fileSystemWatcher.Changed += ReadConfigValues;
			fileSystemWatcher.Created += ReadConfigValues;
			fileSystemWatcher.Renamed += ReadConfigValues;
			fileSystemWatcher.IncludeSubdirectories = true;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(ConfigFileFullPath))
			{
				return;
			}
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			catch
			{
				SeaLegsLogger.LogError((object)("There was an issue loading your " + ConfigFileName));
				SeaLegsLogger.LogError((object)"Please check your config entries for spelling and format!");
			}
		}

		internal static bool IsLocal(Player? player)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)Player.LocalPlayer != (Object)null)
			{
				return (Object)(object)player == (Object)(object)Player.LocalPlayer;
			}
			return false;
		}

		private static Vector3 ParseVector(string raw)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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)
			string[] array = raw.Split(new char[1] { ',' });
			Vector3 zero = Vector3.zero;
			for (int i = 0; i < 3 && i < array.Length; i++)
			{
				if (float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					((Vector3)(ref zero))[i] = result;
				}
			}
			return zero;
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description)
		{
			return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description);
		}

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

		static SeaLegsPlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			SeaLegsLogger = Logger.CreateLogSource("SeaLegs");
			ModEnabled = null;
			BodyOffsetText = null;
			ComponentAllowList = null;
			_offsetRaw = null;
		}
	}
	public static class ToggleExtensions
	{
		[SpecialName]
		public sealed class <G>$3B6D679A65CD5647F9CC4965B58E8041
		{
			[SpecialName]
			public static class <M>$48F04745A371B4424895B07F25A43FD8
			{
			}

			[ExtensionMarker("<M>$48F04745A371B4424895B07F25A43FD8")]
			public bool IsOn()
			{
				throw null;
			}

			[ExtensionMarker("<M>$48F04745A371B4424895B07F25A43FD8")]
			public bool IsOff()
			{
				throw null;
			}
		}

		public static bool IsOn(this SeaLegsPlugin.Toggle toggle)
		{
			return toggle == SeaLegsPlugin.Toggle.On;
		}

		public static bool IsOff(this SeaLegsPlugin.Toggle toggle)
		{
			return toggle == SeaLegsPlugin.Toggle.Off;
		}
	}
	internal static class LocalBody
	{
		internal static bool Active { get; private set; }

		internal static void Build(Player player, List<GameObject> bodyObjects)
		{
			try
			{
				foreach (GameObject bodyObject in bodyObjects)
				{
					if (Object.op_Implicit((Object)(object)bodyObject))
					{
						bodyObject.SetActive(true);
					}
				}
				EnableAllowedComponents(player);
				player.Skin.InitializeOther();
				player.Body.ToggleOldModel(player._isBean.Value);
				OtherPlayer other = player.Other;
				if (Object.op_Implicit((Object)(object)other._col))
				{
					((Collider)other._col).enabled = false;
				}
				ApplyHead(player);
				if (Object.op_Implicit((Object)(object)player.Body._nameTextCanvasHolder))
				{
					((Component)player.Body._nameTextCanvasHolder).gameObject.SetActive(false);
				}
				if (Object.op_Implicit((Object)(object)other._otherPlayerCanvasHolder))
				{
					((Component)other._otherPlayerCanvasHolder).gameObject.SetActive(false);
				}
				LocalBodyDriver localBodyDriver = ((Component)player).gameObject.GetComponent<LocalBodyDriver>();
				if (!Object.op_Implicit((Object)(object)localBodyDriver))
				{
					localBodyDriver = ((Component)player).gameObject.AddComponent<LocalBodyDriver>();
				}
				localBodyDriver.Init(player);
				Active = true;
				SeaLegsPlugin.SeaLegsLogger.LogInfo((object)$"Body restored, {bodyObjects.Count} object(s) kept alive");
			}
			catch (Exception arg)
			{
				Active = false;
				SeaLegsPlugin.SeaLegsLogger.LogError((object)$"Failed to restore the local body: {arg}");
			}
		}

		internal static void Clear()
		{
			Active = false;
		}

		private static void ApplyHead(Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Transform head = player.Body.Head;
			if (Object.op_Implicit((Object)(object)head))
			{
				BuildShadowHead(player, head);
				head.localScale = Vector3.zero;
			}
		}

		private static void BuildShadowHead(Player player, Transform head)
		{
			//IL_0052: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (Object.op_Implicit((Object)(object)player.Other._transform) ? player.Other._transform : head.root);
			Transform[] componentsInChildren = ((Component)head).GetComponentsInChildren<Transform>(true);
			Transform transform = Object.Instantiate<GameObject>(((Component)head).gameObject, head.parent).transform;
			((Object)transform).name = "SeaLegsShadowHead";
			transform.localPosition = head.localPosition;
			transform.localRotation = head.localRotation;
			transform.localScale = Vector3.one;
			Transform[] componentsInChildren2 = ((Component)transform).GetComponentsInChildren<Transform>(true);
			if (componentsInChildren2.Length != componentsInChildren.Length)
			{
				Object.Destroy((Object)(object)((Component)transform).gameObject);
				SeaLegsPlugin.SeaLegsLogger.LogWarning((object)$"Shadow head came out with {componentsInChildren2.Length} bones against the real {componentsInChildren.Length}. Skipping it.");
				return;
			}
			Dictionary<Transform, Transform> dictionary = new Dictionary<Transform, Transform>();
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				dictionary[componentsInChildren[i]] = componentsInChildren2[i];
			}
			Renderer[] componentsInChildren3 = ((Component)transform).GetComponentsInChildren<Renderer>(true);
			for (int j = 0; j < componentsInChildren3.Length; j++)
			{
				componentsInChildren3[j].shadowCastingMode = (ShadowCastingMode)3;
			}
			int num = 0;
			SkinnedMeshRenderer[] componentsInChildren4 = ((Component)val).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val2 in componentsInChildren4)
			{
				if (!Object.op_Implicit((Object)(object)val2.sharedMesh) || val2.bones == null || !RidesHead(val2.bones, dictionary))
				{
					continue;
				}
				SkinnedMeshRenderer component = Object.Instantiate<GameObject>(((Component)val2).gameObject, ((Component)val2).transform.parent).GetComponent<SkinnedMeshRenderer>();
				if (!Object.op_Implicit((Object)(object)component))
				{
					continue;
				}
				((Object)component).name = ((Object)val2).name + " (SeaLegs shadow)";
				Transform[] bones = component.bones;
				for (int k = 0; k < bones.Length; k++)
				{
					if (Object.op_Implicit((Object)(object)bones[k]) && dictionary.TryGetValue(bones[k], out var value))
					{
						bones[k] = value;
					}
				}
				component.bones = bones;
				if (Object.op_Implicit((Object)(object)component.rootBone) && dictionary.TryGetValue(component.rootBone, out var value2))
				{
					component.rootBone = value2;
				}
				((Renderer)component).shadowCastingMode = (ShadowCastingMode)3;
				num++;
			}
			((Component)transform).gameObject.AddComponent<ShadowHeadFollow>().Follow(head);
			SeaLegsPlugin.SeaLegsLogger.LogInfo((object)$"Shadow head built from {num} mesh(es)");
		}

		private static bool RidesHead(Transform[] bones, Dictionary<Transform, Transform> map)
		{
			foreach (Transform val in bones)
			{
				if (Object.op_Implicit((Object)(object)val) && map.ContainsKey(val))
				{
					return true;
				}
			}
			return false;
		}

		private static void EnableAllowedComponents(Player player)
		{
			string[] array = SeaLegsPlugin.ComponentAllowList.Value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = array[i].Trim();
			}
			MonoBehaviour[] disableWhenLocal = player._disableWhenLocal;
			foreach (MonoBehaviour val in disableWhenLocal)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				string name = ((object)val).GetType().Name;
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (text.Length != 0 && string.Equals(text, name, StringComparison.OrdinalIgnoreCase))
					{
						((Behaviour)val).enabled = true;
						break;
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "InitializePlayer")]
	internal static class SaveBodyFromTheDestroyLoop
	{
		internal static List<GameObject>? Stashed { get; private set; }

		private static void Prefix(Player __instance)
		{
			Stashed = null;
			if (SeaLegsPlugin.IsEnabled && ((NetworkBehaviour)__instance).Owner.IsLocalClient)
			{
				Stashed = __instance._otherObjects;
				__instance._otherObjects = new List<GameObject>();
			}
		}

		private static void Postfix(Player __instance)
		{
			if (Stashed != null)
			{
				List<GameObject> stashed = Stashed;
				Stashed = null;
				__instance._otherObjects = stashed;
				LocalBody.Build(__instance, stashed);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnStopClient")]
	internal static class ForgetBodyOnDisconnect
	{
		private static void Postfix(Player __instance)
		{
			if (SeaLegsPlugin.IsLocal(__instance))
			{
				LocalBody.Clear();
			}
		}
	}
	[HarmonyPatch(typeof(PlayerLegs), "AnimateStep")]
	internal static class FlagTheBodysFootstep
	{
		internal static bool Stepping;

		private static void Prefix(PlayerLegs __instance)
		{
			Stepping = SeaLegsPlugin.IsLocal(__instance._player);
		}

		private static void Postfix()
		{
			Stepping = false;
		}
	}
	[HarmonyPatch(typeof(AudioManager), "PlayRandomPlayerClip")]
	internal static class DropTheDoubledFootstep
	{
		private static bool Prefix(Player player)
		{
			if (FlagTheBodysFootstep.Stepping)
			{
				return !SeaLegsPlugin.IsLocal(player);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(OtherPlayer), "WaterCheck")]
	internal static class DropTheDoubledSplash
	{
		private static bool Prefix(OtherPlayer __instance)
		{
			return !SeaLegsPlugin.IsLocal(__instance._player);
		}
	}
}