using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("skill_Limit_Extender")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ca54091feaa9750e9498920a03f4e15779c43cb3")]
[assembly: AssemblyProduct("skill_Limit_Extender")]
[assembly: AssemblyTitle("skill_Limit_Extender")]
[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 SkillLimitExtender
{
[HarmonyPatch(typeof(Skills), "CheatRaiseSkill")]
[HarmonyPatch(new Type[]
{
typeof(string),
typeof(float),
typeof(bool)
})]
internal static class SLE_CheatRaiseSkill_Transpiler
{
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
//IL_01e1: Expected O, but got Unknown
//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
//IL_01f9: Expected O, but got Unknown
//IL_0201: Unknown result type (might be due to invalid IL or missing references)
//IL_020b: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
MethodInfo methodInfo = AccessTools.Method(typeof(SkillConfigManager), "GetCap", (Type[])null, (Type[])null);
FieldInfo fieldInfo = AccessTools.Field(typeof(Skill), "m_info");
FieldInfo fieldInfo2 = AccessTools.Field(typeof(SkillDef), "m_skill");
if (methodInfo == null || fieldInfo == null || fieldInfo2 == null)
{
return list;
}
for (int i = 0; i < list.Count; i++)
{
CodeInstruction val = list[i];
if (!(val.opcode == OpCodes.Ldc_R4) || !(val.operand is float num) || !(Math.Abs(num - 100f) < 0.0001f))
{
continue;
}
bool flag = false;
for (int j = Math.Max(0, i - 5); j < Math.Min(list.Count, i + 5); j++)
{
if (list[j].opcode == OpCodes.Call)
{
object operand = list[j].operand;
if (operand != null && (operand.ToString()?.Contains("Clamp")).GetValueOrDefault())
{
flag = true;
break;
}
}
}
if (!flag)
{
continue;
}
FieldInfo fieldInfo3 = AccessTools.Field(typeof(SkillConfigManager), "DefaultCap");
PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigEntry<int>), "Value");
if (fieldInfo3 != null && propertyInfo != null)
{
List<CodeInstruction> list2 = new List<CodeInstruction>
{
new CodeInstruction(OpCodes.Ldsfld, (object)fieldInfo3),
new CodeInstruction(OpCodes.Callvirt, (object)propertyInfo.GetGetMethod()),
new CodeInstruction(OpCodes.Conv_R4, (object)null)
};
list[i] = list2[0];
list.InsertRange(i + 1, list2.GetRange(1, list2.Count - 1));
i += list2.Count - 1;
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogDebug((object)"[SLE] CheatRaiseSkill: Replaced 100f with DefaultCap");
}
break;
}
}
return list;
}
}
[HarmonyPatch(typeof(Skills), "GetSkillFactor")]
internal static class SLE_Hook_GetSkillFactor
{
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Expected O, but got Unknown
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Expected O, but got Unknown
//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ff: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
MethodInfo methodInfo = AccessTools.Method(typeof(SkillConfigManager), "GetCap", (Type[])null, (Type[])null);
if (methodInfo == null)
{
return list;
}
for (int i = 0; i < list.Count; i++)
{
CodeInstruction val = list[i];
if (val.opcode == OpCodes.Ldc_R4 && val.operand is float num && Math.Abs(num - 100f) < 0.0001f && i + 1 < list.Count && list[i + 1].opcode == OpCodes.Div)
{
List<CodeInstruction> list2 = new List<CodeInstruction>
{
new CodeInstruction(OpCodes.Ldarg_1, (object)null),
new CodeInstruction(OpCodes.Call, (object)methodInfo),
new CodeInstruction(OpCodes.Conv_R4, (object)null)
};
list[i] = list2[0];
list.InsertRange(i + 1, list2.GetRange(1, list2.Count - 1));
i += list2.Count - 1;
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogDebug((object)"[SLE] GetSkillFactor: Replaced 100f with dynamic cap");
}
break;
}
}
return list;
}
}
[HarmonyPatch(typeof(SkillsDialog), "Setup")]
internal static class SLE_Hook_SkillsDialog_LevelBars
{
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_019c: Unknown result type (might be due to invalid IL or missing references)
//IL_01a6: Expected O, but got Unknown
//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
//IL_01bd: Expected O, but got Unknown
//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
//IL_01cf: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
FieldInfo fieldInfo = AccessTools.Field(typeof(SkillConfigManager), "DefaultCap");
PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigEntry<int>), "Value");
if (fieldInfo == null || propertyInfo == null)
{
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogWarning((object)"[SLE] SkillsDialog: Cannot find DefaultCap, skipping patch");
}
return list;
}
int num = 0;
for (int i = 0; i < list.Count - 1; i++)
{
CodeInstruction val = list[i];
CodeInstruction val2 = list[i + 1];
if (!(val.opcode == OpCodes.Ldc_R4) || !(val.operand is float num2) || !(Math.Abs(num2 - 100f) < 0.0001f) || !(val2.opcode == OpCodes.Div))
{
continue;
}
bool flag = false;
for (int j = i + 1; j < Math.Min(list.Count, i + 10); j++)
{
if (list[j].opcode == OpCodes.Callvirt)
{
object operand = list[j].operand;
if (operand != null && (operand.ToString()?.Contains("SetValue")).GetValueOrDefault())
{
flag = true;
break;
}
}
}
if (flag)
{
List<CodeInstruction> list2 = new List<CodeInstruction>
{
new CodeInstruction(OpCodes.Ldsfld, (object)fieldInfo),
new CodeInstruction(OpCodes.Callvirt, (object)propertyInfo.GetGetMethod()),
new CodeInstruction(OpCodes.Conv_R4, (object)null)
};
list[i] = list2[0];
list.InsertRange(i + 1, list2.GetRange(1, list2.Count - 1));
i += list2.Count - 1;
num++;
ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
if (logger2 != null)
{
logger2.LogDebug((object)$"[SLE] SkillsDialog: Replaced 100f #{num} with DefaultCap");
}
}
}
ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
if (logger3 != null)
{
logger3.LogInfo((object)$"[SLE] SkillsDialog: Total 100f replacements: {num}");
}
return list;
}
}
internal static class SLE_SkillsExtensions
{
private static readonly MethodInfo _miGetSkill = AccessTools.Method(typeof(Skills), "GetSkill", new Type[1] { typeof(SkillType) }, (Type[])null);
private static readonly FieldInfo _fiSkillData = AccessTools.Field(typeof(Skills), "m_skillData");
internal static Skill? GetSkillSafe(this Skills skills, SkillType st)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Expected O, but got Unknown
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)skills == (Object)null)
{
return null;
}
if (_miGetSkill != null)
{
try
{
return (Skill)_miGetSkill.Invoke(skills, new object[1] { st });
}
catch
{
}
}
if (_fiSkillData != null)
{
try
{
if (_fiSkillData.GetValue(skills) is IDictionary<SkillType, Skill> dictionary && dictionary.TryGetValue(st, out var value))
{
return value;
}
}
catch
{
}
}
return null;
}
}
[HarmonyPatch(typeof(Skill), "Raise")]
internal static class SLE_Skill_Raise_Transpiler
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Expected O, but got Unknown
//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Expected O, but got Unknown
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Expected O, but got Unknown
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Expected O, but got Unknown
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
//IL_0133: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
FieldInfo fieldInfo = AccessTools.Field(typeof(Skill), "m_info");
FieldInfo fieldInfo2 = AccessTools.Field(typeof(SkillDef), "m_skill");
MethodInfo methodInfo = AccessTools.Method(typeof(SkillConfigManager), "GetCap", (Type[])null, (Type[])null);
if (fieldInfo == null || fieldInfo2 == null || methodInfo == null)
{
return list;
}
for (int i = 0; i < list.Count; i++)
{
CodeInstruction val = list[i];
if (val.opcode == OpCodes.Ldc_R4 && val.operand is float num && Math.Abs(num - 100f) < 0.0001f)
{
List<CodeInstruction> list2 = new List<CodeInstruction>
{
new CodeInstruction(OpCodes.Ldarg_0, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo),
new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo2),
new CodeInstruction(OpCodes.Call, (object)methodInfo),
new CodeInstruction(OpCodes.Conv_R4, (object)null)
};
list[i] = list2[0];
list.InsertRange(i + 1, list2.GetRange(1, list2.Count - 1));
i += list2.Count - 1;
}
}
return list;
}
}
internal static class SkillConfigManager
{
internal static ConfigEntry<bool> ServerConfigLocked = null;
internal static ConfigEntry<int> DefaultCap = null;
internal static ConfigEntry<bool> EnableYamlOverride = null;
private static Dictionary<string, int> _capsByName = new Dictionary<string, int>();
private static bool _initialized;
private static bool _isServerConfig;
internal static void Initialize(ConfigFile config)
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Expected O, but got Unknown
if (!_initialized)
{
ServerConfigLocked = config.Bind<bool>("Server", "LockConfiguration", false, "If true, server forces its configuration to all clients. (Admin Only)");
DefaultCap = config.Bind<int>("General", "DefaultCap", 250, new ConfigDescription("Default skill cap for skills not listed in YAML file", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 10000), Array.Empty<object>()));
EnableYamlOverride = config.Bind<bool>("General", "EnableYamlOverride", true, "Allow YAML file to override individual skill caps");
ReloadFromYaml();
RegisterRPC();
_initialized = true;
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogInfo((object)"[SLE] Lightweight server config initialized");
}
}
}
private static void RegisterRPC()
{
}
internal static void OnConfigReceivedStatic(long sender, int defaultCap, bool enableYaml)
{
OnConfigReceived(sender, defaultCap, enableYaml);
}
internal static void ReloadFromYaml()
{
if (!_isServerConfig)
{
ConfigEntry<bool> enableYamlOverride = EnableYamlOverride;
if (enableYamlOverride == null || enableYamlOverride.Value)
{
_capsByName = YamlExporter.LoadYaml();
return;
}
}
if (_isServerConfig)
{
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogInfo((object)"[SLE] Using server configuration (YAML disabled)");
}
}
}
internal static int GetCap(SkillType st)
{
string text = ((object)(SkillType)(ref st)).ToString();
if (!_isServerConfig)
{
ConfigEntry<bool> enableYamlOverride = EnableYamlOverride;
if (enableYamlOverride != null && enableYamlOverride.Value && _capsByName != null && _capsByName.TryGetValue(text, out var value) && value > 0)
{
return value;
}
}
if (!_isServerConfig)
{
ConfigEntry<bool> enableYamlOverride2 = EnableYamlOverride;
if (enableYamlOverride2 != null && enableYamlOverride2.Value && int.TryParse(text, out var result) && result > 999)
{
int result2;
KeyValuePair<string, int>? keyValuePair = _capsByName?.FirstOrDefault((KeyValuePair<string, int> kv) => !int.TryParse(kv.Key, out result2) && kv.Value > 0);
if (keyValuePair.HasValue && keyValuePair.Value.Value > 0)
{
return keyValuePair.Value.Value;
}
}
}
return DefaultCap?.Value ?? 250;
}
internal static void SendConfigToClients()
{
ZNet instance = ZNet.instance;
if (instance == null || !instance.IsServer())
{
return;
}
ConfigEntry<bool> serverConfigLocked = ServerConfigLocked;
if (serverConfigLocked != null && !serverConfigLocked.Value)
{
return;
}
try
{
int num = DefaultCap?.Value ?? 250;
bool flag = EnableYamlOverride?.Value ?? true;
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "SLE_ConfigSync", new object[2] { num, flag });
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogInfo((object)$"[SLE] Server config sent to clients: DefaultCap={num}, EnableYaml={flag}");
}
}
catch (Exception arg)
{
ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
if (logger2 != null)
{
logger2.LogError((object)$"[SLE] Failed to send config to clients: {arg}");
}
}
}
private static void OnConfigReceived(long sender, int defaultCap, bool enableYaml)
{
ZNet instance = ZNet.instance;
if (instance != null && instance.IsServer())
{
return;
}
try
{
_isServerConfig = true;
DefaultCap.Value = defaultCap;
EnableYamlOverride.Value = enableYaml;
ReloadFromYaml();
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogInfo((object)$"[SLE] Received server config: DefaultCap={defaultCap}, EnableYaml={enableYaml}");
}
}
catch (Exception arg)
{
ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
if (logger2 != null)
{
logger2.LogError((object)$"[SLE] Failed to apply server config: {arg}");
}
}
}
internal static void OnPlayerConnected()
{
SendConfigToClients();
}
internal static int GetSkillLimit(SkillType st)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
return GetCap(st);
}
}
[BepInPlugin("SkillLimitExtender", "SkillLimitExtender", "1.1.0")]
public class SkillLimitExtenderPlugin : BaseUnityPlugin
{
internal const string PluginGuid = "SkillLimitExtender";
internal const string PluginName = "SkillLimitExtender";
internal const string PluginVersion = "1.1.0";
private readonly Harmony _harmony = new Harmony("SkillLimitExtender");
internal static ManualLogSource Logger { get; private set; }
private void Awake()
{
Logger = ((BaseUnityPlugin)this).Logger;
try
{
SkillConfigManager.Initialize(((BaseUnityPlugin)this).Config);
YamlExporter.EnsureYamlExists();
_harmony.PatchAll(typeof(SkillLimitExtenderPlugin).Assembly);
Logger.LogInfo((object)"[SLE] Plugin loaded successfully (v1.1.0)");
}
catch (Exception arg)
{
Logger.LogError((object)$"[SLE] Awake failed: {arg}");
}
}
private void Start()
{
if (!((Object)(object)ZNet.instance != (Object)null))
{
return;
}
try
{
ZRoutedRpc.instance.Register<int, bool>("SLE_ConfigSync", (Action<long, int, bool>)SkillConfigManager.OnConfigReceivedStatic);
ManualLogSource logger = Logger;
if (logger != null)
{
logger.LogInfo((object)"[SLE] RPC registered successfully");
}
}
catch (Exception arg)
{
ManualLogSource logger2 = Logger;
if (logger2 != null)
{
logger2.LogError((object)$"[SLE] RPC registration failed: {arg}");
}
}
}
private void OnDestroy()
{
try
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
catch (Exception arg)
{
Logger.LogError((object)$"[SLE] UnpatchSelf failed: {arg}");
}
}
}
[HarmonyPatch(typeof(Player), "OnSpawned")]
internal static class SLE_Hook_PlayerSpawned
{
[HarmonyPostfix]
private static void Postfix(Player __instance)
{
ZNet instance = ZNet.instance;
if (instance != null && instance.IsServer() && (Object)(object)__instance != (Object)null)
{
SkillConfigManager.SendConfigToClients();
}
}
}
internal static class YamlExporter
{
private static readonly string ConfigDir = Path.Combine(Paths.ConfigPath, "SkillLimitExtender");
private static readonly string YamlPath = Path.Combine(ConfigDir, "SLE_Skill_List.yaml");
internal static string GetYamlPath()
{
return YamlPath;
}
internal static void EnsureYamlExists()
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Invalid comparison between Unknown and I4
Directory.CreateDirectory(ConfigDir);
if (File.Exists(YamlPath))
{
return;
}
Dictionary<string, int> dictionary = new Dictionary<string, int>();
try
{
foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
{
SkillType val = value;
if ((int)val != 0 && (int)val != 999)
{
dictionary[((object)(SkillType)(ref val)).ToString()] = SkillConfigManager.DefaultCap?.Value ?? 250;
}
}
}
catch
{
}
SaveYaml(dictionary);
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogInfo((object)$"[SLE] Created YAML: {YamlPath} (seed={dictionary.Count} vanilla skills)");
}
}
internal static Dictionary<string, int> LoadYaml()
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
try
{
if (!File.Exists(YamlPath))
{
return new Dictionary<string, int>();
}
string text = File.ReadAllText(YamlPath, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
IDeserializer val = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).IgnoreUnmatchedProperties().Build();
return val.Deserialize<Dictionary<string, int>>(text) ?? new Dictionary<string, int>();
}
catch (Exception arg)
{
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogError((object)$"[SLE] LoadYaml error: {arg}");
}
return new Dictionary<string, int>();
}
}
internal static void SaveYaml(Dictionary<string, int> map)
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
try
{
Directory.CreateDirectory(ConfigDir);
ISerializer val = ((BuilderSkeleton<SerializerBuilder>)new SerializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
string contents = val.Serialize((object)map.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> kv) => kv.Key, StringComparer.Ordinal).ToDictionary((KeyValuePair<string, int> kv) => kv.Key, (KeyValuePair<string, int> kv) => kv.Value));
File.WriteAllText(YamlPath, contents, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
}
catch (Exception arg)
{
ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
if (logger != null)
{
logger.LogError((object)$"[SLE] SaveYaml error: {arg}");
}
}
}
}
}