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 ModEnabled; internal static ConfigEntry BodyOffsetText; internal static ConfigEntry ComponentAllowList; private static string _offsetRaw; private static Vector3 _offset; internal static bool IsEnabled { get { ConfigEntry 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 config(string group, string name, T value, ConfigDescription description) { return ((BaseUnityPlugin)this).Config.Bind(group, name, value, description); } private ConfigEntry config(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())); } 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 $3B6D679A65CD5647F9CC4965B58E8041 { [SpecialName] public static class $48F04745A371B4424895B07F25A43FD8 { } [ExtensionMarker("$48F04745A371B4424895B07F25A43FD8")] public bool IsOn() { throw null; } [ExtensionMarker("$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 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(); if (!Object.op_Implicit((Object)(object)localBodyDriver)) { localBodyDriver = ((Component)player).gameObject.AddComponent(); } 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(true); Transform transform = Object.Instantiate(((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(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 dictionary = new Dictionary(); for (int i = 0; i < componentsInChildren.Length; i++) { dictionary[componentsInChildren[i]] = componentsInChildren2[i]; } Renderer[] componentsInChildren3 = ((Component)transform).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren3.Length; j++) { componentsInChildren3[j].shadowCastingMode = (ShadowCastingMode)3; } int num = 0; SkinnedMeshRenderer[] componentsInChildren4 = ((Component)val).GetComponentsInChildren(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(((Component)val2).gameObject, ((Component)val2).transform.parent).GetComponent(); 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().Follow(head); SeaLegsPlugin.SeaLegsLogger.LogInfo((object)$"Shadow head built from {num} mesh(es)"); } private static bool RidesHead(Transform[] bones, Dictionary 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? 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(); } } private static void Postfix(Player __instance) { if (Stashed != null) { List 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); } } }