Decompiled source of FixedMistRNG v1.0.1

FixedMistRNG.dll

Decompiled 2 weeks ago
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<bool> adjustSeedElement = new ChoiceElement<bool>(LocalizedText.Raw("Randomize Seed"), (IChoiceModel<bool>)(object)ChoiceModels.ForBool("No", "Yes"));
			SliderElement<int> seedElement = new SliderElement<int>(LocalizedText.Raw("Seed"), (SliderModel<int>)(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<int> obj2 = seedElement;
				bool flag2 = (((SelectableElement)adjustSeedElement).Interactable = FixedMistClientAddon.IsConnected());
				bool flag4 = (((SelectableElement)obj2).Interactable = flag2);
				descriptionText.text = (flag4 ? adjustSeedDescription : "");
				((SelectableValueElement<bool>)(object)adjustSeedElement).Value = FixedMistClientAddon.Instance != null && FixedMistClientAddon.Instance.AdjustSeed;
				((SelectableValueElement<int>)(object)seedElement).Value = ((FixedMistClientAddon.Instance == null) ? (-1) : FixedMistClientAddon.Instance.CurrentSeed);
			};
			((AbstractMenuScreen)obj).OnHide += delegate
			{
				if (FixedMistClientAddon.IsConnected())
				{
					FixedMistClientAddon.Instance.SendOptionUpdate(((SelectableValueElement<int>)(object)seedElement).Value, ((SelectableValueElement<bool>)(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<FsmStateAction>)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<FsmStateAction>)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<FsmStateAction>)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<SendRandomEvent>(state, 0);
			FsmEvent[] originalEvents = action.events;
			List<FsmEvent> 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<C2SPacketId>? sender;

		private IClientAddonNetworkReceiver<S2CPacketId>? 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<C2SPacketId>((ClientAddon)(object)this);
			receiver = clientApi.NetClient.GetNetworkReceiver<S2CPacketId>((ClientAddon)(object)this, (Func<S2CPacketId, IPacketData>)InstantiatePacket);
			RegisterPacketHandlers();
		}

		private void RegisterPacketHandlers()
		{
			receiver?.RegisterPacketHandler<UpdateSeedPacketData>(S2CPacketId.UpdateSeed, (GenericClientPacketHandler<UpdateSeedPacketData>)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<TransitionPoint>)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<S2CPacketId>? sender;

		private IServerAddonNetworkReceiver<C2SPacketId>? receiver;

		private readonly List<ushort> playersInMist = new List<ushort>();

		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<IServerCommand>)(object)api.CommandManager).RegisterCommand((IServerCommand)(object)new SetSeedCommand(this));
			sender = api.NetServer.GetNetworkSender<S2CPacketId>((ServerAddon)(object)this);
			receiver = api.NetServer.GetNetworkReceiver<C2SPacketId>((ServerAddon)(object)this, (Func<C2SPacketId, IPacketData>)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<UpdateSeedPacketData>(C2SPacketId.UpdateSettings, (GenericServerPacketHandler<UpdateSeedPacketData>)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 <addon>P;

		public bool AuthorizedOnly => false;

		public string Trigger => "/setMistSeed";

		public string[] Aliases => new string[5] { "/setmistseed", "/SetMistSeed", "/SETMISTSEED", "/mistseed", "/mistSeed" };

		public SetSeedCommand(FixedMistServerAddon addon)
		{
			<addon>P = addon;
			base..ctor();
		}

		public void Execute(ICommandSender commandSender, string[] arguments)
		{
			int result;
			if (!commandSender.IsAuthorized || arguments.Length < 2)
			{
				commandSender.SendMessage($"Current seed: {<addon>P.CurrentSeed}!");
			}
			else if (int.TryParse(arguments[1], out result))
			{
				FixedMistServerAddon.SetSeed(result);
				commandSender.SendMessage($"Successfully changed the seed to {<addon>P.CurrentSeed}!");
			}
			else
			{
				commandSender.SendMessage("Please provide an integer value.");
			}
		}
	}
}