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;
}
}
}
}