Decompiled source of ExperienceTuner v1.1.0

BepInEx/plugins/ExperienceTuner/ExperienceTuner.dll

Decompiled 3 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ExperienceTunerProject")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ExperienceTunerProject")]
[assembly: AssemblyTitle("ExperienceTunerProject")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 SkillTuner
{
	[BepInPlugin("yourname.valheim.skilltuner", "Skill Tuner", "1.0.0")]
	public sealed class SkillTunerPlugin : BaseUnityPlugin
	{
		private static class DeathPenaltyScope
		{
			private static int _depth;

			public static bool Active => _depth > 0;

			public static void Enter()
			{
				_depth++;
			}

			public static void Exit()
			{
				if (_depth > 0)
				{
					_depth--;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class PlayerOnDeathPatch
		{
			private static void Prefix()
			{
				DeathPenaltyScope.Enter();
			}

			private static void Postfix()
			{
				DeathPenaltyScope.Exit();
			}
		}

		[HarmonyPatch(typeof(Skills), "RaiseSkill")]
		private static class RaiseSkillPatch
		{
			private static void Prefix(ref float factor)
			{
				factor *= Mathf.Max(0f, EffectiveExperienceMultiplier);
			}
		}

		[HarmonyPatch(typeof(Skills), "LowerAllSkills", new Type[] { typeof(float) })]
		private static class LowerAllSkillsPatch
		{
			private static void Prefix(ref float factor)
			{
				if (DeathPenaltyScope.Active)
				{
					factor *= Mathf.Max(0f, EffectiveDeathPenaltyMultiplier);
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class ZNetAwakePatch
		{
			private static void Postfix(ZNet __instance)
			{
				EnsureRoutedRpcRegistration();
				if (__instance.IsServer())
				{
					BroadcastMultipliers();
				}
				else
				{
					RequestMultipliersFromServer();
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewPeer")]
		private static class ZNetOnNewPeerPatch
		{
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				HandlePeerConnected(__instance, peer);
			}
		}

		private const string PluginGuid = "yourname.valheim.skilltuner";

		private const string PluginName = "Skill Tuner";

		private const string PluginVersion = "1.0.0";

		private const string RpcSyncMultipliers = "SkillTuner Sync";

		private const string RpcRequestMultipliers = "SkillTuner Request";

		internal static ConfigEntry<float>? ExperienceMultiplier;

		internal static ConfigEntry<float>? DeathPenaltyMultiplier;

		private Harmony? _harmony;

		private bool _registeredSettingHandler;

		private static bool _rpcRegistered;

		private static MethodInfo? _getServerPeerIdMethod;

		internal static SkillTunerPlugin? Instance { get; private set; }

		internal static float EffectiveExperienceMultiplier { get; private set; } = 1f;


		internal static float EffectiveDeathPenaltyMultiplier { get; private set; } = 1f;


		private void Awake()
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			Instance = this;
			ExperienceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("SkillGain", "ExperienceMultiplier", 1f, "Multiplier applied to all skill XP gains. Use values >1 for faster progression, 1 for default, 0 to disable XP gain.");
			DeathPenaltyMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("SkillLoss", "DeathPenaltyMultiplier", 1f, "Multiplier applied to the skill-loss factor on death. 1 keeps the vanilla penalty, 0 removes it entirely, values >1 increase it.");
			ApplyMultipliersFromConfig();
			((BaseUnityPlugin)this).Config.SettingChanged += OnConfigSettingChanged;
			_registeredSettingHandler = true;
			_harmony = new Harmony("yourname.valheim.skilltuner");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Skill Tuner 1.0.0 loaded.");
		}

		private void OnDestroy()
		{
			if (_registeredSettingHandler)
			{
				((BaseUnityPlugin)this).Config.SettingChanged -= OnConfigSettingChanged;
				_registeredSettingHandler = false;
			}
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}

		private static void OnConfigSettingChanged(object? sender, SettingChangedEventArgs e)
		{
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
			{
				SkillTunerPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogDebug((object)"Ignoring local config change on client; waiting for host sync.");
				}
			}
			else
			{
				ApplyMultipliersFromConfig();
				EnsureRoutedRpcRegistration();
				BroadcastMultipliers();
			}
		}

		private static void ApplyMultipliersFromConfig()
		{
			float exp = ExperienceMultiplier?.Value ?? 1f;
			float death = DeathPenaltyMultiplier?.Value ?? 1f;
			ApplyMultipliers(exp, death);
		}

		private static void ApplyMultipliers(float exp, float death)
		{
			EffectiveExperienceMultiplier = Mathf.Max(0f, exp);
			EffectiveDeathPenaltyMultiplier = Mathf.Max(0f, death);
			SkillTunerPlugin? instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Skill multipliers set -> XP: {EffectiveExperienceMultiplier:0.###}, Death: {EffectiveDeathPenaltyMultiplier:0.###}");
			}
		}

		private static void HandlePeerConnected(ZNet net, ZNetPeer peer)
		{
			if (peer?.m_rpc != null)
			{
				if (net.IsServer())
				{
					EnsureRoutedRpcRegistration();
					SendMultipliers(peer.m_uid);
				}
				else
				{
					RequestMultipliersFromServer();
				}
			}
		}

		private static void RequestMultipliersFromServer()
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			EnsureRoutedRpcRegistration();
			long serverPeerId = GetServerPeerId();
			if (serverPeerId == 0L)
			{
				return;
			}
			try
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC(serverPeerId, "SkillTuner Request", Array.Empty<object>());
				}
			}
			catch (Exception arg)
			{
				SkillTunerPlugin? instance2 = Instance;
				if (instance2 != null)
				{
					((BaseUnityPlugin)instance2).Logger.LogWarning((object)$"Failed to request multipliers from host: {arg}");
				}
			}
		}

		private static void SendMultipliers(long targetPeerId)
		{
			if (targetPeerId == 0L)
			{
				return;
			}
			EnsureRoutedRpcRegistration();
			try
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC(targetPeerId, "SkillTuner Sync", new object[2] { EffectiveExperienceMultiplier, EffectiveDeathPenaltyMultiplier });
				}
			}
			catch (Exception arg)
			{
				SkillTunerPlugin? instance2 = Instance;
				if (instance2 != null)
				{
					((BaseUnityPlugin)instance2).Logger.LogWarning((object)$"Failed to send multipliers to peer: {arg}");
				}
			}
		}

		private static void BroadcastMultipliers()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			EnsureRoutedRpcRegistration();
			foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers())
			{
				if (connectedPeer?.m_rpc != null)
				{
					SendMultipliers(connectedPeer.m_uid);
				}
			}
		}

		private static void RpcHandleRequestMultipliers(long sender)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				SendMultipliers(sender);
			}
		}

		private static void RpcReceiveMultipliers(long sender, float experienceMultiplier, float deathMultiplier)
		{
			ApplyMultipliers(experienceMultiplier, deathMultiplier);
		}

		private static void EnsureRoutedRpcRegistration()
		{
			if (_rpcRegistered || ZRoutedRpc.instance == null)
			{
				return;
			}
			try
			{
				ZRoutedRpc.instance.Register("SkillTuner Request", (Action<long>)RpcHandleRequestMultipliers);
				ZRoutedRpc.instance.Register<float, float>("SkillTuner Sync", (Action<long, float, float>)RpcReceiveMultipliers);
				_rpcRegistered = true;
			}
			catch (Exception arg)
			{
				SkillTunerPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)$"Failed to register SkillTuner routed RPCs: {arg}");
				}
			}
		}

		private static long GetServerPeerId()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return 0L;
			}
			if ((object)_getServerPeerIdMethod == null)
			{
				_getServerPeerIdMethod = AccessTools.Method(typeof(ZNet), "GetServerPeerID", (Type[])null, (Type[])null);
			}
			if (_getServerPeerIdMethod == null)
			{
				return 0L;
			}
			try
			{
				return (_getServerPeerIdMethod.Invoke(ZNet.instance, Array.Empty<object>()) is long num) ? num : 0;
			}
			catch (Exception arg)
			{
				SkillTunerPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)$"Failed to resolve server peer ID: {arg}");
				}
				return 0L;
			}
		}
	}
}