The BepInEx console will not appear when launching like it does for other games on Thunderstore. This is normal (and helps prevent crashes during startup). You can turn it back on in your BepInEx.cfg file.
Decompiled source of Piggyback v1.3.0
Piggyback.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; using UnityEngine.InputSystem; using Zorro.Core.Serizalization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("nakazora.peak.piggyback")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0+c7f0efc83d30f0367185620f0c08bb86f31b537b")] [assembly: AssemblyProduct("Piggyback")] [assembly: AssemblyTitle("nakazora.peak.piggyback")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 Piggyback { public static class ExpressionUtils { public static Func<T, TResult> CreateFieldGetter<T, TResult>(string fieldName) { ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "instance"); return Expression.Lambda<Func<T, TResult>>(Expression.Field(parameterExpression, fieldName), new ParameterExpression[1] { parameterExpression }).Compile(); } } [BepInPlugin("nakazora.peak.piggyback", "Piggyback", "1.3.0")] public class Piggyback : BaseUnityPlugin { private class CanBeCarriedPatch { [HarmonyPatch(typeof(CharacterInteractible), "CanBeCarried")] [HarmonyPostfix] private static void CanBeCarriedPostfix(ref bool __result, Character ___character) { if (__result || ___character.IsLocal || ___character.data.dead) { return; } if (s_swapBackpackSetting.Value) { if (Player.localPlayer.backpackSlot.hasBackpack && ___character.player.backpackSlot.hasBackpack) { return; } } else if (Player.localPlayer.backpackSlot.hasBackpack) { return; } if (!Character.localCharacter.data.isClimbingAnything && !Object.op_Implicit((Object)(object)___character.data.currentItem) && !IsCharacterDoingIllegalCarryActions(___character) && !Object.op_Implicit((Object)(object)___character.data.carriedPlayer) && !Object.op_Implicit((Object)(object)___character.data.carrier) && (!Object.op_Implicit((Object)(object)Character.localCharacter.data.currentItem) || !Character.localCharacter.data.currentItem.canUseOnFriend)) { __result = true; } } [HarmonyPatch(typeof(CharacterInteractible), "IsInteractible")] [HarmonyPostfix] private static bool IsInteractablePostfix(bool originalResult, CharacterInteractible __instance, Character interactor) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (__instance.character.data.fullyPassedOut) { return originalResult; } if (Vector3.Distance(interactor.Center, __instance.character.Center) > 2f) { return __instance.IsSecondaryInteractible(interactor); } return originalResult; } [HarmonyPatch(typeof(CharacterCarrying), "Update")] [HarmonyPrefix] private static bool CharacterCarryingUpdatePrefix(Character ___character) { if (!___character.refs.view.IsMine) { return true; } if (!Object.op_Implicit((Object)(object)___character.data.carriedPlayer)) { return true; } if (Object.op_Implicit((Object)(object)___character.data.carriedPlayer.data.currentItem)) { return true; } if (IsCharacterDoingIllegalCarryActions(___character.data.carriedPlayer)) { return true; } if (!___character.data.carriedPlayer.data.dead && !___character.input.selectBackpackWasPressed && !___character.data.fullyPassedOut && !___character.data.dead) { return false; } return true; } [HarmonyPatch(typeof(CharacterCarrying), "CarrierGone")] [HarmonyPostfix] private static void CharacterCarryingCarrierGonePostfix(Character ___character) { ___character.data.isCarried = false; } [HarmonyPatch(typeof(CharacterInput), "Sample")] [HarmonyPostfix] private static void CharacterInputSamplePostfix(CharacterInput __instance) { //IL_0024: 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) if (!Character.localCharacter.data.fullyPassedOut && Character.localCharacter.data.isCarried) { __instance.movementInput = Vector2.zero; __instance.interactWasPressed = false; __instance.interactIsPressed = false; __instance.emoteIsPressed = false; __instance.sprintWasPressed = false; __instance.sprintIsPressed = false; __instance.sprintToggleIsPressed = false; __instance.sprintToggleWasPressed = false; __instance.dropWasPressed = false; __instance.dropIsPressed = false; __instance.usePrimaryWasPressed = false; __instance.usePrimaryIsPressed = false; __instance.useSecondaryWasPressed = false; __instance.useSecondaryIsPressed = false; __instance.spectateLeftWasPressed = false; __instance.spectateRightWasPressed = false; __instance.selectBackpackWasPressed = false; __instance.scrollButtonLeftWasPressed = false; __instance.scrollButtonRightWasPressed = false; __instance.selectSlotForwardWasPressed = false; __instance.selectSlotBackwardWasPressed = false; } } [HarmonyPatch(typeof(CharacterInput), "SelectSlotWasPressed")] [HarmonyPostfix] private static bool CharacterInputSelectSlotWasPressedPostfix(bool originalResult, CharacterInput __instance) { if (!Character.localCharacter.data.fullyPassedOut && Character.localCharacter.data.isCarried) { return false; } return originalResult; } } private class SpectateViewPatch { private static readonly Func<MainCamera, CameraOverride> GetCamOverride = ExpressionUtils.CreateFieldGetter<MainCamera, CameraOverride>("camOverride"); private static readonly Action<MainCameraMovement> SpectateDelegate = (Action<MainCameraMovement>)Delegate.CreateDelegate(typeof(Action<MainCameraMovement>), null, typeof(MainCameraMovement).GetMethod("Spectate", BindingFlags.Instance | BindingFlags.NonPublic)); private static readonly Action<Character> SetSpecCharacter = (Action<Character>)Delegate.CreateDelegate(typeof(Action<Character>), AccessTools.PropertySetter(typeof(MainCameraMovement), "specCharacter")); private static float? m_defaultSpectateZoomMax = null; private static float m_customSpectateZoomMax = 3f; [HarmonyPatch(typeof(MainCameraMovement), "LateUpdate")] [HarmonyPostfix] private static void MainCameraMovementLateUpdatePostfix(MainCameraMovement __instance, MainCamera ___cam, bool ___isGodCam, ref bool ___isSpectating) { if (s_spectateViewSetting.Value && !(___isGodCam | ___isSpectating) && Object.op_Implicit((Object)(object)Character.localCharacter) && Character.localCharacter.data.isCarried && !Object.op_Implicit((Object)(object)GetCamOverride(___cam))) { SpectateDelegate(__instance); ___isSpectating = true; } } [HarmonyPatch(typeof(MainCameraMovement), "HandleSpecSelection")] [HarmonyPrefix] private static bool HandleSpecSelectionPrefix(ref bool __result, MainCameraMovement __instance) { if (!Character.localCharacter.data.fullyPassedOut && Object.op_Implicit((Object)(object)Character.localCharacter.data.carrier)) { if (!Object.op_Implicit((Object)(object)MainCameraMovement.specCharacter)) { SetSpecCharacter(Character.localCharacter.data.carrier); } float valueOrDefault = m_defaultSpectateZoomMax.GetValueOrDefault(); if (!m_defaultSpectateZoomMax.HasValue) { valueOrDefault = __instance.spectateZoomMax; m_defaultSpectateZoomMax = valueOrDefault; } __instance.spectateZoomMax = m_customSpectateZoomMax; __result = true; return false; } if (m_defaultSpectateZoomMax.HasValue) { __instance.spectateZoomMax = m_defaultSpectateZoomMax.Value; } return true; } } private class HoldToCarryPatch { private static readonly Func<CharacterInteractible, bool> CarriedByLocalCharacterDelegate = (Func<CharacterInteractible, bool>)Delegate.CreateDelegate(typeof(Func<CharacterInteractible, bool>), null, typeof(CharacterInteractible).GetMethod("CarriedByLocalCharacter", BindingFlags.Instance | BindingFlags.NonPublic)); [HarmonyPatch(typeof(CharacterInteractible), "Start")] [HarmonyPostfix] private static void ReplaceCharacterInteractibleComponent(CharacterInteractible __instance) { GameObject gameObject = ((Component)__instance).gameObject; if (!((Object)(object)gameObject.GetComponent<CharacterInteractibleConstant>() != (Object)null)) { ((CharacterInteractible)gameObject.AddComponent<CharacterInteractibleConstant>()).character = __instance.character; Object.Destroy((Object)(object)__instance); } } [HarmonyPatch(typeof(CharacterInteractible), "Interact")] [HarmonyPrefix] private static bool CharacterInteractibleInteractPrefix(CharacterInteractible __instance, Character interactor) { if (!CarriedByLocalCharacterDelegate(__instance) && !__instance.character.data.fullyPassedOut) { return s_holdToCarrySetting.Value == 0f; } return true; } } public class CharacterInteractibleConstant : CharacterInteractible, IInteractibleConstant, IInteractible { private static readonly Func<CharacterInteractible, bool> CanBeCarriedDelegate = (Func<CharacterInteractible, bool>)Delegate.CreateDelegate(typeof(Func<CharacterInteractible, bool>), null, typeof(CharacterInteractible).GetMethod("CanBeCarried", BindingFlags.Instance | BindingFlags.NonPublic)); private static readonly Action<CharacterCarrying, Character> StartCarryDelegate = (Action<CharacterCarrying, Character>)Delegate.CreateDelegate(typeof(Action<CharacterCarrying, Character>), null, typeof(CharacterCarrying).GetMethod("StartCarry", BindingFlags.Instance | BindingFlags.NonPublic)); public bool holdOnFinish => false; private void Start() { Logger.LogInfo((object)$"CharacterInteractibleConstant added to {base.character}"); } public bool IsConstantlyInteractable(Character interactor) { return CanBeCarriedDelegate((CharacterInteractible)(object)this); } public float GetInteractTime(Character interactor) { return s_holdToCarrySetting.Value; } public void Interact_CastFinished(Character interactor) { if (CanBeCarriedDelegate((CharacterInteractible)(object)this)) { StartCarryDelegate(interactor.refs.carriying, base.character); } } public void CancelCast(Character interactor) { } public void ReleaseInteract(Character interactor) { } } private class BackpackSwapOnCarryPatch { private static bool s_wasBackpackSwapped; [HarmonyPatch(typeof(CharacterCarrying), "StartCarry")] [HarmonyPrefix] private static bool StartCarryPrefix(ref Character ___character, Character target) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if (!s_swapBackpackSetting.Value) { return true; } s_wasBackpackSwapped = false; BackpackSlot backpackSlot = ___character.player.backpackSlot; if (!backpackSlot.hasBackpack || target.player.backpackSlot.hasBackpack) { return true; } target.player.backpackSlot = backpackSlot; ___character.player.backpackSlot = new BackpackSlot((byte)3); byte[] array = IBinarySerializable.ToManagedArray<InventorySyncData>(new InventorySyncData(___character.player.itemSlots, ___character.player.backpackSlot, ___character.player.tempFullSlot)); ((MonoBehaviourPun)___character.player).photonView.RPC("SyncInventoryRPC", (RpcTarget)0, new object[2] { array, true }); byte[] array2 = IBinarySerializable.ToManagedArray<InventorySyncData>(new InventorySyncData(target.player.itemSlots, target.player.backpackSlot, target.player.tempFullSlot)); ((MonoBehaviourPun)target.player).photonView.RPC("SyncInventoryRPC", (RpcTarget)0, new object[2] { array2, true }); s_wasBackpackSwapped = true; return true; } [HarmonyPatch(typeof(CharacterCarrying), "Drop")] [HarmonyPostfix] private static void DropPostfix(ref Character ___character, Character target) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (s_swapBackpackSetting.Value && s_wasBackpackSwapped) { BackpackSlot backpackSlot = target.player.backpackSlot; if (backpackSlot.hasBackpack) { ___character.player.backpackSlot = backpackSlot; target.player.backpackSlot = new BackpackSlot((byte)3); byte[] array = IBinarySerializable.ToManagedArray<InventorySyncData>(new InventorySyncData(target.player.itemSlots, target.player.backpackSlot, target.player.tempFullSlot)); ((MonoBehaviourPun)target.player).photonView.RPC("SyncInventoryRPC", (RpcTarget)0, new object[2] { array, true }); byte[] array2 = IBinarySerializable.ToManagedArray<InventorySyncData>(new InventorySyncData(___character.player.itemSlots, ___character.player.backpackSlot, ___character.player.tempFullSlot)); ((MonoBehaviourPun)___character.player).photonView.RPC("SyncInventoryRPC", (RpcTarget)0, new object[2] { array2, true }); } } } } internal static ManualLogSource Logger; private readonly Harmony m_harmony = new Harmony("nakazora.peak.piggyback"); private static ConfigEntry<bool> s_enablePiggybackSetting; private static ConfigEntry<bool> s_allowPiggybackByOthersSetting; private static ConfigEntry<bool> s_spectateViewSetting; private static ConfigEntry<float> s_holdToCarrySetting; private static ConfigEntry<bool> s_swapBackpackSetting; private static ConfigEntry<string> s_gamepadDropKeyBindingSetting; private static readonly Dictionary<string, string> GamepadKeysToControlPath = new Dictionary<string, string> { { "DpadUp", "dpad/up" }, { "DpadDown", "dpad/down" }, { "DpadLeft", "dpad/left" }, { "DpadRight", "dpad/right" }, { "North", "buttonSouth" }, { "East", "buttonEast" }, { "South", "buttonSouth" }, { "West", "buttonWest" }, { "LeftStickButton", "leftStickPress" }, { "RightStickButton", "rightStickPress" }, { "LeftShoulder", "leftShoulder" }, { "RightShoulder", "rightShoulder" }, { "LeftTrigger", "leftTrigger" }, { "RightTrigger", "rightTrigger" }, { "Start", "start" }, { "Select", "select" } }; private static List<InputAction> s_gamepadDropActions = new List<InputAction>(); private static readonly Action<CharacterCarrying, Character> DropFromCarryDelegate = (Action<CharacterCarrying, Character>)Delegate.CreateDelegate(typeof(Action<CharacterCarrying, Character>), null, typeof(CharacterCarrying).GetMethod("Drop", BindingFlags.Instance | BindingFlags.NonPublic)); private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; SetupConfig(); if (s_enablePiggybackSetting.Value) { m_harmony.PatchAll(typeof(CanBeCarriedPatch)); m_harmony.PatchAll(typeof(SpectateViewPatch)); m_harmony.PatchAll(typeof(HoldToCarryPatch)); } m_harmony.PatchAll(typeof(BackpackSwapOnCarryPatch)); Logger.LogInfo((object)"Plugin Piggyback loaded"); } private void SetupConfig() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown s_enablePiggybackSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnablePiggyback", true, "If false, you won't be able to piggyback others.\nThe AllowPiggybackByOthers, SwapBackpack, and GamepadDropKeybind settings will still take effect, but you won't be able to carry players that are not passed out."); s_allowPiggybackByOthersSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowPiggybackByOthers", true, "If false, you'll be immediately dropped whenever a player attempts to carry you while you're not passed out. Use this if you want to fully prevent players from picking you up."); s_spectateViewSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SpectateView", true, "If true, the camera will switch to the spectate view while being carried.\nIf false, the camera will remain in the first-person view while being carried.\nNote that you'll only be able to spectate the player carrying you."); s_holdToCarrySetting = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HoldToCarryTime", 1.5f, new ConfigDescription("The time in seconds you need to hold the interact button to start carrying another player.\nIf 0, you will start carrying the player immediately upon pressing the interact button.\nIf the player is passed out, you will start carrying them immediately even if this is set to a value greater than 0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); s_swapBackpackSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SwapBackpack", true, "(Experimental) If true, if you have a backpack and start carrying another player who does not have a backpack, the player you're carrying will automatically equip your backpack.\nThe backpack will be returned to you when you drop the player.\nIf false or if the player you want to carry already has a backpack, you must manually drop your backpack before you can carry that player."); s_gamepadDropKeyBindingSetting = ((BaseUnityPlugin)this).Config.Bind<string>("Controls", "GamepadDropKeybind", "LeftShoulder+RightShoulder", "The key binding to drop the player you're carrying. This only applies to Gamepad, the binding on keyboard should be the Number 4 key by default.\nYou can combine multiple keys by separating them with a plus (+) sign. This would require you to press all the given keys at the same time to drop the player.\nAcceptable Gamepad Keys:\n" + string.Join(", ", GamepadKeysToControlPath.Keys)); s_gamepadDropKeyBindingSetting.SettingChanged += delegate { SetupGamepadDropAction(); }; SetupGamepadDropAction(); } private static void SetupGamepadDropAction() { //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Expected O, but got Unknown if (s_gamepadDropActions.Count > 0) { foreach (InputAction s_gamepadDropAction in s_gamepadDropActions) { s_gamepadDropAction.Disable(); s_gamepadDropAction.Dispose(); } s_gamepadDropActions.Clear(); } List<string> list = new List<string>(); bool flag = false; string[] array = s_gamepadDropKeyBindingSetting.Value.Split('+'); foreach (string text in array) { if (GamepadKeysToControlPath.TryGetValue(text.Trim(), out var value)) { list.Add("<Gamepad>/" + value); continue; } Logger.LogWarning((object)("Unknown gamepad key: " + text.Trim())); flag = true; } if (flag) { string text2 = (string)((ConfigEntryBase)s_gamepadDropKeyBindingSetting).DefaultValue; Logger.LogError((object)("Invalid gamepad key binding detected. Falling back to default binding: " + text2)); list.Clear(); array = text2.Split('+'); foreach (string text3 in array) { if (GamepadKeysToControlPath.TryGetValue(text3.Trim(), out var value2)) { list.Add("<Gamepad>/" + value2); } else { Logger.LogError((object)("Unknown default gamepad key: " + text3.Trim())); } } } for (int j = 0; j < list.Count; j++) { InputAction val = new InputAction($"DropCarry_{j}", (InputActionType)1, list[j], (string)null, (string)null, (string)null); s_gamepadDropActions.Add(val); val.Enable(); } } private void Update() { if (Object.op_Implicit((Object)(object)Character.localCharacter)) { if (Object.op_Implicit((Object)(object)Character.localCharacter.data.carriedPlayer) && s_gamepadDropActions.Count > 0 && ((s_gamepadDropActions.Count == 1 && s_gamepadDropActions[0].WasPressedThisFrame()) || s_gamepadDropActions.All((InputAction action) => action.IsPressed()))) { DropPlayerFromCarry(Character.localCharacter.data.carriedPlayer); } if (!s_allowPiggybackByOthersSetting.Value && !Character.localCharacter.data.fullyPassedOut) { DropPlayerFromCarry(Character.localCharacter); } else if (s_enablePiggybackSetting.Value && !Character.localCharacter.data.fullyPassedOut && IsCharacterDoingIllegalCarryActions(Character.localCharacter)) { DropPlayerFromCarry(Character.localCharacter); } } } private static void DropPlayerFromCarry(Character character) { if (Object.op_Implicit((Object)(object)character.data.carrier)) { DropFromCarryDelegate(character.data.carrier.refs.carriying, character); } } private static bool IsCharacterDoingIllegalCarryActions(Character character) { if (!character.data.isSprinting && !character.data.isJumping && !character.data.isClimbingAnything && !character.data.isCrouching) { return character.data.isReaching; } return true; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "nakazora.peak.piggyback"; public const string PLUGIN_NAME = "Piggyback"; public const string PLUGIN_VERSION = "1.3.0"; } }