using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
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 MycopunkShared;
using Pigeon.Movement;
using Unity.Netcode;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MycopunkQuickTimers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+fe8cef1291afd0a7f7ea8a9ddfd28d3607c4cdd5")]
[assembly: AssemblyProduct("MycopunkQuickTimers")]
[assembly: AssemblyTitle("MycopunkQuickTimers")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.2.0")]
[module: UnverifiableCode]
[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 MycopunkQuickTimers
{
internal static class MenuText
{
private static readonly Dictionary<string, Dictionary<string, string>> Names = new Dictionary<string, Dictionary<string, string>>
{
["en"] = new Dictionary<string, string>
{
["quick.startup"] = "Skip startup animations",
["quick.drop"] = "Shorten fixed-map drop",
["quick.extraction"] = "Shorten extraction flight",
["quick.mission_start"] = "Shorten mission start delay",
["quick.mission_end"] = "Shorten mission end delay"
},
["ja"] = new Dictionary<string, string>
{
["quick.startup"] = "起動時の演出をスキップ",
["quick.drop"] = "固定マップの降下待ちを短縮",
["quick.extraction"] = "離脱時のドロップポッドの帰還演出を短縮",
["quick.mission_start"] = "ミッション開始前の待ちを短縮",
["quick.mission_end"] = "ミッション終了後の待ちを短縮"
}
};
private static readonly Dictionary<string, Dictionary<string, string>> Descriptions = new Dictionary<string, Dictionary<string, string>> { ["ja"] = new Dictionary<string, string>
{
["quick.startup"] = "起動時の演出をスキップし、音声の読み込み中にタイトルを表示します。変更は次回のゲーム起動時から反映されます。",
["quick.drop"] = "固定マップの標準の降下時間を短縮します。ホストの設定が全員に適用され、地形生成は待ちます。",
["quick.extraction"] = "ホストがミッション統計を受信し、参加者全員が生存してドロップポッド内にいる場合に、離脱時の帰還演出を短縮します。それ以外は元の時間になります。",
["quick.mission_start"] = "全員の読み込みを待った後の固定時間を短縮します。ホストの設定が全員に適用されます。",
["quick.mission_end"] = "ホストが全員のミッション統計を集めた後、報酬処理前の自分の待ち時間を短縮します。確認できない場合は元の待ち時間になります。"
} };
public static object[] Label(string key)
{
return ModsMenuTag.Label(key, (IDictionary<string, Dictionary<string, string>>)Names, (IDictionary<string, Dictionary<string, string>>)Descriptions);
}
}
internal static class MissionDelays
{
[HarmonyPatch]
private static class StatsSyncPatch
{
[HarmonyPatch(typeof(MissionManager), "OnMissionCompleteCoroutine")]
[HarmonyPrefix]
private static void ResetStats()
{
_statsReceived = false;
}
[HarmonyPatch(typeof(DropPod), "SetMissionData_ClientRpc")]
[HarmonyPostfix]
private static void ReceiveStats()
{
_statsReceived = true;
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
private static class DropPatch
{
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
return Drop(instructions);
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
private static class ExtractionPatch
{
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
return Extraction(instructions);
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
private static class StartPatch
{
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
return Start(instructions);
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
private static class EndPatch
{
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
return End(instructions);
}
}
private static bool _statsSyncAvailable;
private static bool _statsReceived;
private static bool _extractionFaulted;
private static readonly FieldInfo LabelsField = AccessTools.Field(typeof(CodeInstruction), "labels");
internal static void Setup(Harmony harmony)
{
try
{
harmony.CreateClassProcessor(typeof(StatsSyncPatch)).Patch();
_statsSyncAvailable = true;
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Mission end delay: stats synchronization unavailable; using original delay. " + ex.Message));
}
Apply(harmony, typeof(DropPatch), "FakeWaitToContinueDrop");
Apply(harmony, typeof(ExtractionPatch), "LaunchFromMissionCoroutine");
Apply(harmony, typeof(StartPatch), "StartFirstObjective");
Apply(harmony, typeof(EndPatch), "OnMissionCompleteCoroutine");
}
private static void Apply(Harmony harmony, Type patch, string coroutineName)
{
try
{
harmony.CreateClassProcessor(patch).Patch();
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Mission delay " + coroutineName + " unchanged: " + ex.Message));
}
}
private static bool HasLabels(CodeInstruction instruction)
{
if (!(LabelsField == null) && LabelsField.GetValue(instruction) is IList list)
{
return list.Count != 0;
}
return true;
}
private static bool IsLessThan(CodeInstruction instruction)
{
if (!(instruction.opcode == OpCodes.Blt))
{
return instruction.opcode == OpCodes.Blt_S;
}
return true;
}
private static bool IsTimeField(FieldInfo field)
{
return field.Name.StartsWith("<time>", StringComparison.Ordinal);
}
private static bool IsTimeField(CodeInstruction instruction)
{
if (instruction.opcode == OpCodes.Ldfld && instruction.operand is FieldInfo field)
{
return IsTimeField(field);
}
return false;
}
private static IEnumerable<CodeInstruction> Drop(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> code = instructions.ToList();
MethodInfo overrideDuration = AccessTools.Method(typeof(Mission), "OverrideDropDuration", (Type[])null, (Type[])null);
List<int> matches = (from i in Enumerable.Range(2, code.Count - 5)
where code[i].opcode == OpCodes.Ldc_R4 && object.Equals(code[i].operand, 6f) && (code[i - 1].opcode == OpCodes.Brtrue || code[i - 1].opcode == OpCodes.Brtrue_S) && CodeInstructionExtensions.Calls(code[i - 2], overrideDuration) && (code[i + 1].opcode == OpCodes.Br || code[i + 1].opcode == OpCodes.Br_S) && code[i + 3].opcode == OpCodes.Stfld && code[i + 3].operand is FieldInfo field && IsTimeField(field)
select i).ToList();
ReplaceConstant(code, matches, "DropDuration", "drop");
return code;
}
private static IEnumerable<CodeInstruction> Start(IEnumerable<CodeInstruction> instructions)
{
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Expected O, but got Unknown
List<CodeInstruction> code = instructions.ToList();
FieldInfo field = AccessTools.Field(typeof(MissionManager), "timeBeforeStartingMission");
List<int> list = (from i in Enumerable.Range(1, code.Count - 2)
where field != null && CodeInstructionExtensions.LoadsField(code[i], field, false) && IsLessThan(code[i + 1]) && i >= 2 && IsTimeField(code[i - 2])
select i).ToList();
if (list.Count != 1 || HasLabels(code[list[0] + 1]))
{
Plugin.Log.LogWarning((object)"Mission start delay: expected code not found; unchanged.");
return code;
}
code.Insert(list[0] + 1, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(MissionDelays), "StartDuration", (Type[])null, (Type[])null)));
return code;
}
private static IEnumerable<CodeInstruction> Extraction(IEnumerable<CodeInstruction> instructions)
{
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Expected O, but got Unknown
List<CodeInstruction> code = instructions.ToList();
FieldInfo field = AccessTools.Field(typeof(DropPod), "extractionTime");
List<int> list = (from i in Enumerable.Range(2, code.Count - 3)
where field != null && CodeInstructionExtensions.LoadsField(code[i], field, false) && IsLessThan(code[i + 1]) && IsTimeField(code[i - 2])
select i).ToList();
if (list.Count != 1 || HasLabels(code[list[0] + 1]))
{
Plugin.Log.LogWarning((object)"Extraction delay: expected code not found; unchanged.");
return code;
}
code.Insert(list[0] + 1, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(MissionDelays), "ExtractionDuration", (Type[])null, (Type[])null)));
return code;
}
private static IEnumerable<CodeInstruction> End(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> code = instructions.ToList();
FieldInfo rewards = AccessTools.Field(typeof(Global), "rewardInfoBuffer");
List<int> matches = (from i in Enumerable.Range(1, code.Count - 3)
where code[i].opcode == OpCodes.Ldc_R4 && object.Equals(code[i].operand, 3.5f) && IsTimeField(code[i - 1]) && IsLessThan(code[i + 1]) && rewards != null && CodeInstructionExtensions.LoadsField(code[i + 2], rewards, false)
select i).ToList();
ReplaceConstant(code, matches, "EndDuration", "end");
return code;
}
private static void ReplaceConstant(List<CodeInstruction> code, List<int> matches, string method, string name)
{
if (matches.Count != 1)
{
Plugin.Log.LogWarning((object)("Mission " + name + " delay: expected code not found; unchanged."));
return;
}
code[matches[0]].opcode = OpCodes.Call;
code[matches[0]].operand = AccessTools.Method(typeof(MissionDelays), method, (Type[])null, (Type[])null);
}
private static float DropDuration()
{
if (!Plugin.FastDrop.Value)
{
return 6f;
}
return 0.01f;
}
private static float StartDuration(float original)
{
if (!Plugin.FastMissionStart.Value)
{
return original;
}
return Math.Min(original, 0.01f);
}
private static float ExtractionDuration(float original)
{
if (!Plugin.FastExtraction.Value || _extractionFaulted || !_statsSyncAvailable || !_statsReceived)
{
return original;
}
try
{
NetworkManager singleton = NetworkManager.Singleton;
DropPod instance = DropPod.Instance;
List<Player> players = GameManager.players;
if ((Object)(object)singleton == (Object)null || !singleton.IsServer || !singleton.IsListening || (Object)(object)instance == (Object)null || players == null || players.Count == 0 || players.Count < singleton.ConnectedClientsList.Count)
{
return original;
}
foreach (Player item in players)
{
if ((Object)(object)item == (Object)null || !item.IsAlive || !instance.IsPlayerInside(item, false, 16f))
{
return original;
}
}
return Math.Min(original, 1f);
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Extraction delay check failed; using original duration. " + ex.Message));
_extractionFaulted = true;
return original;
}
}
private static float EndDuration()
{
if (!Plugin.FastMissionEnd.Value || !_statsSyncAvailable || !_statsReceived)
{
return 3.5f;
}
return 0.01f;
}
}
[BepInPlugin("f1w3.mycopunk.quicktimers", "Mycopunk Quick Timers", "0.1.2")]
[BepInDependency("doku_macaron.mycopunk.macaroncore", "1.1.0")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
public const string Guid = "f1w3.mycopunk.quicktimers";
public const string Name = "Mycopunk Quick Timers";
public const string Version = "0.1.2";
internal static ManualLogSource Log;
internal static ConfigEntry<bool> FastDrop;
internal static ConfigEntry<bool> FastMissionStart;
internal static ConfigEntry<bool> FastMissionEnd;
internal static ConfigEntry<bool> FastExtraction;
private static ConfigEntry<bool> _fastStartup;
private Harmony _harmony;
private void Awake()
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Expected O, but got Unknown
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Expected O, but got Unknown
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Expected O, but got Unknown
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Expected O, but got Unknown
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Expected O, but got Unknown
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_010b: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
_fastStartup = ((BaseUnityPlugin)this).Config.Bind<bool>("Startup", "ShortenLogos", true, new ConfigDescription("Skip startup animations and show the title while sound banks load. Changes take effect the next time the game starts.", (AcceptableValueBase)null, MenuText.Label("quick.startup")));
FastDrop = ((BaseUnityPlugin)this).Config.Bind<bool>("Mission", "ShortenDrop", false, new ConfigDescription("Shorten the default fixed-map drop delay. The host controls this for the lobby; terrain generation is still awaited.", (AcceptableValueBase)null, MenuText.Label("quick.drop")));
FastExtraction = ((BaseUnityPlugin)this).Config.Bind<bool>("Mission", "ShortenExtraction", false, new ConfigDescription("Shorten the return flight when the host has received mission statistics and all connected players are alive inside the pod. Otherwise retain the original duration.", (AcceptableValueBase)null, MenuText.Label("quick.extraction")));
FastMissionStart = ((BaseUnityPlugin)this).Config.Bind<bool>("Mission", "ShortenStart", false, new ConfigDescription("Shorten the delay after players have loaded. The host controls this for the lobby.", (AcceptableValueBase)null, MenuText.Label("quick.mission_start")));
FastMissionEnd = ((BaseUnityPlugin)this).Config.Bind<bool>("Mission", "ShortenEnd", false, new ConfigDescription("Shorten the local delay after the host confirms all mission statistics. If confirmation is unavailable, retain the original delay.", (AcceptableValueBase)null, MenuText.Label("quick.mission_end")));
_harmony = new Harmony("f1w3.mycopunk.quicktimers");
StartupTiming.Setup(_harmony);
MissionDelays.Setup(_harmony);
if (_fastStartup.Value)
{
StartupLogos.Setup(_harmony);
}
Log.LogInfo((object)("Mycopunk Quick Timers 0.1.2 loaded (startup=" + _fastStartup.Value + ", drop=" + FastDrop.Value + ", start=" + FastMissionStart.Value + ", end=" + FastMissionEnd.Value + ", extraction=" + FastExtraction.Value + ")."));
}
private void OnDestroy()
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
internal static class StartupLogos
{
private static readonly FieldInfo LabelsField = AccessTools.Field(typeof(CodeInstruction), "labels");
internal static void Setup(Harmony harmony)
{
try
{
harmony.CreateClassProcessor(typeof(StartupLogos)).Patch();
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Startup logos unchanged: " + ex.Message));
}
}
private static bool IsTimeField(FieldInfo field)
{
return field.Name.StartsWith("<time>", StringComparison.Ordinal);
}
private static bool HasLabel(CodeInstruction instruction, object label)
{
if (LabelsField?.GetValue(instruction) is IList list)
{
return list.Contains(label);
}
return false;
}
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0706: Unknown result type (might be due to invalid IL or missing references)
//IL_0710: Expected O, but got Unknown
//IL_075f: Unknown result type (might be due to invalid IL or missing references)
//IL_0769: Expected O, but got Unknown
List<CodeInstruction> code = instructions.ToList();
List<int> list = new List<int>();
List<int> list2 = new List<int>();
for (int i = 1; i + 3 < code.Count; i++)
{
if (code[i].opcode == OpCodes.Ldc_R4 && code[i].operand is float num && code[i - 1].opcode == OpCodes.Ldfld && code[i - 1].operand is FieldInfo field && IsTimeField(field) && (code[i + 1].opcode == OpCodes.Blt || code[i + 1].opcode == OpCodes.Blt_S))
{
if (num == 3f)
{
list.Add(i);
}
else if (num == 1f)
{
list2.Add(i);
}
}
}
FieldInfo fieldInfo = AccessTools.Field(typeof(StartMenu), "pigeonsAtPlayIcon");
FieldInfo fieldInfo2 = AccessTools.Field(typeof(StartMenu), "devolverIcon");
MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(Global), "DisablePCFeatures");
MethodInfo banksLoaded = AccessTools.PropertyGetter(typeof(Global), "AreAllBanksLoaded");
FieldInfo wipe = AccessTools.Field(typeof(StartMenu), "initializeWipe");
MethodInfo methodInfo2 = AccessTools.Method(typeof(PlayerInput), "UnlockCursor", Type.EmptyTypes, (Type[])null);
int num2 = code.FindIndex((CodeInstruction c) => c.opcode == OpCodes.Ldstr && object.Equals(c.operand, "DisableStartup"));
int num3 = num2 + 9;
int num4 = ((num3 < code.Count) ? code.FindIndex(num3 + 1, (CodeInstruction c) => CodeInstructionExtensions.Calls(c, banksLoaded)) : (-1));
int bankBranch = num4 + 1;
int num5 = ((list.Count == 2) ? code.FindIndex(list[0] + 1, list[1] - list[0] - 1, (CodeInstruction c) => c.opcode == OpCodes.Ldc_R4 && c.operand is float num6 && num6 == 0.6f) : (-1));
if (list.Count != 2 || list2.Count != 2 || fieldInfo == null || fieldInfo2 == null || methodInfo == null || banksLoaded == null || wipe == null || methodInfo2 == null || !CodeInstructionExtensions.Calls(code[list2[0] + 2], methodInfo2) || !CodeInstructionExtensions.LoadsField(code[list2[1] + 3], wipe, false) || !CodeInstructionExtensions.LoadsField(code[list[0] + 3], fieldInfo, false) || !CodeInstructionExtensions.Calls(code[list[1] + 2], methodInfo) || num5 < 1 || num5 + 3 >= code.Count || !CodeInstructionExtensions.LoadsField(code[num5 + 3], fieldInfo2, false) || code[num5 - 1].opcode != OpCodes.Ldfld || !(code[num5 - 1].operand is FieldInfo field2) || !IsTimeField(field2) || (code[num5 + 1].opcode != OpCodes.Blt && code[num5 + 1].opcode != OpCodes.Blt_S) || num2 < 2 || num3 >= code.Count || !CodeInstructionExtensions.Calls(code[num2 - 2], banksLoaded) || code[num3 - 1].opcode != OpCodes.And || (code[num3].opcode != OpCodes.Brtrue && code[num3].opcode != OpCodes.Brtrue_S) || code[num3].operand == null || num4 < 0 || bankBranch >= code.Count || !HasLabel(code[num4], code[num3].operand) || (code[bankBranch].opcode != OpCodes.Brtrue && code[bankBranch].opcode != OpCodes.Brtrue_S) || code[bankBranch].operand == null || !code.Skip(bankBranch + 1).Select((CodeInstruction instruction, int index) => new { instruction, index }).Any(x => HasLabel(x.instruction, code[bankBranch].operand) && bankBranch + 2 + x.index < code.Count && CodeInstructionExtensions.LoadsField(code[bankBranch + 2 + x.index], wipe, false)))
{
Plugin.Log.LogWarning((object)"Startup sequence: expected code not found; unchanged.");
return code;
}
foreach (int item in list)
{
code[item].operand = 0f;
}
foreach (int item2 in list2)
{
code[item2].operand = 0f;
}
code[num5].operand = 0f;
object operand = code[bankBranch].operand;
code[bankBranch].opcode = OpCodes.Pop;
code[bankBranch].operand = null;
code.Insert(bankBranch + 1, new CodeInstruction(OpCodes.Br, operand));
object operand2 = code[num3].operand;
code[num3].opcode = OpCodes.Pop;
code[num3].operand = null;
code.Insert(num3 + 1, new CodeInstruction(OpCodes.Br, operand2));
Plugin.Log.LogInfo((object)"Startup sequence: showing the title while sound banks load in the background.");
return code;
}
}
[HarmonyPatch]
internal static class StartupTiming
{
private static float _firstCheck = -1f;
private static bool _reported;
internal static void Setup(Harmony harmony)
{
try
{
harmony.CreateClassProcessor(typeof(StartupTiming)).Patch();
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Startup timing unavailable: " + ex.Message));
}
}
[HarmonyPatch(typeof(Global), "BankCallback")]
[HarmonyPostfix]
private static void BankLoaded()
{
if (!_reported && _firstCheck >= 0f && (Object)(object)StartMenu.Instance != (Object)null)
{
BanksChecked(Global.AreAllBanksLoaded);
}
}
[HarmonyPatch(typeof(StartMenu), "OnMusicLoaded")]
[HarmonyPostfix]
private static void MusicLoaded()
{
if (!_reported && (Object)(object)StartMenu.Instance != (Object)null)
{
Plugin.Log.LogInfo((object)$"Startup timing: menu music ready at {Time.realtimeSinceStartup:F2} s since Unity startup.");
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyPostfix]
private static void BanksChecked(bool __result)
{
if (!_reported && !((Object)(object)StartMenu.Instance == (Object)null))
{
float realtimeSinceStartup = Time.realtimeSinceStartup;
if (_firstCheck < 0f)
{
_firstCheck = realtimeSinceStartup;
Plugin.Log.LogInfo((object)$"Startup timing: first required-bank check at {realtimeSinceStartup:F2} s; ready={__result}.");
}
if (__result)
{
_reported = true;
Plugin.Log.LogInfo((object)$"Startup timing: required sound banks ready at {realtimeSinceStartup:F2} s; waited {realtimeSinceStartup - _firstCheck:F2} s after first check.");
}
}
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}