using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using FixedMistRNG.SSMPAddon; using FixedMistRNG.SSMPAddon.Command.Server; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using Microsoft.CodeAnalysis; using SSMP.Api.Client; using SSMP.Api.Client.Networking; using SSMP.Api.Command; using SSMP.Api.Command.Server; using SSMP.Api.Server; using SSMP.Api.Server.Networking; using SSMP.Networking.Packet; using Silksong.FsmUtil; using Silksong.ModMenu.Elements; using Silksong.ModMenu.Models; using Silksong.ModMenu.Plugin; using Silksong.ModMenu.Screens; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [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("FixedMistRNG")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+dcd92dd3b48c8a6e063c518fd1fdc2fd1ec15b94")] [assembly: AssemblyProduct("FixedMistRNG")] [assembly: AssemblyTitle("FixedMistRNG")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Vitaxses/FixedMistRNG")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.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.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] [Microsoft.CodeAnalysis.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] [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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace FixedMistRNG { [HarmonyPatch(typeof(ActivateRandomChildren), "OnEnable")] internal static class ActivateRandomChildrenPatch { [HarmonyPrefix] public static bool Prefix_OnEnable(ActivateRandomChildren __instance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (FixedMistClientAddon.IsConnected()) { Scene scene = ((Component)__instance).gameObject.scene; if (((Scene)(ref scene)).name.StartsWith("Dust_Maze")) { foreach (Transform item in ((Component)__instance).transform) { ((Component)item).gameObject.SetActive(false); } Random random = new Random(FixedMistClientAddon.Instance.GetSeed()); int num = random.Next(__instance.amountMin, __instance.amountMax); while ((float)num > 0f) { int num2 = random.Next(0, ((Component)__instance).transform.childCount); ((Component)((Component)__instance).transform.GetChild(num2)).gameObject.SetActive(true); num--; } return false; } } return true; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("io.github.vitaxses.fixedmistrng", "FixedMistRNG", "1.0.1")] public class FixedMistPlugin : BaseUnityPlugin, IModMenuCustomMenu, IModMenuInterface { public const string Id = "io.github.vitaxses.fixedmistrng"; public static string Name => "FixedMistRNG"; public static string Version => "1.0.1"; private void Awake() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) ClientAddon.RegisterAddon((ClientAddon)(object)new FixedMistClientAddon()); ServerAddon.RegisterAddon((ServerAddon)(object)new FixedMistServerAddon()); new Harmony("io.github.vitaxses.fixedmistrng").PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Name + " (io.github.vitaxses.fixedmistrng) v" + Version + " has loaded!")); } public LocalizedText ModMenuName() { return LocalizedText.op_Implicit("Fixed Mist RNG"); } public AbstractMenuScreen BuildCustomMenu() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) PaginatedMenuScreenBuilder val = new PaginatedMenuScreenBuilder(ModMenuName(), 8); string adjustSeedDescription = "Randomize the seed if a player leaves The Mist or respawns at the entrance while no one else is inside"; ChoiceElement adjustSeedElement = new ChoiceElement(LocalizedText.Raw("Randomize Seed"), (IChoiceModel)(object)ChoiceModels.ForBool("No", "Yes")); SliderElement seedElement = new SliderElement(LocalizedText.Raw("Seed"), (SliderModel)(object)SliderModels.ForInts(0, 1000)); val.Add((MenuElement)(object)adjustSeedElement); val.Add((MenuElement)(object)seedElement); PaginatedMenuScreen obj = val.Build(); ((AbstractMenuScreen)obj).OnShow += delegate { Text descriptionText = adjustSeedElement.DescriptionText; SliderElement obj2 = seedElement; bool flag2 = (((SelectableElement)adjustSeedElement).Interactable = FixedMistClientAddon.IsConnected()); bool flag4 = (((SelectableElement)obj2).Interactable = flag2); descriptionText.text = (flag4 ? adjustSeedDescription : ""); ((SelectableValueElement)(object)adjustSeedElement).Value = FixedMistClientAddon.Instance != null && FixedMistClientAddon.Instance.AdjustSeed; ((SelectableValueElement)(object)seedElement).Value = ((FixedMistClientAddon.Instance == null) ? (-1) : FixedMistClientAddon.Instance.CurrentSeed); }; ((AbstractMenuScreen)obj).OnHide += delegate { if (FixedMistClientAddon.IsConnected()) { FixedMistClientAddon.Instance.SendOptionUpdate(((SelectableValueElement)(object)seedElement).Value, ((SelectableValueElement)(object)adjustSeedElement).Value); } }; return (AbstractMenuScreen)(object)obj; } } [HarmonyPatch(typeof(MazeController))] internal static class MazeControllerPatches { private static State lastState; [HarmonyPatch("Activate")] [HarmonyPrefix] private static void Prefix_Activate(MazeController __instance) { SaveAndInitRngState(__instance); } public static void SaveAndInitRngState(MazeController mc) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (FixedMistClientAddon.IsConnected()) { lastState = Random.state; Random.InitState(FixedMistClientAddon.Instance.GetSeed()); } } [HarmonyPatch("Activate")] [HarmonyPostfix] public static void Postfix_Activate() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (FixedMistClientAddon.IsConnected()) { Random.state = lastState; } } } [HarmonyPatch(typeof(PlayMakerFSM), "Start")] internal static class PlayMakerFSMPatch { [HarmonyPostfix] private static void Postfix_Start(PlayMakerFSM __instance) { if (FixedMistClientAddon.IsConnected()) { MistMazeController(__instance); WraithSpawner(__instance); } } private static void SetNode(PlayMakerFSM fsm, FsmGameObject? nodes, Random rng) { if (nodes != null && !((Object)(object)nodes.Value == (Object)null)) { Transform transform = nodes.Value.transform; if (transform.childCount != 0) { GameObject gameObject = ((Component)transform.GetChild(rng.Next(transform.childCount))).gameObject; FsmUtil.FindGameObjectVariable(fsm, "Next Node").Value = gameObject; } } } private static void MistMazeController(PlayMakerFSM fsm) { PlayMakerFSM fsm2 = fsm; if (!(fsm2.FsmName != "mist_maze_controller")) { Random rng = new Random(FixedMistClientAddon.Instance.GetSeed()); FsmState state = FsmUtil.GetState(fsm2, "Set Silkflies"); FsmUtil.DisableAction(state, 1); FsmGameObject silkFlyNodes = FsmUtil.FindGameObjectVariable(fsm2, "Silkfly Nodes"); FsmUtil.InsertMethod(state, 1, (Action)delegate { SetNode(fsm2, silkFlyNodes, rng); }); FsmState state2 = FsmUtil.GetState(fsm2, "Set Traps"); FsmUtil.DisableAction(state2, 0); FsmGameObject traps = FsmUtil.FindGameObjectVariable(fsm2, "Trap Sets"); FsmUtil.InsertMethod(state2, 0, (Action)delegate { SetNode(fsm2, traps, rng); }); FsmState state3 = FsmUtil.GetState(fsm2, "Set Wraiths"); FsmUtil.DisableAction(state3, 0); FsmGameObject wraithNodes = FsmUtil.FindGameObjectVariable(fsm2, "Wraith Nodes"); FsmUtil.InsertMethod(state3, 0, (Action)delegate { SetNode(fsm2, wraithNodes, rng); }); } } private static void WraithSpawner(PlayMakerFSM fsm) { PlayMakerFSM fsm2 = fsm; if (fsm2.FsmName != "Control" || !((Object)fsm2).name.StartsWith("Wraith Summoner")) { return; } Random rng = new Random(FixedMistClientAddon.Instance.GetSeed()); FsmState state = FsmUtil.GetState(fsm2, "Choose"); SendRandomEvent action = FsmUtil.GetAction(state, 0); FsmEvent[] originalEvents = action.events; List remaining = originalEvents.ToList(); FsmUtil.DisableAction(state, 0); FsmUtil.AddMethod(state, (Action)delegate { if (remaining.Count == 0) { remaining = originalEvents.ToList(); } int index = rng.Next(remaining.Count); FsmEvent val = remaining[index]; remaining.RemoveAt(index); fsm2.SendEvent(val.Name); }, false); } } } namespace FixedMistRNG.SSMPAddon { internal static class AddonIdentifiers { public const string NAME = "Fixed Mist RNG"; public static string VERSION => Assembly.GetExecutingAssembly().GetName().Version.ToString(); } public class FixedMistClientAddon : ClientAddon { internal static FixedMistClientAddon? Instance; private IClientAddonNetworkSender? sender; private IClientAddonNetworkReceiver? receiver; public override bool NeedsNetwork => true; public override uint ApiVersion => 1u; protected override string Name => "Fixed Mist RNG"; protected override string Version => AddonIdentifiers.VERSION; public int CurrentSeed { get; private set; } public bool AdjustSeed { get; private set; } public override void Initialize(IClientApi clientApi) { Instance = this; sender = clientApi.NetClient.GetNetworkSender((ClientAddon)(object)this); receiver = clientApi.NetClient.GetNetworkReceiver((ClientAddon)(object)this, (Func)InstantiatePacket); RegisterPacketHandlers(); } private void RegisterPacketHandlers() { receiver?.RegisterPacketHandler(S2CPacketId.UpdateSeed, (GenericClientPacketHandler)delegate(UpdateSeedPacketData packetData) { CurrentSeed = packetData.Seed; AdjustSeed = packetData.AdjustSeed; MazeController newestInstance = MazeController.NewestInstance; if (Object.op_Implicit((Object)(object)newestInstance) && newestInstance.IsCapScene) { MazeControllerPatches.Postfix_Activate(); MazeControllerPatches.SaveAndInitRngState(newestInstance); newestInstance.LinkDoors((IReadOnlyList)newestInstance.entryDoors); } }); } public static IPacketData? InstantiatePacket(S2CPacketId id) { if (id == S2CPacketId.UpdateSeed) { return (IPacketData?)(object)new UpdateSeedPacketData(); } return null; } public void SendOptionUpdate(int Seed, bool AdjustSeed) { if ((CurrentSeed != Seed || this.AdjustSeed != AdjustSeed) && sender != null && IsConnected()) { sender.SendSingleData(C2SPacketId.UpdateSettings, (IPacketData)(object)new UpdateSeedPacketData { Seed = Seed, AdjustSeed = AdjustSeed }); } } internal static bool IsConnected() { if (Instance != null && ((ClientAddon)Instance).ClientApi != null) { return ((ClientAddon)Instance).ClientApi.NetClient.IsConnected; } return false; } internal int GetSeed() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) int currentSeed = CurrentSeed; Scene activeScene = SceneManager.GetActiveScene(); return currentSeed ^ ((Scene)(ref activeScene)).name.GetHashCode(); } } public class FixedMistServerAddon : ServerAddon { internal static FixedMistServerAddon? Instance; private IServerApi? api; private IServerAddonNetworkSender? sender; private IServerAddonNetworkReceiver? receiver; private readonly List playersInMist = new List(); private Random? rng; public override bool NeedsNetwork => true; public override uint ApiVersion => 1u; protected override string Name => "Fixed Mist RNG"; protected override string Version => AddonIdentifiers.VERSION; public int CurrentSeed { get; private set; } public bool AdjustSeed { get; private set; } public override void Initialize(IServerApi Api) { Instance = this; api = Api; ((ICommandManager)(object)api.CommandManager).RegisterCommand((IServerCommand)(object)new SetSeedCommand(this)); sender = api.NetServer.GetNetworkSender((ServerAddon)(object)this); receiver = api.NetServer.GetNetworkReceiver((ServerAddon)(object)this, (Func)InstantiatePacket); RegisterPacketHandlers(); api.ServerManager.PlayerDisconnectEvent += delegate(IServerPlayer player) { playersInMist.Remove(player.Id); }; api.ServerManager.PlayerConnectEvent += OnPlayerConnect; api.ServerManager.PlayerEnterSceneEvent += OnPlayerEnterScene; SetSeed(GenerateNewSeed()); } private void OnPlayerEnterScene(IServerPlayer player) { string currentScene = player.CurrentScene; if (IsMist(currentScene) && !playersInMist.Contains(player.Id)) { playersInMist.Add(player.Id); } else if (!IsMist(currentScene) && playersInMist.Remove(player.Id)) { CheckForMistUpdate(); } } private void OnPlayerConnect(IServerPlayer player) { if (IsMist(player.CurrentScene) && !playersInMist.Contains(player.Id)) { playersInMist.Add(player.Id); } sender?.SendSingleData(S2CPacketId.UpdateSeed, (IPacketData)(object)new UpdateSeedPacketData { Seed = CurrentSeed, AdjustSeed = AdjustSeed }, player.Id); } private void RegisterPacketHandlers() { receiver?.RegisterPacketHandler(C2SPacketId.UpdateSettings, (GenericServerPacketHandler)delegate(ushort id, UpdateSeedPacketData packetData) { IServerPlayer player = api.ServerManager.GetPlayer(id); if (!player.IsAuthorized) { api.ServerManager.SendMessage(player, "You have to be authorized for updating settings."); } else { CurrentSeed = packetData.Seed; AdjustSeed = packetData.AdjustSeed; api.ServerManager.BroadcastMessage(player.Username + " updated FixedMistRNG settings."); sender?.BroadcastSingleData(S2CPacketId.UpdateSeed, (IPacketData)(object)new UpdateSeedPacketData { Seed = CurrentSeed, AdjustSeed = AdjustSeed }); } }); } public static IPacketData? InstantiatePacket(C2SPacketId id) { if (id == C2SPacketId.UpdateSettings) { return (IPacketData?)(object)new UpdateSeedPacketData(); } return null; } public void CheckForMistUpdate() { if (AdjustSeed && playersInMist.Count <= 0) { SetSeed(GenerateNewSeed()); } } public int GenerateNewSeed() { if (rng == null) { rng = new Random(DateTime.UtcNow.Millisecond); } return rng.Next(1001); } public static void SetSeed(int value) { if (Instance != null) { Instance.CurrentSeed = value; Instance.sender?.BroadcastSingleData(S2CPacketId.UpdateSeed, (IPacketData)(object)new UpdateSeedPacketData { Seed = Instance.CurrentSeed, AdjustSeed = Instance.AdjustSeed }); } } private static bool IsMist(string scene) { if (scene.StartsWith("Dust_Maze") && !scene.EndsWith("_Last_Hall")) { return !scene.EndsWith("_entrance"); } return false; } } public enum S2CPacketId { UpdateSeed } public enum C2SPacketId { UpdateSettings } public class UpdateSeedPacketData : IPacketData { public bool IsReliable => true; public bool DropReliableDataIfNewerExists => true; public int Seed { get; set; } public bool AdjustSeed { get; set; } public void ReadData(IPacket packet) { Seed = packet.ReadInt(); AdjustSeed = packet.ReadBool(); } public void WriteData(IPacket packet) { packet.Write(Seed); packet.Write(AdjustSeed); } } } namespace FixedMistRNG.SSMPAddon.Command.Server { public class SetSeedCommand : IServerCommand, ICommand { [CompilerGenerated] private FixedMistServerAddon P; public bool AuthorizedOnly => false; public string Trigger => "/setMistSeed"; public string[] Aliases => new string[5] { "/setmistseed", "/SetMistSeed", "/SETMISTSEED", "/mistseed", "/mistSeed" }; public SetSeedCommand(FixedMistServerAddon addon) { P = addon; base..ctor(); } public void Execute(ICommandSender commandSender, string[] arguments) { int result; if (!commandSender.IsAuthorized || arguments.Length < 2) { commandSender.SendMessage($"Current seed: {P.CurrentSeed}!"); } else if (int.TryParse(arguments[1], out result)) { FixedMistServerAddon.SetSeed(result); commandSender.SendMessage($"Successfully changed the seed to {P.CurrentSeed}!"); } else { commandSender.SendMessage("Please provide an integer value."); } } } }