Decompiled source of PersistentBuffs v1.0.0

PersistentBuffs.dll

Decompiled 4 days ago
using 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));
			}
		}
	}
}