using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using GTFO.API; using GameData; using HarmonyLib; using Microsoft.CodeAnalysis; using SNetwork; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("GlowierSticks")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("GlowierSticks")] [assembly: AssemblyTitle("GlowierSticks")] [assembly: AssemblyVersion("1.0.0.0")] 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; } } } namespace GlowierSticks { public static class Configuration { public static class LocalConfig { private static readonly ConfigFile configFile; public const double DEFAULT_GLOWSTICK_RANGE = 15.0; private static readonly ConfigEntry _glowstickRange; public const bool DEFAULT_DO_LIGHTNESS_NORMALIZATION = true; private static readonly ConfigEntry _doLightnessRangeNormalization; public const float DEFAULT_GLOWSTICK_FADE_IN_TIME = 3f; private static readonly ConfigEntry _glowstickFadeInTime; public const float DEFAULT_GLOWSTICK_DURATION = 237f; private static readonly ConfigEntry _glowstickDuration; public const float DEFAULT_GLOWSTICK_FADE_OUT_TIME = 60f; private static readonly ConfigEntry _glowstickFadeOutTime; public const int DEFAULT_GLOWSTICK_COUNT_MAX = 8; private static readonly ConfigEntry _glowstickCountMax; public const int DEFAULT_GLOWSTICK_COUNT_MIN = 7; private static readonly ConfigEntry _glowstickCountMin; private static readonly ConfigEntry _doDebug; public static double GlowstickRange => _glowstickRange.Value; public static bool DoLightnessRangeNormalization => _doLightnessRangeNormalization.Value; public static float GlowstickFadeInTime => _glowstickFadeInTime.Value; public static float GlowstickDuration => _glowstickDuration.Value; public static float GlowstickFadeOutTime => _glowstickFadeOutTime.Value; public static int GlowstickCountMax => _glowstickCountMax.Value; public static int GlowstickCountMin => _glowstickCountMin.Value; public static bool DoDebug => _doDebug.Value; static LocalConfig() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown configFile = new ConfigFile(Path.Combine(Paths.ConfigPath, "GlowierSticks.cfg"), true); _glowstickRange = configFile.Bind("General Settings", "Target glowstick range", 15.0, "Total range of glowstick light, in meters (base game uses 5m); depending on color, brightly lit area reaches up to ~75% of this distance"); _doLightnessRangeNormalization = configFile.Bind("General Settings", "Light range normalization", true, "Whether to adjust glowstick light range so that every glowstick light color visually reaches about as far as a base-game yellow glowstick would"); _glowstickFadeInTime = configFile.Bind("General Settings", "Glowstick fade-in time", 3f, "Time required for a glowstick to reach full brightness from the moment it's thrown, in seconds (base game uses 2)"); _glowstickDuration = configFile.Bind("General Settings", "Glowstick duration", 237f, "Time glowstick light will shine for at full brightness, in seconds (base game uses 60)"); _glowstickFadeOutTime = configFile.Bind("General Settings", "Glowstick fade-out time", 60f, "Time over which glowstick light will gradually fade out once its duration expires; (base game uses 3)"); _glowstickCountMax = configFile.Bind("Generation Settings", "Glowstick maximum count", 8, "Maximum number of glowsticks in a randomly generated pack"); _glowstickCountMin = configFile.Bind("Generation Settings", "Glowstick minimum count", 7, "Miminum number of glowsticks in a randomly generated pack"); _doDebug = configFile.Bind("Misc Settings", "Debug output", false, "Whether to output various debugging messages"); } } public static class RemoteConfig { internal struct ConfigPacket { public double GlowstickRange; public bool DoLightnessRangeNormalization; public float GlowstickFadeInTime; public float GlowstickDuration; public float GlowstickFadeOutTime; public int GlowstickCountMax; public int GlowstickCountMin; public ConfigPacket() { GlowstickRange = LocalConfig.GlowstickRange; DoLightnessRangeNormalization = LocalConfig.DoLightnessRangeNormalization; GlowstickFadeInTime = LocalConfig.GlowstickFadeInTime; GlowstickDuration = LocalConfig.GlowstickDuration; GlowstickFadeOutTime = LocalConfig.GlowstickFadeOutTime; GlowstickCountMax = LocalConfig.GlowstickCountMax; GlowstickCountMin = LocalConfig.GlowstickCountMin; } } private static bool _isReady = false; private static double _glowstickRange = 15.0; private static bool _doLightnessRangeNormalization = true; private static float _glowstickFadeInTime = 3f; private static float _glowstickDuration = 237f; private static float _glowstickFadeOutTime = 60f; private static int _glowstickCountMax = 8; private static int _glowstickCountMin = 7; public static bool IsReady => _isReady; public static double GlowstickRange => _glowstickRange; public static bool DoLightnessRangeNormalization => _doLightnessRangeNormalization; public static float GlowstickFadeInTime => _glowstickFadeInTime; public static float GlowstickDuration => _glowstickDuration; public static float GlowstickFadeOutTime => _glowstickFadeOutTime; public static int GlowstickCountMax => _glowstickCountMax; public static int GlowstickCountMin => _glowstickCountMin; internal static void LoadRemoteConfig(ConfigPacket packet) { if (_isReady) { Logging.Warning("New remote config received before the current was invalidated!"); return; } Logging.Debug("Loading remote settings:\n" + $"GlowstickRange = {packet.GlowstickRange}\n" + $"DoLightnessNormalization = {packet.DoLightnessRangeNormalization}\n" + $"GlowstickFadeInTime = {packet.GlowstickFadeInTime}\n" + $"GlowstickDuration = {packet.GlowstickDuration}\n" + $"GlowstickFadeOutTime = {packet.GlowstickFadeOutTime}\n" + $"GlowstickCountMax = {packet.GlowstickCountMax}\n" + $"GlowstickCountMin = {packet.GlowstickCountMin}"); _glowstickRange = packet.GlowstickRange; _doLightnessRangeNormalization = packet.DoLightnessRangeNormalization; _glowstickFadeInTime = packet.GlowstickFadeInTime; _glowstickDuration = packet.GlowstickDuration; _glowstickFadeOutTime = packet.GlowstickFadeOutTime; _glowstickCountMax = packet.GlowstickCountMax; _glowstickCountMin = packet.GlowstickCountMin; _isReady = true; } internal static void InvalidateRemoteConfig() { Logging.Debug("Invalidating remote config"); _isReady = false; } } private static readonly HashSet TRACKED_GLOWSTICK_PIDS = new HashSet { 114u, 130u, 167u, 174u }; public static double GlowstickRange => SNet.IsMaster ? LocalConfig.GlowstickRange : RemoteConfig.GlowstickRange; public static bool DoLightnessRangeNormalization => SNet.IsMaster ? LocalConfig.DoLightnessRangeNormalization : RemoteConfig.DoLightnessRangeNormalization; public static float GlowstickFadeInTime => SNet.IsMaster ? LocalConfig.GlowstickFadeInTime : RemoteConfig.GlowstickFadeInTime; public static float GlowstickDuration => SNet.IsMaster ? LocalConfig.GlowstickDuration : RemoteConfig.GlowstickDuration; public static float GlowstickFadeOutTime => SNet.IsMaster ? LocalConfig.GlowstickFadeOutTime : RemoteConfig.GlowstickFadeOutTime; public static int GlowstickCountMax => SNet.IsMaster ? LocalConfig.GlowstickCountMax : RemoteConfig.GlowstickCountMax; public static int GlowstickCountMin => SNet.IsMaster ? LocalConfig.GlowstickCountMin : RemoteConfig.GlowstickCountMin; public static HashSet TrackedGlowstickPIDs => TRACKED_GLOWSTICK_PIDS; public static bool IsEligibleGlowstick(ItemDataBlock itemDataBlock) { return TRACKED_GLOWSTICK_PIDS.Contains(((GameDataBlockBase)(object)itemDataBlock).persistentID); } } [BepInPlugin("CinnamonSnowball.GlowierSticks", "GlowierSticks", "0.8.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] internal sealed class Plugin : BasePlugin { internal static class GlowstickTweaks { [HarmonyPatch] internal static class GlowstickPatches { [HarmonyPatch(typeof(SNet_SyncManager), "OnFoundMaster")] [HarmonyPostfix] private static void PostFoundMaster() { if (SNet.IsMaster) { ModifyTemplates(); } } internal static void OnReceiveConfigPacket() { ModifyTemplates(); } [HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")] [HarmonyPostfix] private static void PostLeaveHub() { ResetTemplates(); } [HarmonyPatch(typeof(GlowstickInstance), "Setup")] [HarmonyPostfix] private static void PostGlowstickInstanceSetup(GlowstickInstance __instance) { if (!SNet.IsMaster && !Configuration.RemoteConfig.IsReady) { Logging.Debug("Aborting glowstick modification: in an unmodded lobby!"); return; } if (!Configuration.IsEligibleGlowstick(((Item)__instance).ItemDataBlock)) { Logging.Debug("Aborting glowstick modification: persistent ID not in list of glowsticks to modify!"); return; } Logging.Debug("Setting up thrown glowstick..."); double num = 1.0; if (Configuration.DoLightnessRangeNormalization) { num = CalculateLightnessRangeMultiplier(__instance); Logging.Debug($"Lightness range multiplier: {Math.Round(num, 3)}"); } double num2 = Configuration.GlowstickRange * num / 5.0; Logging.Debug($"Light range: {Math.Round(__instance.s_lightRange, 3)} → {Math.Round((double)__instance.s_lightRange * num2, 3)}{(Configuration.DoLightnessRangeNormalization ? " (lightness-normalized)" : "")}"); __instance.s_lightRange *= (float)num2; float num3 = Configuration.GlowstickDuration / 60f; Logging.Debug($"Duration: {Math.Round(__instance.s_lightLifeTime, 3)} → {Math.Round(__instance.s_lightLifeTime * num3, 3)}"); __instance.s_lightLifeTime *= num3; Logging.Debug("Setup done!"); } [HarmonyPatch(typeof(GlowstickInstance), "Update")] [HarmonyPrefix] private static void PreGlowstickInstanceUpdate(GlowstickInstance __instance) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 if ((SNet.IsMaster || Configuration.RemoteConfig.IsReady) && Configuration.IsEligibleGlowstick(((Item)__instance).ItemDataBlock)) { if ((int)__instance.m_state == 1) { __instance.m_progressionSpeed = 1f / Configuration.GlowstickFadeInTime; } else if ((int)__instance.m_state == 3) { __instance.m_progressionSpeed = 1f / Configuration.GlowstickFadeOutTime; } } } } private const double IDEAL_LIGHT_COLOR_OKLCH_LIGHTNESS = 0.9; private const double VANILLA_GLOWSTICK_RANGE = 5.0; private const float VANILLA_GLOWSTICK_DURATION = 60f; private static readonly Dictionary> baseAmmoCounts = new Dictionary>(); private static double CalculateOKLCHLightness(Color lightColor) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) double num = lightColor.r; double num2 = lightColor.g; double num3 = lightColor.b; double num4 = ((num <= 0.04045) ? (num / 12.92) : Math.Pow((num + 0.055) / 1.055, 2.4)); double num5 = ((num2 <= 0.04045) ? (num2 / 12.92) : Math.Pow((num2 + 0.055) / 1.055, 2.4)); double num6 = ((num3 <= 0.04045) ? (num3 / 12.92) : Math.Pow((num3 + 0.055) / 1.055, 2.4)); double num7 = Math.Cbrt(num4 * 0.4122214708 + num5 * 0.5363325363 + num6 * 0.0514459929); double num8 = Math.Cbrt(num4 * 0.2119034982 + num5 * 0.6806995451 + num6 * 0.1073969566); double num9 = Math.Cbrt(num4 * 0.0883024619 + num5 * 0.2817188376 + num6 * 0.6299787005); return num7 * 0.2104542553 + num8 * 0.793617785 - num9 * 0.0040720468; } private static double CalculateLightnessRangeMultiplier(GlowstickInstance glowstick) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) double num = CalculateOKLCHLightness(glowstick.m_LightColorTarget); Logging.Debug($"OKLCH lightness of glowstick light color: {Math.Round(num, 3)}"); return Math.Sqrt(0.9 / num); } internal static void ModifyTemplates() { Logging.Debug("Modifying glowstick templates..."); int num = Configuration.GlowstickCountMax + 1; int glowstickCountMin = Configuration.GlowstickCountMin; foreach (uint trackedGlowstickPID in Configuration.TrackedGlowstickPIDs) { ItemDataBlock block = GameDataBlockBase.GetBlock(trackedGlowstickPID); if (!baseAmmoCounts.ContainsKey(trackedGlowstickPID)) { baseAmmoCounts[trackedGlowstickPID] = new List { block.ConsumableAmmoMax, block.ConsumableAmmoMin }; } Logging.Debug($"{((GameDataBlockBase)(object)block).name}({trackedGlowstickPID}) ammo: max {block.ConsumableAmmoMax} → {num}; min {block.ConsumableAmmoMin} → {glowstickCountMin}"); block.ConsumableAmmoMax = num; block.ConsumableAmmoMin = glowstickCountMin; } Logging.Debug("Template modification done!"); } internal static void ResetTemplates() { Logging.Debug("Resetting glowstick templates..."); foreach (uint trackedGlowstickPID in Configuration.TrackedGlowstickPIDs) { ItemDataBlock block = GameDataBlockBase.GetBlock(trackedGlowstickPID); List list = baseAmmoCounts[trackedGlowstickPID]; Logging.Debug($"{((GameDataBlockBase)(object)block).name}({trackedGlowstickPID}): max {block.ConsumableAmmoMax} → {list[0]}; min {block.ConsumableAmmoMin} → {list[1]}"); block.ConsumableAmmoMax = list[0]; block.ConsumableAmmoMin = list[1]; } Logging.Debug("Template reset done!"); } } public const string MOD_NAME = "GlowierSticks"; public const string MOD_AUTHOR = "CinnamonSnowball"; public const string MOD_GUID = "CinnamonSnowball.GlowierSticks"; public const string MOD_VERSION = "0.8.1"; public override void Load() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown Harmony val = new Harmony("CinnamonSnowball.GlowierSticks"); val.PatchAll(); AssetAPI.OnStartupAssetsLoaded += AssetAPI_OnStartupAssetsLoaded; Logging.Info($"GlowierSticks loaded with {val.GetPatchedMethods().Count()} patches!"); } private void AssetAPI_OnStartupAssetsLoaded() { RemoteConfigSync.Init(); } } internal static class Logging { private static readonly ManualLogSource logger = Logger.CreateLogSource("GlowierSticks"); public static void Log(string format, params object[] args) { Log(string.Format(format, args)); } public static void Log(string str) { if (logger != null) { logger.Log((LogLevel)8, (object)str); } } public static void Warning(string format, params object[] args) { Warning(string.Format(format, args)); } public static void Warning(string str) { if (logger != null) { logger.Log((LogLevel)4, (object)str); } } public static void Error(string format, params object[] args) { Error(string.Format(format, args)); } public static void Error(string str) { if (logger != null) { logger.Log((LogLevel)2, (object)str); } } public static void Info(string format, params object[] args) { Info(string.Format(format, args)); } public static void Info(string str) { if (logger != null) { logger.Log((LogLevel)16, (object)str); } } public static void Debug(string format, params object[] args) { Debug(string.Format(format, args)); } public static void Debug(string str) { if (Configuration.LocalConfig.DoDebug && logger != null) { logger.Log((LogLevel)32, (object)str); } } } [HarmonyPatch] internal static class RemoteConfigSync { [HarmonyPatch] private static class RemoteConfigSyncPatches { [HarmonyPatch(typeof(SNet_SyncManager), "OnPlayerSpawnedAgent")] [HarmonyPostfix] private static void PostOnPlayerSpawnedAgent(SNet_Player player) { if (SNet.IsMaster && !player.IsLocal && !player.IsBot) { SendConfigPacket(player); } } [HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")] [HarmonyPostfix] private static void PostLeaveHub() { Configuration.RemoteConfig.InvalidateRemoteConfig(); } } private static readonly string SYNC_EVENT_NAME = "GLOWIERSTICKS_CONFIG_SYNC"; internal static void Init() { NetworkAPI.RegisterEvent(SYNC_EVENT_NAME, (Action)ReceiveConfigPacket); } internal static void SendConfigPacket(SNet_Player target) { Logging.Debug($"Sending config packet to player #{target.PlayerSlotIndex() + 1}"); NetworkAPI.InvokeEvent(SYNC_EVENT_NAME, new Configuration.RemoteConfig.ConfigPacket(), target, (SNet_ChannelType)2); } internal static void ReceiveConfigPacket(ulong sender, Configuration.RemoteConfig.ConfigPacket packet) { SNet_Player val = default(SNet_Player); if (!SNet.TryGetPlayer(sender, ref val)) { Logging.Warning($"Received config packet from lookup ID {sender} but they're not a player in our session!"); } else if ((Object)(object)val != (Object)(object)SNet.Master) { Logging.Warning($"Received config packet from player #{val.PlayerSlotIndex() + 1} but they're not the host!"); } else { Logging.Debug($"Config packet received from host player #{val.PlayerSlotIndex() + 1}"); Configuration.RemoteConfig.LoadRemoteConfig(packet); Plugin.GlowstickTweaks.GlowstickPatches.OnReceiveConfigPacket(); } } } }