Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PersistentBuffs v1.0.0
PersistentBuffs.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PersistentBuffs")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PersistentBuffs")] [assembly: AssemblyTitle("PersistentBuffs")] [assembly: AssemblyVersion("1.0.0.0")] [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 PersistentBuffs { [BepInPlugin("com.dreich.persistentbuffs", "Persistent Buffs", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class PersistentBuffsPlugin : BaseUnityPlugin { public const string PluginGuid = "com.dreich.persistentbuffs"; public const string PluginName = "Persistent Buffs"; public const string PluginVersion = "1.0.0"; internal const string ConfigSyncGuid = "_shudnal.ConditionalConfigSync"; internal static ManualLogSource Log; internal static bool ConfigSyncAvailable; private static ConfigEntry<bool> lockConfigurationSetting; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> KeepRested; internal static ConfigEntry<bool> KeepMeads; internal static ConfigEntry<string> ExtraEffects; internal static ConfigEntry<string> ExcludedEffects; internal static ConfigEntry<bool> LogEvents; private static Harmony harmony; private void Awake() { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ConfigSyncAvailable = Chainloader.PluginInfos.ContainsKey("_shudnal.ConditionalConfigSync"); if (ConfigSyncAvailable) { ConfigSyncCompat.Initialize("com.dreich.persistentbuffs", "Persistent Buffs", "1.0.0"); } else { Log.LogInfo((object)"ConditionalConfigSync not found; configuration will not be synced from the server."); } lockConfigurationSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock Configuration", true, "If on, the configuration is locked and can be changed by server admins only. Requires ConditionalConfigSync."); if (ConfigSyncAvailable) { ConfigSyncCompat.AddLockingConfigEntry(lockConfigurationSetting); } Enabled = BindServerControlled("General", "Enabled", defaultValue: true, "Keep the buffs chosen below when you quit or disconnect. Their timers are paused while you are offline and continue when you log back in."); KeepRested = BindServerControlled("Buffs", "KeepRested", defaultValue: true, "Keep the Rested buff from comfort, with its remaining time."); KeepMeads = BindServerControlled("Buffs", "KeepMeads", defaultValue: true, "Keep the buffs from meads and other drinkable or edible items that give a status effect, with their remaining time. This also keeps the mead's cooldown, so you cannot drink another of the same kind right after logging back in."); ExtraEffects = BindServerControlled("Buffs", "ExtraEffects", "", "Comma-separated status effect names (e.g. SE_Something) to keep as well. Only effects with a timer are kept."); ExcludedEffects = BindServerControlled("Buffs", "ExcludedEffects", "", "Comma-separated status effect names that are never kept, even if they match the settings above."); LogEvents = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogEvents", false, "Log the buffs that are saved and restored."); harmony = new Harmony("com.dreich.persistentbuffs"); harmony.PatchAll(); } private ConfigEntry<T> BindServerControlled<T>(string section, string key, T defaultValue, string description) { ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description); if (ConfigSyncAvailable) { ConfigSyncCompat.AddServerControlled<T>(val); } return val; } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } harmony = null; } } internal static class ConfigSyncCompat { private static object configSync; [MethodImpl(MethodImplOptions.NoInlining)] internal static void Initialize(string guid, string name, string version) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown configSync = (object)new ConfigSync(guid) { DisplayName = name, CurrentVersion = version, MinimumRequiredVersion = version, ModRequired = false, ModRequirementMode = (ModRequirementMode)1 }; } [MethodImpl(MethodImplOptions.NoInlining)] internal static void AddLockingConfigEntry(ConfigEntry<bool> entry) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) ((ConditionalConfigSync)(ConfigSync)configSync).AddLockingConfigEntry<bool>(entry); } [MethodImpl(MethodImplOptions.NoInlining)] internal static void AddServerControlled<T>(ConfigEntry<T> entry) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) ((ConditionalConfigSync)(ConfigSync)configSync).AddConfigEntry<T>(entry, (ConfigSyncMode)0); } } internal sealed class SavedBuff { public int NameHash; public float Duration; public float Elapsed; } internal static class BuffStore { internal const string CustomDataKey = "PersistentBuffs_Effects"; private static readonly FieldRef<StatusEffect, float> TimeRef = AccessTools.FieldRefAccess<StatusEffect, float>("m_time"); private static readonly HashSet<int> meadEffects = new HashSet<int>(); private static readonly List<SavedBuff> pending = new List<SavedBuff>(); private static string extraParsedFrom; private static string excludedParsedFrom; private static HashSet<int> extraEffects = new HashSet<int>(); private static HashSet<int> excludedEffects = new HashSet<int>(); internal static void CollectMeadEffects(ObjectDB db) { if ((Object)(object)db == (Object)null || db.m_items == null) { return; } meadEffects.Clear(); foreach (GameObject item in db.m_items) { ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null); StatusEffect val2 = (((Object)(object)val != (Object)null && val.m_itemData != null && val.m_itemData.m_shared != null) ? val.m_itemData.m_shared.m_consumeStatusEffect : null); if ((Object)(object)val2 != (Object)null) { meadEffects.Add(val2.NameHash()); } } meadEffects.Remove(SEMan.s_statusEffectRested); } private static HashSet<int> ParseNames(string raw, ref string parsedFrom, HashSet<int> current) { raw = raw ?? ""; if (raw == parsedFrom) { return current; } HashSet<int> hashSet = new HashSet<int>(); string[] array = raw.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { hashSet.Add(StringExtensionMethods.GetStableHashCode(text)); } } parsedFrom = raw; return hashSet; } private static bool ShouldKeep(StatusEffect effect) { if ((Object)(object)effect == (Object)null || effect.m_ttl <= 0f || effect.IsDone()) { return false; } int num = effect.NameHash(); excludedEffects = ParseNames(PersistentBuffsPlugin.ExcludedEffects.Value, ref excludedParsedFrom, excludedEffects); if (excludedEffects.Contains(num)) { return false; } if (num == SEMan.s_statusEffectRested) { return PersistentBuffsPlugin.KeepRested.Value; } if (meadEffects.Contains(num)) { return PersistentBuffsPlugin.KeepMeads.Value; } extraEffects = ParseNames(PersistentBuffsPlugin.ExtraEffects.Value, ref extraParsedFrom, extraEffects); return extraEffects.Contains(num); } internal static void Save(Player player) { if (player.m_customData == null) { return; } player.m_customData.Remove("PersistentBuffs_Effects"); if (!PersistentBuffsPlugin.Enabled.Value || ((Character)player).IsDead()) { return; } SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null) { return; } List<string> list = new List<string>(); foreach (StatusEffect statusEffect in sEMan.GetStatusEffects()) { if (ShouldKeep(statusEffect)) { list.Add(statusEffect.NameHash().ToString(CultureInfo.InvariantCulture) + ":" + statusEffect.m_ttl.ToString("R", CultureInfo.InvariantCulture) + ":" + TimeRef.Invoke(statusEffect).ToString("R", CultureInfo.InvariantCulture)); } } if (list.Count > 0) { player.m_customData["PersistentBuffs_Effects"] = string.Join(";", list.ToArray()); if (PersistentBuffsPlugin.LogEvents.Value) { PersistentBuffsPlugin.Log.LogInfo((object)$"Saved {list.Count} buff(s)."); } } } internal static void TakeFromSave(Player player) { pending.Clear(); if (player.m_customData == null || !player.m_customData.TryGetValue("PersistentBuffs_Effects", out var value)) { return; } player.m_customData.Remove("PersistentBuffs_Effects"); string[] array = value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length == 3 && int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && !(result2 <= 0f) && !(result3 >= result2)) { pending.Add(new SavedBuff { NameHash = result, Duration = result2, Elapsed = Math.Max(0f, result3) }); } } } internal static void Restore(Player player) { if (pending.Count == 0) { return; } List<SavedBuff> list = new List<SavedBuff>(pending); pending.Clear(); if (!PersistentBuffsPlugin.Enabled.Value || ((Character)player).IsDead()) { return; } SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null) { return; } int num = 0; foreach (SavedBuff item in list) { try { StatusEffect val = sEMan.GetStatusEffect(item.NameHash) ?? sEMan.AddStatusEffect(item.NameHash, true, 0, 0f, (short)0); if (!((Object)(object)val == (Object)null)) { val.m_ttl = item.Duration; TimeRef.Invoke(val) = item.Elapsed; num++; } } catch (Exception ex) { PersistentBuffsPlugin.Log.LogWarning((object)("Could not restore a buff: " + ex.Message)); } } if (num > 0 && PersistentBuffsPlugin.LogEvents.Value) { PersistentBuffsPlugin.Log.LogInfo((object)$"Restored {num} buff(s)."); } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDBAwakePatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { BuffStore.CollectMeadEffects(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDBCopyPatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { BuffStore.CollectMeadEffects(__instance); } } [HarmonyPatch(typeof(Player), "Save")] internal static class PlayerSavePatch { private static void Prefix(Player __instance) { try { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { BuffStore.Save(__instance); } } catch (Exception ex) { PersistentBuffsPlugin.Log.LogWarning((object)("Could not save buffs: " + ex.Message)); } } } [HarmonyPatch(typeof(Player), "Load")] internal static class PlayerLoadPatch { private static void Postfix(Player __instance) { try { BuffStore.TakeFromSave(__instance); } catch (Exception ex) { PersistentBuffsPlugin.Log.LogWarning((object)("Could not read saved buffs: " + ex.Message)); } } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class PlayerSpawnedPatch { private static void Postfix(Player __instance) { try { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { BuffStore.Restore(__instance); } } catch (Exception ex) { PersistentBuffsPlugin.Log.LogWarning((object)("Could not restore buffs: " + ex.Message)); } } } }