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 DifficultyScaler Fixed v0.1.8
plugins/Cozyheim_CoreAPI.dll
Decompiled 2 years agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("CoreAPI")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("CoreAPI")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("82567395-190E-4D2E-B63A-E651911C76E4")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace Cozyheim.DifficultyScaler; public class DifficultyScalerBase : MonoBehaviour { private readonly Dictionary<DifficultyScalerMultiplier, float> _multipliers = new Dictionary<DifficultyScalerMultiplier, float>(); public float StartHealth { get; private set; } public int Level { get; private set; } private void Awake() { Character component = ((Component)this).GetComponent<Character>(); if (!((Object)(object)component == (Object)null)) { StartHealth = component.m_health; } } private void Start() { Setup(); } private void Setup() { Character component = ((Component)this).GetComponent<Character>(); ZNetView val = default(ZNetView); if ((Object)(object)component == (Object)null || !((Component)this).TryGetComponent<ZNetView>(ref val)) { return; } ZDO zDO = val.GetZDO(); if (zDO == null) { return; } Level = zDO.GetInt(ZDOVars.s_level, 1); float @float = zDO.GetFloat(ZDOVars.s_maxHealth, StartHealth); if (!Mathf.Approximately(@float, StartHealth) && !Mathf.Approximately(@float, StartHealth * (float)Level)) { return; } StartHealth *= Level; float num = 1f + GetTotalHealthMultiplier(); component.SetMaxHealth(StartHealth * num); foreach (KeyValuePair<DifficultyScalerMultiplier, float> multiplier in _multipliers) { GetMultiplier(multiplier.Key); } } public void SetMultiplier(DifficultyScalerMultiplier multiplierType, float multiplierValue) { if (_multipliers.ContainsKey(multiplierType)) { _multipliers[multiplierType] = multiplierValue; } else { _multipliers.Add(multiplierType, multiplierValue); } } public float GetMultiplier(DifficultyScalerMultiplier multiplierType) { if (!_multipliers.TryGetValue(multiplierType, out var value)) { return 0f; } if (!IsMultiplierBasedOnLevel(multiplierType)) { return value; } return (float)(Level - 1) * value; } public float GetSumOfMultipliers(IEnumerable<DifficultyScalerMultiplier> multiplierTypes) { return multiplierTypes.Sum((Func<DifficultyScalerMultiplier, float>)GetMultiplier); } public float GetTotalDamageMultiplier() { return _multipliers.Sum((KeyValuePair<DifficultyScalerMultiplier, float> keyValuePair) => (keyValuePair.Key != DifficultyScalerMultiplier.HealthMultiplier) ? GetMultiplier(keyValuePair.Key) : 0f); } public float GetTotalHealthMultiplier() { return _multipliers.Sum((KeyValuePair<DifficultyScalerMultiplier, float> keyValuePair) => (keyValuePair.Key != 0) ? GetMultiplier(keyValuePair.Key) : 0f); } public bool IsMultiplierBasedOnLevel(DifficultyScalerMultiplier multiplierType) { return multiplierType == DifficultyScalerMultiplier.StarMultiplier; } } public enum DifficultyScalerMultiplier { DamageMultiplier, HealthMultiplier, NightMultiplier, BossKillMultiplier, StarMultiplier, BiomeMultiplier }
plugins/Cozyheim_DifficultyScaler.dll
Decompiled 2 years ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using JetBrains.Annotations; using Jotunn; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("DifficultyScaler")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DifficultyScaler")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("bdde30d3-f108-465f-9a6f-5afbc030caeb")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] 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; } } } namespace SimpleJSON { public enum JSONNodeType { Array = 1, Object = 2, String = 3, Number = 4, NullValue = 5, Boolean = 6, None = 7, Custom = 255 } public enum JSONTextMode { Compact, Indent } public abstract class JSONNode { public struct Enumerator { private enum Type { None, Array, Object } private Type type; private Dictionary<string, JSONNode>.Enumerator m_Object; private List<JSONNode>.Enumerator m_Array; public bool IsValid => type != Type.None; public KeyValuePair<string, JSONNode> Current { get { if (type == Type.Array) { return new KeyValuePair<string, JSONNode>(string.Empty, m_Array.Current); } if (type == Type.Object) { return m_Object.Current; } return new KeyValuePair<string, JSONNode>(string.Empty, null); } } public Enumerator(List<JSONNode>.Enumerator aArrayEnum) { type = Type.Array; m_Object = default(Dictionary<string, JSONNode>.Enumerator); m_Array = aArrayEnum; } public Enumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) { type = Type.Object; m_Object = aDictEnum; m_Array = default(List<JSONNode>.Enumerator); } public bool MoveNext() { if (type == Type.Array) { return m_Array.MoveNext(); } if (type == Type.Object) { return m_Object.MoveNext(); } return false; } } public struct ValueEnumerator { private Enumerator m_Enumerator; public JSONNode Current => m_Enumerator.Current.Value; public ValueEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public ValueEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public ValueEnumerator GetEnumerator() { return this; } } public struct KeyEnumerator { private Enumerator m_Enumerator; public string Current => m_Enumerator.Current.Key; public KeyEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public KeyEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public KeyEnumerator GetEnumerator() { return this; } } public class LinqEnumerator : IEnumerator<KeyValuePair<string, JSONNode>>, IDisposable, IEnumerator, IEnumerable<KeyValuePair<string, JSONNode>>, IEnumerable { private JSONNode m_Node; private Enumerator m_Enumerator; public KeyValuePair<string, JSONNode> Current => m_Enumerator.Current; object IEnumerator.Current => m_Enumerator.Current; internal LinqEnumerator(JSONNode aNode) { m_Node = aNode; if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public void Dispose() { m_Node = null; m_Enumerator = default(Enumerator); } public IEnumerator<KeyValuePair<string, JSONNode>> GetEnumerator() { return new LinqEnumerator(m_Node); } public void Reset() { if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } IEnumerator IEnumerable.GetEnumerator() { return new LinqEnumerator(m_Node); } } public static bool forceASCII = false; public static bool longAsString = false; public static bool allowLineComments = true; [ThreadStatic] private static StringBuilder m_EscapeBuilder; public abstract JSONNodeType Tag { get; } public virtual JSONNode this[int aIndex] { get { return null; } set { } } public virtual JSONNode this[string aKey] { get { return null; } set { } } public virtual string Value { get { return ""; } set { } } public virtual int Count => 0; public virtual bool IsNumber => false; public virtual bool IsString => false; public virtual bool IsBoolean => false; public virtual bool IsNull => false; public virtual bool IsArray => false; public virtual bool IsObject => false; public virtual bool Inline { get { return false; } set { } } public virtual IEnumerable<JSONNode> Children { get { yield break; } } public IEnumerable<JSONNode> DeepChildren { get { foreach (JSONNode child in Children) { foreach (JSONNode deepChild in child.DeepChildren) { yield return deepChild; } } } } public IEnumerable<KeyValuePair<string, JSONNode>> Linq => new LinqEnumerator(this); public KeyEnumerator Keys => new KeyEnumerator(GetEnumerator()); public ValueEnumerator Values => new ValueEnumerator(GetEnumerator()); public virtual double AsDouble { get { double result = 0.0; if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { return result; } return 0.0; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual int AsInt { get { return (int)AsDouble; } set { AsDouble = value; } } public virtual float AsFloat { get { return (float)AsDouble; } set { AsDouble = value; } } public virtual bool AsBool { get { bool result = false; if (bool.TryParse(Value, out result)) { return result; } return !string.IsNullOrEmpty(Value); } set { Value = (value ? "true" : "false"); } } public virtual long AsLong { get { long result = 0L; if (long.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { return result; } return 0L; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual ulong AsULong { get { ulong result = 0uL; if (ulong.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { return result; } return 0uL; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual JSONArray AsArray => this as JSONArray; public virtual JSONObject AsObject => this as JSONObject; internal static StringBuilder EscapeBuilder { get { if (m_EscapeBuilder == null) { m_EscapeBuilder = new StringBuilder(); } return m_EscapeBuilder; } } public virtual void Add(string aKey, JSONNode aItem) { } public virtual void Add(JSONNode aItem) { Add("", aItem); } public virtual JSONNode Remove(string aKey) { return null; } public virtual JSONNode Remove(int aIndex) { return null; } public virtual JSONNode Remove(JSONNode aNode) { return aNode; } public virtual void Clear() { } public virtual JSONNode Clone() { return null; } public virtual bool HasKey(string aKey) { return false; } public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { return aDefault; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, 0, JSONTextMode.Compact); return stringBuilder.ToString(); } public virtual string ToString(int aIndent) { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, aIndent, JSONTextMode.Indent); return stringBuilder.ToString(); } internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); public abstract Enumerator GetEnumerator(); public static implicit operator JSONNode(string s) { if (s != null) { return new JSONString(s); } return JSONNull.CreateOrGet(); } public static implicit operator string(JSONNode d) { if (!(d == null)) { return d.Value; } return null; } public static implicit operator JSONNode(double n) { return new JSONNumber(n); } public static implicit operator double(JSONNode d) { if (!(d == null)) { return d.AsDouble; } return 0.0; } public static implicit operator JSONNode(float n) { return new JSONNumber(n); } public static implicit operator float(JSONNode d) { if (!(d == null)) { return d.AsFloat; } return 0f; } public static implicit operator JSONNode(int n) { return new JSONNumber(n); } public static implicit operator int(JSONNode d) { if (!(d == null)) { return d.AsInt; } return 0; } public static implicit operator JSONNode(long n) { if (longAsString) { return new JSONString(n.ToString(CultureInfo.InvariantCulture)); } return new JSONNumber(n); } public static implicit operator long(JSONNode d) { if (!(d == null)) { return d.AsLong; } return 0L; } public static implicit operator JSONNode(ulong n) { if (longAsString) { return new JSONString(n.ToString(CultureInfo.InvariantCulture)); } return new JSONNumber(n); } public static implicit operator ulong(JSONNode d) { if (!(d == null)) { return d.AsULong; } return 0uL; } public static implicit operator JSONNode(bool b) { return new JSONBool(b); } public static implicit operator bool(JSONNode d) { if (!(d == null)) { return d.AsBool; } return false; } public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue) { return aKeyValue.Value; } public static bool operator ==(JSONNode a, object b) { if ((object)a == b) { return true; } bool flag = a is JSONNull || (object)a == null || a is JSONLazyCreator; bool flag2 = b is JSONNull || b == null || b is JSONLazyCreator; if (flag && flag2) { return true; } if (!flag) { return a.Equals(b); } return false; } public static bool operator !=(JSONNode a, object b) { return !(a == b); } public override bool Equals(object obj) { return (object)this == obj; } public override int GetHashCode() { return base.GetHashCode(); } internal static string Escape(string aText) { StringBuilder escapeBuilder = EscapeBuilder; escapeBuilder.Length = 0; if (escapeBuilder.Capacity < aText.Length + aText.Length / 10) { escapeBuilder.Capacity = aText.Length + aText.Length / 10; } foreach (char c in aText) { switch (c) { case '\\': escapeBuilder.Append("\\\\"); continue; case '"': escapeBuilder.Append("\\\""); continue; case '\n': escapeBuilder.Append("\\n"); continue; case '\r': escapeBuilder.Append("\\r"); continue; case '\t': escapeBuilder.Append("\\t"); continue; case '\b': escapeBuilder.Append("\\b"); continue; case '\f': escapeBuilder.Append("\\f"); continue; } if (c < ' ' || (forceASCII && c > '\u007f')) { ushort num = c; escapeBuilder.Append("\\u").Append(num.ToString("X4")); } else { escapeBuilder.Append(c); } } string result = escapeBuilder.ToString(); escapeBuilder.Length = 0; return result; } private static JSONNode ParseElement(string token, bool quoted) { if (quoted) { return token; } if (token.Length <= 5) { string text = token.ToLower(); switch (text) { case "false": case "true": return text == "true"; case "null": return JSONNull.CreateOrGet(); } } if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return token; } public static JSONNode Parse(string aJSON) { Stack<JSONNode> stack = new Stack<JSONNode>(); JSONNode jSONNode = null; int i = 0; StringBuilder stringBuilder = new StringBuilder(); string aKey = ""; bool flag = false; bool flag2 = false; bool flag3 = false; for (; i < aJSON.Length; i++) { switch (aJSON[i]) { case '{': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONObject()); if (jSONNode != null) { jSONNode.Add(aKey, stack.Peek()); } aKey = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); flag3 = false; break; case '[': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONArray()); if (jSONNode != null) { jSONNode.Add(aKey, stack.Peek()); } aKey = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); flag3 = false; break; case ']': case '}': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stack.Count == 0) { throw new Exception("JSON Parse: Too many closing brackets"); } stack.Pop(); if (stringBuilder.Length > 0 || flag2) { jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2)); } if (jSONNode != null) { jSONNode.Inline = !flag3; } flag2 = false; aKey = ""; stringBuilder.Length = 0; if (stack.Count > 0) { jSONNode = stack.Peek(); } break; case ':': if (flag) { stringBuilder.Append(aJSON[i]); break; } aKey = stringBuilder.ToString(); stringBuilder.Length = 0; flag2 = false; break; case '"': flag = !flag; flag2 = flag2 || flag; break; case ',': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stringBuilder.Length > 0 || flag2) { jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2)); } flag2 = false; aKey = ""; stringBuilder.Length = 0; flag2 = false; break; case '\n': case '\r': flag3 = true; break; case '\t': case ' ': if (flag) { stringBuilder.Append(aJSON[i]); } break; case '\\': i++; if (flag) { char c = aJSON[i]; switch (c) { case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case 'n': stringBuilder.Append('\n'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'u': { string s = aJSON.Substring(i + 1, 4); stringBuilder.Append((char)int.Parse(s, NumberStyles.AllowHexSpecifier)); i += 4; break; } default: stringBuilder.Append(c); break; } } break; case '/': if (allowLineComments && !flag && i + 1 < aJSON.Length && aJSON[i + 1] == '/') { while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') { } } else { stringBuilder.Append(aJSON[i]); } break; default: stringBuilder.Append(aJSON[i]); break; case '\ufeff': break; } } if (flag) { throw new Exception("JSON Parse: Quotation marks seems to be messed up."); } if (jSONNode == null) { return ParseElement(stringBuilder.ToString(), flag2); } return jSONNode; } } public class JSONArray : JSONNode { private List<JSONNode> m_List = new List<JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Array; public override bool IsArray => true; public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_List.Count) { return new JSONLazyCreator(this); } return m_List[aIndex]; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex < 0 || aIndex >= m_List.Count) { m_List.Add(value); } else { m_List[aIndex] = value; } } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this); } set { if (value == null) { value = JSONNull.CreateOrGet(); } m_List.Add(value); } } public override int Count => m_List.Count; public override IEnumerable<JSONNode> Children { get { foreach (JSONNode item in m_List) { yield return item; } } } public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } m_List.Add(aItem); } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_List.Count) { return null; } JSONNode result = m_List[aIndex]; m_List.RemoveAt(aIndex); return result; } public override JSONNode Remove(JSONNode aNode) { m_List.Remove(aNode); return aNode; } public override void Clear() { m_List.Clear(); } public override JSONNode Clone() { JSONArray jSONArray = new JSONArray(); jSONArray.m_List.Capacity = m_List.Capacity; foreach (JSONNode item in m_List) { if (item != null) { jSONArray.Add(item.Clone()); } else { jSONArray.Add(null); } } return jSONArray; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('['); int count = m_List.Count; if (inline) { aMode = JSONTextMode.Compact; } for (int i = 0; i < count; i++) { if (i > 0) { aSB.Append(','); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append(']'); } } public class JSONObject : JSONNode { private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Object; public override bool IsObject => true; public override JSONNode this[string aKey] { get { if (m_Dict.ContainsKey(aKey)) { return m_Dict[aKey]; } return new JSONLazyCreator(this, aKey); } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = value; } else { m_Dict.Add(aKey, value); } } } public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } return m_Dict.ElementAt(aIndex).Value; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex >= 0 && aIndex < m_Dict.Count) { string key = m_Dict.ElementAt(aIndex).Key; m_Dict[key] = value; } } } public override int Count => m_Dict.Count; public override IEnumerable<JSONNode> Children { get { foreach (KeyValuePair<string, JSONNode> item in m_Dict) { yield return item.Value; } } } public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } if (aKey != null) { if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = aItem; } else { m_Dict.Add(aKey, aItem); } } else { m_Dict.Add(Guid.NewGuid().ToString(), aItem); } } public override JSONNode Remove(string aKey) { if (!m_Dict.ContainsKey(aKey)) { return null; } JSONNode result = m_Dict[aKey]; m_Dict.Remove(aKey); return result; } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } KeyValuePair<string, JSONNode> keyValuePair = m_Dict.ElementAt(aIndex); m_Dict.Remove(keyValuePair.Key); return keyValuePair.Value; } public override JSONNode Remove(JSONNode aNode) { try { KeyValuePair<string, JSONNode> keyValuePair = m_Dict.Where((KeyValuePair<string, JSONNode> k) => k.Value == aNode).First(); m_Dict.Remove(keyValuePair.Key); return aNode; } catch { return null; } } public override void Clear() { m_Dict.Clear(); } public override JSONNode Clone() { JSONObject jSONObject = new JSONObject(); foreach (KeyValuePair<string, JSONNode> item in m_Dict) { jSONObject.Add(item.Key, item.Value.Clone()); } return jSONObject; } public override bool HasKey(string aKey) { return m_Dict.ContainsKey(aKey); } public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { if (m_Dict.TryGetValue(aKey, out var value)) { return value; } return aDefault; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('{'); bool flag = true; if (inline) { aMode = JSONTextMode.Compact; } foreach (KeyValuePair<string, JSONNode> item in m_Dict) { if (!flag) { aSB.Append(','); } flag = false; if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } aSB.Append('"').Append(JSONNode.Escape(item.Key)).Append('"'); if (aMode == JSONTextMode.Compact) { aSB.Append(':'); } else { aSB.Append(" : "); } item.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append('}'); } } public class JSONString : JSONNode { private string m_Data; public override JSONNodeType Tag => JSONNodeType.String; public override bool IsString => true; public override string Value { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONString(string aData) { m_Data = aData; } public override JSONNode Clone() { return new JSONString(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('"').Append(JSONNode.Escape(m_Data)).Append('"'); } public override bool Equals(object obj) { if (base.Equals(obj)) { return true; } if (obj is string text) { return m_Data == text; } JSONString jSONString = obj as JSONString; if (jSONString != null) { return m_Data == jSONString.m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = ""; } } public class JSONNumber : JSONNode { private double m_Data; public override JSONNodeType Tag => JSONNodeType.Number; public override bool IsNumber => true; public override string Value { get { return m_Data.ToString(CultureInfo.InvariantCulture); } set { if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { m_Data = result; } } } public override double AsDouble { get { return m_Data; } set { m_Data = value; } } public override long AsLong { get { return (long)m_Data; } set { m_Data = value; } } public override ulong AsULong { get { return (ulong)m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONNumber(double aData) { m_Data = aData; } public JSONNumber(string aData) { Value = aData; } public override JSONNode Clone() { return new JSONNumber(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(Value); } private static bool IsNumeric(object value) { if (!(value is int) && !(value is uint) && !(value is float) && !(value is double) && !(value is decimal) && !(value is long) && !(value is ulong) && !(value is short) && !(value is ushort) && !(value is sbyte)) { return value is byte; } return true; } public override bool Equals(object obj) { if (obj == null) { return false; } if (base.Equals(obj)) { return true; } JSONNumber jSONNumber = obj as JSONNumber; if (jSONNumber != null) { return m_Data == jSONNumber.m_Data; } if (IsNumeric(obj)) { return Convert.ToDouble(obj) == m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = 0.0; } } public class JSONBool : JSONNode { private bool m_Data; public override JSONNodeType Tag => JSONNodeType.Boolean; public override bool IsBoolean => true; public override string Value { get { return m_Data.ToString(); } set { if (bool.TryParse(value, out var result)) { m_Data = result; } } } public override bool AsBool { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONBool(bool aData) { m_Data = aData; } public JSONBool(string aData) { Value = aData; } public override JSONNode Clone() { return new JSONBool(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(m_Data ? "true" : "false"); } public override bool Equals(object obj) { if (obj == null) { return false; } if (obj is bool) { return m_Data == (bool)obj; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = false; } } public class JSONNull : JSONNode { private static JSONNull m_StaticInstance = new JSONNull(); public static bool reuseSameInstance = true; public override JSONNodeType Tag => JSONNodeType.NullValue; public override bool IsNull => true; public override string Value { get { return "null"; } set { } } public override bool AsBool { get { return false; } set { } } public static JSONNull CreateOrGet() { if (reuseSameInstance) { return m_StaticInstance; } return new JSONNull(); } private JSONNull() { } public override Enumerator GetEnumerator() { return default(Enumerator); } public override JSONNode Clone() { return CreateOrGet(); } public override bool Equals(object obj) { if ((object)this == obj) { return true; } return obj is JSONNull; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } } internal class JSONLazyCreator : JSONNode { private JSONNode m_Node; private string m_Key; public override JSONNodeType Tag => JSONNodeType.None; public override JSONNode this[int aIndex] { get { return new JSONLazyCreator(this); } set { Set(new JSONArray()).Add(value); } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this, aKey); } set { Set(new JSONObject()).Add(aKey, value); } } public override int AsInt { get { Set(new JSONNumber(0.0)); return 0; } set { Set(new JSONNumber(value)); } } public override float AsFloat { get { Set(new JSONNumber(0.0)); return 0f; } set { Set(new JSONNumber(value)); } } public override double AsDouble { get { Set(new JSONNumber(0.0)); return 0.0; } set { Set(new JSONNumber(value)); } } public override long AsLong { get { if (JSONNode.longAsString) { Set(new JSONString("0")); } else { Set(new JSONNumber(0.0)); } return 0L; } set { if (JSONNode.longAsString) { Set(new JSONString(value.ToString(CultureInfo.InvariantCulture))); } else { Set(new JSONNumber(value)); } } } public override ulong AsULong { get { if (JSONNode.longAsString) { Set(new JSONString("0")); } else { Set(new JSONNumber(0.0)); } return 0uL; } set { if (JSONNode.longAsString) { Set(new JSONString(value.ToString(CultureInfo.InvariantCulture))); } else { Set(new JSONNumber(value)); } } } public override bool AsBool { get { Set(new JSONBool(aData: false)); return false; } set { Set(new JSONBool(value)); } } public override JSONArray AsArray => Set(new JSONArray()); public override JSONObject AsObject => Set(new JSONObject()); public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONLazyCreator(JSONNode aNode) { m_Node = aNode; m_Key = null; } public JSONLazyCreator(JSONNode aNode, string aKey) { m_Node = aNode; m_Key = aKey; } private T Set<T>(T aVal) where T : JSONNode { if (m_Key == null) { m_Node.Add(aVal); } else { m_Node.Add(m_Key, aVal); } m_Node = null; return aVal; } public override void Add(JSONNode aItem) { Set(new JSONArray()).Add(aItem); } public override void Add(string aKey, JSONNode aItem) { Set(new JSONObject()).Add(aKey, aItem); } public static bool operator ==(JSONLazyCreator a, object b) { if (b == null) { return true; } return (object)a == b; } public static bool operator !=(JSONLazyCreator a, object b) { return !(a == b); } public override bool Equals(object obj) { if (obj == null) { return true; } return (object)this == obj; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } } public static class JSON { public static JSONNode Parse(string aJSON) { return JSONNode.Parse(aJSON); } } } namespace ServerSync { [PublicAPI] public abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] public class SyncedConfigEntry<T> : OwnConfigEntryBase { public readonly ConfigEntry<T> SourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public SyncedConfigEntry(ConfigEntry<T> sourceConfig) { SourceConfig = sourceConfig; } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } public abstract class CustomSyncedValueBase { public readonly string Identifier; public readonly Type Type; private object? boxedValue; public object? LocalBaseValue; protected bool localIsOwner; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type) { Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] public sealed class CustomSyncedValue<T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T)) : base(configSync, identifier, typeof(T)) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] public class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { configSync2.IsSourceOfTruth = __instance.IsDedicated() || __instance.IsServer(); ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_ConfigSync); if (isServer) { Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } static void SendAdmin(List<ZNetPeer> peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } static IEnumerator WatchAdminListChanges() { SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); List<string> CurrentList = new List<string>(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List<string>(adminList.GetList()); List<ZNetPeer> list = (from p in ZNet.instance.GetPeers() where adminList.Contains(p.m_rpc.GetSocket().GetHostName()) select p).ToList(); SendAdmin(ZNet.instance.GetPeers().Except(list).ToList(), isAdmin: false); SendAdmin(list, isAdmin: true); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_InitialConfigSync); } } } private class ParsedConfigs { public readonly Dictionary<OwnConfigEntryBase, object?> configValues = new Dictionary<OwnConfigEntryBase, object>(); public readonly Dictionary<CustomSyncedValueBase, object?> customValues = new Dictionary<CustomSyncedValueBase, object>(); } [HarmonyPatch(typeof(ZNet), "Shutdown")] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); } ProcessingServerUpdate = false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ISocket { public readonly ISocket Original; public readonly List<ZPackage> Package = new List<ZPackage>(); public volatile bool finished; public volatile int versionMatchQueued = -1; public BufferingSocket(ISocket original) { Original = original; } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix(ref Dictionary<Assembly, BufferingSocket>? __state, ZNet __instance, ZRpc rpc) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (__instance.IsServer()) { BufferingSocket value = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, value); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend != 0) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, value); } if (__state == null) { __state = new Dictionary<Assembly, BufferingSocket>(); } __state[Assembly.GetExecutingAssembly()] = value; } } [HarmonyPostfix] private static void Postfix(Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc) { ZNetPeer peer; if (__instance.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val2 = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val2 != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val2, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i++) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } IEnumerator sendAsync() { foreach (ConfigSync configSync in configSyncs) { List<PackageEntry> list = new List<PackageEntry>(); if (configSync.CurrentVersion != null) { list.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); list.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)methodInfo == null) ? ((object)val.Contains(rpc.GetSocket().GetHostName())) : methodInfo.Invoke(ZNet.instance, new object[2] { val, rpc.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, list, partial: false); yield return ((MonoBehaviour)__instance).StartCoroutine(configSync.sendZPackage(new List<ZNetPeer> { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string key; public string section; public Type type; public object? value; } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } private class InvalidDeserializationTypeException : Exception { public string expected; public string field = ""; public string received; } private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; public static bool ProcessingServerUpdate; private static readonly HashSet<ConfigSync> configSyncs; private static bool isServer; private static bool lockExempt; private static long packageCounter; private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); private readonly HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>(); private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>(); public readonly string Name; public string? CurrentVersion; public string? DisplayName; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private OwnConfigEntryBase? lockedConfig; public string? MinimumRequiredVersion; public bool ModRequired; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0051; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag.GetValueOrDefault(); } if (num) { return !lockExempt; } goto IL_0051; IL_0051: return false; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public event Action<bool>? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { configSyncs = new HashSet<ConfigSync>(); RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry<T> syncedEntry = ownConfigEntryBase as SyncedConfigEntry<T>; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry<T>(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "<Tags>k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty<object>()).Concat(new SyncedConfigEntry<T>[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry<T> AddLockingConfigEntry<T>(ConfigEntry<T> lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry<T>(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry<T>)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(ZRoutedRpc.Everybody, customValue); } }; } private void RPC_InitialConfigSync(ZRpc rpc, ZPackage package) { RPC_ConfigSync(0L, package); } private void RPC_ConfigSync(long sender, ZPackage package) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown try { if (isServer && IsLocked) { SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); bool? obj; if ((int)val == 0) { obj = null; } else { ZRpc? currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj2; if (currentRpc == null) { obj2 = null; } else { ISocket socket = currentRpc.GetSocket(); obj2 = ((socket != null) ? socket.GetHostName() : null); } obj = val.Contains((string)obj2); } if (obj == false) { return; } } cacheExpirations.RemoveAll(delegate(KeyValuePair<long, string> kv) { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2u) != 0) { long num = package.ReadLong(); string text = sender.ToString() + num; if (!configValueCache.TryGetValue(text, out var value)) { value = new SortedDictionary<int, byte[]>(); configValueCache[text] = value; cacheExpirations.Add(new KeyValuePair<long, string>(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value.Add(key, package.ReadByteArray()); if (value.Count < num2) { return; } configValueCache.Remove(text); package = new ZPackage(value.Values.SelectMany((byte[] a) => a).ToArray()); b = package.ReadByte(); } ProcessingServerUpdate = true; if ((b & 4u) != 0) { MemoryStream stream = new MemoryStream(package.ReadByteArray()); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); b = package.ReadByte(); } if ((b & 1) == 0) { resetConfigsFromServer(); } if (!isServer) { if (IsSourceOfTruth) { lockedConfigChanged += serverLockedSettingChanged; } IsSourceOfTruth = false; } ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package); foreach (KeyValuePair<OwnConfigEntryBase, object> configValue in parsedConfigs.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } configValue.Key.BaseConfig.BoxedValue = configValue.Value; } foreach (KeyValuePair<CustomSyncedValueBase, object> customValue in parsedConfigs.customValues) { if (!isServer) { CustomSyncedValueBase key2 = customValue.Key; if (key2.LocalBaseValue == null) { key2.LocalBaseValue = customValue.Key.BoxedValue; } } customValue.Key.BoxedValue = customValue.Value; } if (!isServer) { Debug.Log((object)$"Received {parsedConfigs.configValues.Count} configs and {parsedConfigs.customValues.Count} custom values from the server for mod {DisplayName ?? Name}"); serverLockedSettingChanged(); } } finally { ProcessingServerUpdate = false; } } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary<string, OwnConfigEntryBase> dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary<string, CustomSyncedValueBase> dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int i = 0; i < num; i++) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 == "" || type != null) { object obj; try { obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { Debug.LogWarning((object)("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected)); continue; } OwnConfigEntryBase value2; if (text == "Internal") { CustomSyncedValueBase value; if (text2 == "serverversion") { if (obj?.ToString() != CurrentVersion) { Debug.LogWarning((object)("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown"))); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName)); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName)); } else { Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match.")); } continue; } Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config)); if (configSync == null) { return true; } if (!configSync.IsSourceOfTruth && config.SynchronizedConfig && config.LocalBaseValue != null) { if (!configSync.IsLocked) { if (config == configSync.lockedConfig) { return lockExempt; } return true; } return false; } return true; } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute<ConfigurationManagerAttributes>(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase config) => config.LocalBaseValue != null)) { item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; } lockedConfigChanged -= serverLockedSettingChanged; IsSourceOfTruth = true; serverLockedSettingChanged(); } private IEnumerator<bool> distributeConfigToPeers(ZNetPeer peer, ZPackage package) { ZRoutedRpc rpc = ZRoutedRpc.instance; if (rpc == null) { yield break; } byte[] data = package.GetArray(); if (data != null && data.LongLength > 250000) { int fragments = (int)(1 + (data.LongLength - 1) / 250000); long packageIdentifier = ++packageCounter; int fragment = 0; while (fragment < fragments) { foreach (bool item in waitForQueue()) { yield return item; } if (peer.m_socket.IsConnected()) { ZPackage val = new ZPackage(); val.Write((byte)2); val.Write(packageIdentifier); val.Write(fragment); val.Write(fragments); val.Write(data.Skip(250000 * fragment).Take(250000).ToArray()); SendPackage(val); if (fragment != fragments - 1) { yield return true; } int num = fragment + 1; fragment = num; continue; } break; } yield break; } foreach (bool item2 in waitForQueue()) { yield return item2; } SendPackage(package); void SendPackage(ZPackage pkg) { string text = Name + " ConfigSync"; if (isServer) { peer.m_rpc.Invoke(text, new object[1] { pkg }); } else { rpc.InvokeRoutedRPC(peer.m_server ? 0 : peer.m_uid, text, new object[1] { pkg }); } } IEnumerable<bool> waitForQueue() { float timeout = Time.time + 30f; while (peer.m_socket.GetSendQueueSize() > 20000) { if (Time.time > timeout) { Debug.Log((object)$"Disconnecting {peer.m_uid} after 30 seconds config sending timeout"); peer.m_rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)5 }); ZNet.instance.Disconnect(peer); break; } yield return false; } } } private IEnumerator sendZPackage(long target, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return Enumerable.Empty<object>().GetEnumerator(); } List<ZNetPeer> list = (List<ZNetPeer>)AccessTools.DeclaredField(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance); if (target != ZRoutedRpc.Everybody) { list = list.Where((ZNetPeer p) => p.m_uid == target).ToList(); } return sendZPackage(list, package); } private IEnumerator sendZPackage(List<ZNetPeer> peers, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] array = package.GetArray(); if (array != null && array.LongLength > 10000) { ZPackage val = new ZPackage(); val.Write((byte)4); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Optimal)) { deflateStream.Write(array, 0, array.Length); } val.Write(memoryStream.ToArray()); package = val; } List<IEnumerator<bool>> writers = (from peer in peers where peer.IsReady() select peer into p select distributeConfigToPeers(p, package)).ToList(); writers.RemoveAll((IEnumerator<bool> writer) => !writer.MoveNext()); while (writers.Count > 0) { yield return null; writers.RemoveAll((IEnumerator<bool> writer) => !writer.MoveNext()); } } private void Broadcast(long target, params ConfigEntryBase[] configs) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(configs); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private void Broadcast(long target, params CustomSyncedValueBase[] customValues) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(null, customValues); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private static OwnConfigEntryBase? configData(ConfigEntryBase config) { return config.Description.Tags?.OfType<OwnConfigEntryBase>().SingleOrDefault(); } public static SyncedConfigEntry<T>? ConfigData<T>(ConfigEntry<T> config) { return ((ConfigEntryBase)config).Description.Tags?.OfType<SyncedConfigEntry<T>>().SingleOrDefault(); } private static T configAttribute<T>(ConfigEntryBase config) { return config.Description.Tags.OfType<T>().First(); } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { if (!type.IsEnum) { return type; } return Enum.GetUnderlyingType(type); } private static ZPackage ConfigsToPackage(IEnumerable<ConfigEntryBase>? configs = null, IEnumerable<CustomSyncedValueBase>? customValues = null, IEnumerable<PackageEntry>? packageEntries = null, bool partial = true) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown List<ConfigEntryBase> list = configs?.Where((ConfigEntryBase config) => configData(config).SynchronizedConfig).ToList() ?? new List<ConfigEntryBase>(); List<CustomSyncedValueBase> list2 = customValues?.ToList() ?? new List<CustomSyncedValueBase>(); ZPackage val = new ZPackage(); val.Write((byte)(partial ? 1 : 0)); val.Write(list.Count + list2.Count + (packageEntries?.Count() ?? 0)); foreach (PackageEntry item in packageEntries ?? Array.Empty<PackageEntry>()) { AddEntryToPackage(val, item); } foreach (CustomSyncedValueBase item2 in list2) { AddEntryToPackage(val, new PackageEntry { section = "Internal", key = item2.Identifier, type = item2.Type, value = item2.BoxedValue }); } foreach (ConfigEntryBase item3 in list) { AddEntryToPackage(val, new PackageEntry { section = item3.Definition.Section, key = item3.Definition.Key, type = configType(item3), value = item3.BoxedValue }); } return val; } private static void AddEntryToPackage(ZPackage package, PackageEntry entry) { package.Write(entry.section); package.Write(entry.key); package.Write((entry.value == null) ? "" : GetZPackageTypeString(entry.type)); AddValueToZPackage(package, entry.value); } private static string GetZPackageTypeString(Type type) { return type.AssemblyQualifiedName; } private static void AddValueToZPackage(ZPackage package, object? value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { AddValueToZPackage(package, item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetZPackageTypeString(fieldInfo.FieldType)); AddValueToZPackage(package, fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetZPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadValueWithTypeFromZPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = ReadValueWithTypeFromZPackage(package, type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List<string>) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type.GetGenericTypeDefinition())) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List<object> source = new List<object>(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.First(); } } [PublicAPI] [HarmonyPatch] public class VersionCheck { private static readonly HashSet<VersionCheck> versionChecks; private static readonly Dictionary<string, string> notProcessedNames; private readonly List<ZRpc> ValidatedClients = new List<ZRpc>(); private ConfigSync? ConfigSync; private string? currentVersion; private string? displayName; private string? minimumRequiredVersion; public bool ModRequired = true; public string Name; private string? ReceivedCurrentVersion; private string? ReceivedMinimumRequiredVersion; public string DisplayName { get { return displayName ?? Name; } set { displayName = value; } } public string CurrentVersion { get { return currentVersion ?? "0.0.0"; } set { currentVersion = value; } } public string MinimumRequiredVersion { get { string text = minimumRequiredVersion; if (text == null) { if (!ModRequired) { return "0.0.0"; } text = CurrentVersion; } return text; } set { minimumRequiredVersion = value; } } static VersionCheck() { versionChecks = new HashSet<VersionCheck>(); notProcessedNames = new Dictionary<string, string>(); typeof(ThreadingHelper).GetMethod("StartSyncInvoke").Invoke(ThreadingHelper.Instance, new object[1] { new Action(PatchServerSync) }); } public VersionCheck(string name) { Name = name; ModRequired = true; versionChecks.Add(this); } public VersionCheck(ConfigSync configSync) { ConfigSync = configSync; Name = ConfigSync.Name; versionChecks.Add(this); } private static void PatchServerSync() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown Patches patchInfo = PatchProcessor.GetPatchInfo((MethodBase)AccessTools.DeclaredMethod(typeof(ZNet), "Awake", (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Postfixes.Count((Patch p) => p.PatchMethod.DeclaringType == typeof(ConfigSync.RegisterRPCPatch)) > 0) { return; } Harmony val = new Harmony("org.bepinex.helpers.ServerSync"); foreach (Type item in from t in typeof(ConfigSync).GetNestedTypes(BindingFlags.NonPublic).Concat(new Type[1] { typeof(VersionCheck) }) where t.IsClass select t) { val.PatchAll(item); } } public void Initialize() { ReceivedCurrentVersion = null; ReceivedMinimumRequiredVersion = null; if (ConfigSync != null) { Name = ConfigSync.Name; DisplayName = ConfigSync.DisplayName; CurrentVersion = ConfigSync.CurrentVersion; MinimumRequiredVersion = ConfigSync.MinimumRequiredVersion; ModRequired = ConfigSync.ModRequired; } } private bool IsVersionOk() { if (ReceivedMinimumRequiredVersion == null || ReceivedCurrentVersion == null) { return !ModRequired; } bool num = new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion); bool flag = new Version(ReceivedCurrentVersion) >= new Version(MinimumRequiredVersion); return num && flag; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return "Mod " + DisplayName + " must not be installed."; } if (!(new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion))) { return "Mod " + DisplayName + " requires minimum " + ReceivedMinimumRequiredVersion + ". Installed is version " + CurrentVersion + "."; } return "Mod " + DisplayName + " requires maximum " + ReceivedCurrentVersion + ". Installed is version " + CurrentVersion + "."; } private string ErrorServer(ZRpc rpc) { return "Disconnect: The client (" + rpc.GetSocket().GetHostName() + ") doesn't have the correct " + DisplayName + " version " + MinimumRequiredVersion; } private string Error(ZRpc? rpc = null) { if (rpc != null) { return ErrorServer(rpc); } return ErrorClient(); } private static VersionCheck[] GetFailedClient() { return versionChecks.Where((VersionCheck check) => !check.IsVersionOk()).ToArray(); } private static VersionCheck[] GetFailedServer(ZRpc rpc) { return versionChecks.Where((VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc)).ToArray(); } private static void Logout() { Game.instance.Logout(true, true); AccessTools.DeclaredField(typeof(ZNet), "m_connectionStatus").SetValue(null, (object)(ConnectionStatus)3); } private static void DisconnectClient(ZRpc rpc) { rpc.Invoke("Error", new object[1] { 3 }); } private static void CheckVersion(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, null); } private static void CheckVersion(ZRpc rpc, ZPackage pkg, Action<ZRpc, ZPackage>? original) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string text3 = pkg.ReadString(); bool flag = false; foreach (VersionCheck versionCheck in versionChecks) { if (!(text != versionCheck.Name)) { Debug.Log((object)("Received " + versionCheck.DisplayName + " version " + text3 + " and minimum version " + text2 + " from the " + (ZNet.instance.IsServer() ? "client" : "server") + ".")); versionCheck.ReceivedMinimumRequiredVersion = text2; versionCheck.ReceivedCurrentVersion = text3; if (ZNet.instance.IsServer() && versionCheck.IsVersionOk()) { versionCheck.ValidatedClients.Add(rpc); } flag = true; } } if (flag) { return; } pkg.SetPos(0); if (original != null) { original(rpc, pkg); if (pkg.GetPos() == 0) { notProcessedNames.Add(text, text3); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool RPC_PeerInfo(ZRpc rpc, ZNet __instance) { VersionCheck[] array = (__instance.IsServer() ? GetFailedServer(rpc) : GetFailedClient()); if (array.Length == 0) { return true; } VersionCheck[] array2 = array; for (int i = 0; i < array2.Length; i++) { Debug.LogWarning((object)array2[i].Error(rpc)); } if (__instance.IsServer()) { DisconnectClient(rpc); } else { Logout(); } return false; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPrefix] private static void RegisterAndCheckVersion(ZNetPeer peer, ZNet __instance) { //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown notProcessedNames.Clear(); IDictionary dictionary = (IDictionary)typeof(ZRpc).GetField("m_functions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(peer.m_rpc); if (dictionary.Contains(StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck"))) { object obj = dictionary[StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck")]; Action<ZRpc, ZPackage> action = (Action<ZRpc, ZPackage>)obj.GetType().GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj); peer.m_rpc.Register<ZPackage>("ServerSync VersionCheck", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, action); }); } else { peer.m_rpc.Register<ZPackage>("ServerSync VersionCheck", (Action<ZRpc, ZPackage>)CheckVersion); } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.Initialize(); if (versionCheck.ModRequired || __instance.IsServer()) { Debug.Log((object)("Sending " + versionCheck.DisplayName + " version " + versionCheck.CurrentVersion + " and minimum version " + versionCheck.MinimumRequiredVersion + " to the " + (__instance.IsServer() ? "client" : "server") + ".")); ZPackage val = new ZPackage(); val.Write(versionCheck.Name); val.Write(versionCheck.MinimumRequiredVersion); val.Write(versionCheck.CurrentVersion); peer.m_rpc.Invoke("ServerSync VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPrefix] private static void RemoveDisconnected(ZNetPeer peer, ZNet __instance) { if (!__instance.IsServer()) { return; } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.ValidatedClients.Remove(peer.m_rpc); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] [HarmonyPostfix] private static void ShowConnectionError(FejdStartup __instance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (!__instance.m_connectionFailedPanel.activeSelf || (int)ZNet.GetConnectionStatus() != 3) { return; } VersionCheck[] failedClient = GetFailedClient(); if (failedClient.Length != 0) { string text = string.Join("\n", failedClient.Select((VersionCheck check) => check.Error())); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + text; } foreach (KeyValuePair<string, string> item in notProcessedNames.OrderBy((KeyValuePair<string, string> kv) => kv.Key)) { if (!__instance.m_connectionFailedError.text.Contains(item.Key)) { TMP_Text connectionFailedError2 = __instance.m_connectionFailedError; connectionFailedError2.text = connectionFailedError2.text + "\n" + item.Key + " (Version: " + item.Value + ")"; } } } } } namespace Cozyheim.DifficultyScaler { internal class MonsterModifier { [HarmonyPatch] private class Patch { } } [BepInPlugin("dk.thrakal.DifficultyScaler", "DifficultyScaler", "0.1.8")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class Main : BaseUnityPlugin { internal const string modName = "DifficultyScaler"; internal const string version = "0.1.8"; internal const string GUID = "dk.thrakal.DifficultyScaler"; private static Dictionary<string, float> _monsterHealth; private static Dictionary<string, float> _monsterDamage; internal static ConfigSync configSync = new ConfigSync("dk.thrakal.DifficultyScaler") { DisplayName = "DifficultyScaler", CurrentVersion = "0.1.8", MinimumRequiredVersion = "0.1.8" }; internal static ConfigFile configFile; internal static ConfigEntry<bool> modEnabled; internal static ConfigEntry<bool> debugEnabled; internal static ConfigEntry<LogType> debugLevel; internal static ConfigEntry<float> overallHealthMultipler; internal static ConfigEntry<float> overallDamageMultipler; internal static ConfigEntry<string> monsterHealthModifier; internal static ConfigEntry<string> monsterDamageModifier; private static ConfigEntry<bool> enableBossKillDifficulty; private static ConfigEntry<float> bossKillMultiplier; private static ConfigEntry<string> bossGlobalKeys; private static ConfigEntry<bool> enableBiomeDifficulty; private static ConfigEntry<float> meadowsMultiplier; private static ConfigEntry<float> blackForestMultiplier; private static ConfigEntry<float> swampMultiplier; private static ConfigEntry<float> mountainMultiplier; private static ConfigEntry<float> plainsMultiplier; private static ConfigEntry<float> mistlandsMultiplier; private static ConfigEntry<float> oceanMultiplier; private static ConfigEntry<float> deepNorthMultiplier; private static ConfigEntry<float> ashlandsMultiplier; private static ConfigEntry<bool> enableNightDifficulty; internal static ConfigEntry<float> nightMultiplier; private static ConfigEntry<bool> enableStarDifficulty; internal static ConfigEntry<float> starMultiplier; private readonly Harmony harmony = new Harmony("dk.thrakal.DifficultyScaler"); private ConfigEntry<int> configTest; internal static bool IsSpawnerTweaksInstalled { get; private set; } private void Awake() { configFile = ((BaseUnityPlugin)this).Config; configFile.SaveOnConfigSet = true; modEnabled = CreateConfigEntry("00_General", "ModEnabled", value: true, "Enable this mod", synchronizedSetting: false); debugEnabled = CreateConfigEntry("00_General", "DebugEnabled", value: false, "Display debug messages in the console"); debugLevel = CreateConfigEntry("00_General", "DebugLevel", LogType.Info, "The level of debug messages to display in the console (if DebugEnabled is enabled)."); overallHealthMultipler = CreateConfigEntry("01_Monsters", "overallHealthMultipler", 0f, "Increases the base health of all monsters in the game. (0 = No change, 1.5 = +150% health)."); overallDamageMultipler = CreateConfigEntry("01_Monsters", "overallDamageMultipler", 0f, "Increases the base damage of all monsters in the game. (0 = No change, 1.5 = +150% damage)."); monsterHealthModifier = CreateConfigEntry("01_Monsters", "monsterBaseHealthModifier", "", "Set the base health (60 = 60 base health) of individual monsters. Format must follow: Monstername:Health (example: Skeleton:60,Greyling:25)"); monsterDamageModifier = CreateConfigEntry("01_Monsters", "monsterDamageModifier", "", "Set a damage multiplier (110 = +10% increased damage) of individual monsters. Format must follow: Monstername:Damage (example: Skeleton:110,Greyling:120)"); enableBossKillDifficulty = CreateConfigEntry("02_Boss", "enableBossKillDifficulty", value: false, "Enables difficulty scaling after killing bosses"); bossKillMultiplier = CreateConfigEntry("02_Boss", "bossKillMultiplier", 0.5f, "Increases the base health & damage of all monsters by this value, per boss you have killed. (0 = no scaling, 0.5 = +50% health/damage per boss)"); bossGlobalKeys = CreateConfigEntry("02_Boss", "bossGlobalKeys", "defeated_eikthyr, defeated_gdking, defeated_bonemass, defeated_dragon, defeated_goblinking, defeated_queen", "Global keys to check against for registering boss kills. Add custom boss keys to this list if you are using custom bosses."); enableBiomeDifficulty = CreateConfigEntry("03_Biomes", "enableBiomeDifficulty", value: false, "Enables difficulty scaling for biomes"); meadowsMultiplier = CreateConfigEntry("03_Biomes", "meadowsMultiplier", 0f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); blackForestMultiplier = CreateConfigEntry("03_Biomes", "blackForestMultiplier", 0.5f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); swampMultiplier = CreateConfigEntry("03_Biomes", "swampMultiplier", 1f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); oceanMultiplier = CreateConfigEntry("03_Biomes", "oceanMultiplier", 1.25f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); mountainMultiplier = CreateConfigEntry("03_Biomes", "mountainMultiplier", 1.5f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); plainsMultiplier = CreateConfigEntry("03_Biomes", "plainsMultiplier", 2f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); mistlandsMultiplier = CreateConfigEntry("03_Biomes", "mistlandsMultiplier", 2.5f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); deepNorthMultiplier = CreateConfigEntry("03_Biomes", "deepNorthMultiplier", 3f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); ashlandsMultiplier = CreateConfigEntry("03_Biomes", "ashlandsMultiplier", 4f, "Increases the base health & damage of all monsters in this Biome (0 = no scaling, 0.5 = +50% health/damage)"); enableNightDifficulty = CreateConfigEntry("04_Night", "enableNightDifficulty", value: false, "Enables difficulty scaling during night"); nightMultiplier = CreateConfigEntry("04_Night", "nightMultiplier", 1f, "Increases the base health & damage of all monsters by this value during night. (0 = no scaling, 0.5 = +50% health/damage)"); enableStarDifficulty = CreateConfigEntry("05_MonsterStars", "enableStarDifficulty", value: false, "Enables difficulty scaling for starred monsters. (Overrides vanilla health scaling!)"); starMultiplier = CreateConfigEntry("05_MonsterStars", "starMultiplier", 1f, "Increases the base health & damage of all monsters by this value for each star they have. Vanilla value is 1. (0 = no scaling, 0.5 = +50% health/damage)"); IsSpawnerTweaksInstalled = IsModInstalled("spawner_tweaks"); if (modEnabled.Value) { harmony.PatchAll(); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ConsoleLog()); PrefabManager.OnVanillaPrefabsAvailable += CreateMonsterHealthDictionary; } } private void OnDestroy() { harmony.UnpatchSelf(); } public static bool IsModInstalled(string modGuid) { return Chainloader.PluginInfos.Select((KeyValuePair<string, PluginInfo> plugin) => plugin.Value.Metadata).Any((BepInPlugin metadata) => metadata.GUID.Equals(modGuid)); } public static float GetBiomeMultiplier(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 if ((int)biome <= 16) { switch (biome - 1) { default: if ((int)biome != 8) { if ((int)biome != 16) { break; } return plainsMultiplier.Value; } return blackForestMultiplier.Value; case 0: return meadowsMultiplier.Value; case 1: return swampMultiplier.Value; case 3: return mountainMultiplier.Value; case 2: break; } } else if ((int)biome <= 64) { if ((int)biome == 32) { return ashlandsMultiplier.Value; } if ((int)biome == 64) { return deepNorthMultiplier.Value; } } else { if ((int)biome == 256) { return oceanMultiplier.Value; } if ((int)biome == 512) { return mistlandsMultiplier.Value; } } return 0f; } public static bool IsMultiplierEnabled(DifficultyScalerMultiplier multiplierType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) switch ((int)multiplierType) { case 0: return true; case 1: return true; case 2: return enableNightDifficulty.Value; case 3: return enableBossKillDifficulty.Value; case 4: return enableStarDifficulty.Value; case 5: return enableBiomeDifficulty.Value; default: ConsoleLog.Print(string.Format("({0}) Multiplier type {1} is not handled.", "IsMultiplierEnabled", multiplierType), LogType.Warning); return false; } } public static float GetCalculatedBossKillMultiplier() { string[] array = bossGlobalKeys.Value.Split(new char[1] { ',' }); int num = 0; string[] array2 = array; foreach (string text in array2) { if (text != "" && ZoneSystem.instance.GetGlobalKey(text.Trim())) { num++; } } return bossKillMultiplier.Value * (float)num; } public static bool TryGetMonsterBaseHealthOverride(string monsterName, out float baseHealthOverrideValue) { return _monsterHealth.TryGetValue(monsterName, out baseHealthOverrideValue); } public static bool TryGetMonsterDamage(string monsterName, out float monsterDamageValue) { return _monsterDamage.TryGetValue(monsterName, out monsterDamageValue); } public static void CreateMonsterHealthDictionary() { _monsterHealth = new Dictionary<string, float>(); ConsoleLog.Print("-- Changing base HEALTH for monsters: --", LogType.Message); string[] array = monsterHealthModifier.Value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length >= 2 && float.TryParse(array2[1].Trim(), out var result)) { string text = array2[0].Trim(); float health = ((Character)PrefabManager.Instance.GetPrefab(text).GetComponent<Humanoid>()).m_health; _monsterHealth.Add(text + "(Clone)", result); ConsoleLog.Print(text + ": (" + health + " -> " + result + " HP)"); } } CreateMonsterDamageDictionary(); PrefabManager.OnVanillaPrefabsAvailable -= CreateMonsterHealthDictionary; } public static void CreateMonsterDamageDictionary() { _monsterDamage = new Dictionary<string, float>(); ConsoleLog.Print("-- Changing base DAMAGE for monsters: --", LogType.Message); string[] array = monsterDamageModifier.Value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length >= 2 && float.TryParse(array2[1].Trim(), out var result)) { string text = array2[0].Trim(); ConsoleLog.Print(text + ": " + result + "% damage"); result /= 100f; _monsterDamage.Add(text + "(Clone)", result); } } ConsoleLog.Print("-- Adjusting ALL monster's base damage and health: --", LogType.Message); ConsoleLog.Print("Base health set to: +" + (overallHealthMultipler.Value * 100f).ToString("N0") + "%"); ConsoleLog.Print("Base damage set to: +" + (overallDamageMultipler.Value * 100f).ToString("N0") + "%"); } private ConfigEntry<T> CreateConfigEntry<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { ConfigEntry<T> val = configFile.Bind<T>(group, name, value, description); configSync.AddConfigEntry<T>(val).SynchronizedConfig = synchronizedSetting; return val; } private ConfigEntry<T> CreateConfigEntry<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return CreateConfigEntry(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } } internal class ConsoleLog : ConsoleCommand { private Dictionary<string, Action> _commands = new Dictionary<string, Action>(); private List<string> commandArgs; private CommandList[] commandList = new CommandList[1] { new CommandList("ReloadConfig", ReloadConfig) }; public override string Name => "DifficultyScaler"; public override string Help => "Commands for 'Cozyheim: " + ((ConsoleCommand)this).Name + "'"; public override bool IsCheat => false; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => false; private static void ReloadConfig() { if (IsUserAdmin()) { Main.configFile.Reload(); Main.CreateMonsterDamageDictionary(); Main.CreateMonsterHealthDictionary(); Print("Config reloaded!", LogType.Message); } } private static bool IsUserAdmin() { if (!SynchronizationManager.Instance.PlayerIsAdmin) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Only admins are allowed to use this command", 0, (Sprite)null); return false; } return true; } public override List<string> CommandOptionList() { List<string> list = new List<string>(); CommandList[] array = this.commandList; foreach (CommandList commandList in array) { if (!_commands.ContainsKey(commandList.name)) { _commands.Add(commandList.name, commandList.action); } list.Add(commandList.name); } return list; } public override void Run(string[] args) { string text = args[0]; commandArgs = args.ToList(); commandArgs.RemoveAt(0); foreach (KeyValuePair<string, Action> command in _commands) { if (command.Key.ToLower() == text.ToLower()) { command.Value(); return; } } Debug.Log((object)("The command doesn't exist: '" + text + "'")); } internal static void Print(object printMsg, LogType type = LogType.Info, bool debugMode = true) { if (!(Main.debugEnabled.Value && debugMode)) { return; } string text = printMsg.ToString(); switch (type) { case LogType.Info: if (Main.debugLevel.Value == LogType.Info) { Logger.LogInfo((object)text); } break; case LogType.Message: if (Main.debugLevel.Value <= LogType.Message) { Logger.LogMessage((object)text); } break; case LogType.Warning: if (Main.debugLevel.Value <= LogType.Warning) { Logger.LogWarning((object)text); } break; case LogType.Error: if (Main.debugLevel.Value <= LogType.Error) { Logger.LogError((object)text); } break; case LogType.Fatal: if (Main.debugLevel.Value <= LogType.Fatal) { Logger.LogFatal((object)text); } break; default: Logger.LogInfo((object)text); break; } } internal static void Print(object printMsg, bool debugMode) { Print(printMsg, LogType.Info, debugMode); } } internal class CommandList { public string name; public Action action; public CommandList(string name, Action action) { this.name = name; this.action = action; } } internal enum LogType { Info, Message, Error, Warning, Fatal } } namespace Cozyheim.DifficultyScaler.Patches { [HarmonyPatch(typeof(Character))] internal static class CharacterPatch { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void Setup(Character __instance) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) ZNetView val = default(ZNetView); if ((Object)(object)__instance == (Object)null || (int)__instance.m_faction == 0 || __instance.IsTamed() || !((Component)__instance).TryGetComponent<ZNetView>(ref val) || !val.IsOwner() || val.GetZDO() == null) { return; } if (Main.TryGetMonsterBaseHealthOverride(((Object)__instance).name, out var baseHealthOverrideValue)) { __instance.SetMaxHealth(baseHealthOverrideValue); } DifficultyScalerBase val2 = default(DifficultyScalerBase); if (!((Component)__instance).TryGetComponent<DifficultyScalerBase>(ref val2)) { val2 = ((Component)__instance).gameObject.AddComponent<DifficultyScalerBase>(); } if ((Object)(object)val2 == (Object)null) { ConsoleLog.Print("difficultyScalerComponent is null!", LogType.Error); return; } foreach (KeyValuePair<DifficultyScalerMultiplier, float> item in new Dictionary<DifficultyScalerMultiplier, float> { { (DifficultyScalerMultiplier)5, Main.GetBiomeMultiplier(Heightmap.FindBiome(((Component)__instance).transform.position)) }, { (DifficultyScalerMultiplier)3, Main.GetCalculatedBossKillMultiplier() }, { (DifficultyScalerMultiplier)2, EnvMan.instance.CalculateNight() ? Main.nightMultiplier.Value : 0f }, { (DifficultyScalerMultiplier)1, Main.overallHealthMultipler.Value }, { (DifficultyScalerMultiplier)0, Main.overallDamageMultipler.Value }, { (DifficultyScalerMultiplier)4, Main.starMultiplier.Value } }) { if (Main.IsMultiplierEnabled(item.Key)) { val2.SetMultiplier(item.Key, item.Value); } } } [HarmonyPatch("ApplyDamage")] [HarmonyPrefix] private static void Character_ApplyDamage_Prefix(Character __instance, ref HitData hit) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Invalid comparison between Unknown and I4 //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) if (!hit.HaveAttacker() || (int)__instance.m_faction != 0) { return; } float totalDamage = hit.GetTotalDamage(); float num = 1f; if (Main.TryGetMonsterDamage(((Object)hit.GetAttacker()).name, out var monsterDamageValue)) { float totalDamage2 = hit.GetTotalDamage(); hit.ApplyModifier(monsterDamageValue); ConsoleLog.Print("(ApplyDamage) " + ((Object)hit.GetAttacker()).name + " base damage adjusted to " + (monsterDamageValue * 100f).ToString("N0") + "% of normal damage."); ConsoleLog.Print($"-> Base damage: {totalDamage2} -> {hit.GetTotalDamage()}"); } DifficultyScalerBase val = default(DifficultyScalerBase); if (!((Component)hit.GetAttacker()).TryGetComponent<DifficultyScalerBase>(ref val)) { ConsoleLog.Print(((Object)__instance).name + " -> (ApplyDamage) Didn't find attacker's difficultyScalarBase. " + ((Object)hit.GetAttacker()).name, LogType.Error); return; } List<DifficultyScalerMultiplier> list = new List<DifficultyScalerMultiplier>(); foreach (DifficultyScalerMultiplier value in Enum.GetValues(typeof(DifficultyScalerMultiplier))) { DifficultyScalerMultiplier val2 = value; if ((int)val2 != 1 && Main.IsMultiplierEnabled(val2)) { list.Add(val2); ConsoleLog.Print($"{((Object)__instance).name} -> (ApplyDamage) {((object)(DifficultyScalerMultiplier)(ref val2)).ToString()} Bonus: +{val.GetMultiplier(val2) * 100f:N0}%"); } } num += val.GetSumOfMultipliers((IEnumerable<DifficultyScalerMultiplier>)list); hit.ApplyModifier(num); ConsoleLog.Print(((Object)__instance).name + " -> (ApplyDamage) Total bo