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 TouchGrass v1.0.0
TouchGrass.dll
Decompiled a week 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.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("TouchGrass")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("TouchGrass")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TouchGrass { [BepInPlugin("sighsorry.TouchGrass", "TouchGrass", "1.0.0")] public class TouchGrassPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } public enum FatigueStatusEffectDisplay { Off, Detailed } public enum TrainingDummyDamageType { Blunt, Slash, Pierce, Fire, Frost, Lightning, Poison, Spirit } public enum TrainingMeterDisplay { Off, Detailed } [CompilerGenerated] private sealed class <GetConfigurableSkillTypes>d__43 : IEnumerable<SkillType>, IEnumerable, IEnumerator<SkillType>, IDisposable, IEnumerator { private int <>1__state; private SkillType <>2__current; private int <>l__initialThreadId; private IEnumerator <>7__wrap1; SkillType IEnumerator<SkillType>.Current { [DebuggerHidden] get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return <>2__current; } } [DebuggerHidden] public <GetConfigurableSkillTypes>d__43(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = Enum.GetValues(typeof(SkillType)).GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } while (<>7__wrap1.MoveNext()) { SkillType val = (SkillType)<>7__wrap1.Current; if (((int)val != 0 && (int)val != 999) || 1 == 0) { <>2__current = val; <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 is IDisposable disposable) { disposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<SkillType> IEnumerable<SkillType>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <GetConfigurableSkillTypes>d__43(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<SkillType>)this).GetEnumerator(); } } internal const string ModName = "TouchGrass"; internal const string ModVersion = "1.0.0"; internal const string Author = "sighsorry"; private const string ModGUID = "sighsorry.TouchGrass"; private static string ConfigFileName = "sighsorry.TouchGrass.cfg"; private static string ConfigFileFullPath; internal static string ConnectionError; private readonly Harmony _harmony = new Harmony("sighsorry.TouchGrass"); public static readonly ManualLogSource TouchGrassLogger; private static readonly ConfigSync ConfigSync; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private const long RELOAD_DELAY = 10000000L; private static ConfigEntry<Toggle> _serverConfigLocked; internal static ConfigEntry<float> _locationFullEfficiencySeconds; internal static ConfigEntry<float> _locationFadeSeconds; internal static ConfigEntry<float> _locationMinimumMultiplier; internal static ConfigEntry<float> _stationaryRadius; internal static ConfigEntry<Toggle> _fatigueStationarySkillGains; internal static ConfigEntry<FatigueStatusEffectDisplay> _fatigueStatusEffectDisplay; internal static ConfigEntry<float> _trainingDummyHealth; internal static ConfigEntry<float> _trainingDummyCrowdingRadius; internal static ConfigEntry<int> _trainingDummyCrowdingMaxCount; internal static ConfigEntry<float> _archeryTargetSkillMultiplier; internal static ConfigEntry<TrainingDummyDamageType> _localTrainingDummyDamageType; internal static ConfigEntry<float> _localTrainingDummyDamageAmount; internal static ConfigEntry<TrainingMeterDisplay> _trainingMeterDisplay; internal static ConfigEntry<float> _trainingMeterWindowSeconds; internal static readonly Dictionary<SkillType, ConfigEntry<float>> _skillGainRates; internal static readonly Dictionary<SkillType, ConfigEntry<float>> _skillReductionRates; public void Awake() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Expected O, but got Unknown //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Expected O, but got Unknown //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Expected O, but got Unknown //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Expected O, but got Unknown //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Expected O, but got Unknown bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; _serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only."); ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); _locationFullEfficiencySeconds = config("2 - Skill Gate", "Location Full Efficiency Seconds", 120f, new ConfigDescription("Stationary farmable skill gains stay at full value for this many chained seconds before fading.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f), Array.Empty<object>())); _locationFadeSeconds = config("2 - Skill Gate", "Location Fade Seconds", 180f, new ConfigDescription("After full-efficiency time, stationary farmable skill gains fade to the minimum multiplier over this many seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 7200f), Array.Empty<object>())); _locationMinimumMultiplier = config("2 - Skill Gate", "Location Minimum Multiplier", 0.1f, new ConfigDescription("Lowest multiplier applied to repeated stationary farmable skill gains.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _stationaryRadius = config("2 - Skill Gate", "Stationary XZ Radius", 4f, new ConfigDescription("Radius on the X/Z plane. If farmable skill gains keep happening without leaving this radius, location efficiency fades.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 50f), Array.Empty<object>())); _fatigueStationarySkillGains = config("2 - Skill Gate", "Fatigue Stationary Skill Gains", Toggle.On, "If on, repeated farmable skill gains within the stationary X/Z radius share one global fading multiplier instead of being blocked outright. Affected skills: " + SkillNameFormatter.FormatList(SkillEarnGate.GetLocationFatigueSkillTypes()) + "."); _fatigueStatusEffectDisplay = config("2 - Skill Gate", "Fatigue Status Effect Display", FatigueStatusEffectDisplay.Detailed, "Controls the local player's fatigue status effect display. Detailed shows the status effect icon and static compendium details; Off hides it."); _archeryTargetSkillMultiplier = config("1 - General", "Archery Target Skill Multiplier", 1f, new ConfigDescription("Multiplier applied to skill experience awarded by piece_ArcheryTarget. 0 disables archery target skill gain.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); _trainingDummyHealth = config("3 - Training Dummy", "Training Dummy Health", 2500f, new ConfigDescription("Max health applied to piece_TrainingDummy.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100000f), Array.Empty<object>())); _trainingDummyCrowdingRadius = config("3 - Training Dummy", "Training Dummy Crowding Radius", 4f, new ConfigDescription("XZ radius used to discourage placing too many training dummies in one spot. 0 disables the crowding check.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>())); _trainingDummyCrowdingMaxCount = config("3 - Training Dummy", "Training Dummy Crowding Max Count", 4, new ConfigDescription("Maximum existing training dummies allowed inside the crowding radius. 4 means the 5th placement is discouraged. 0 disables the crowding check.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); _localTrainingDummyDamageType = config("3 - Training Dummy", "Training Dummy Damage Type", TrainingDummyDamageType.Blunt, "Damage type used by local training dummy damage tests.", synchronizedSetting: false); _localTrainingDummyDamageAmount = config("3 - Training Dummy", "Training Dummy Damage", 1f, new ConfigDescription("Damage amount used by local training dummy damage tests.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 500f), Array.Empty<object>()), synchronizedSetting: false); _trainingMeterDisplay = config("3 - Training Dummy", "Training Meter Display", TrainingMeterDisplay.Detailed, "Controls the integrated client-side training HUD for piece_TrainingDummy damage, incoming hits, and skill gains.", synchronizedSetting: false); _trainingMeterWindowSeconds = config("3 - Training Dummy", "Training Meter Window Seconds", 15f, new ConfigDescription("Rolling time window used by the training HUD.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()), synchronizedSetting: false); BindTrainingObjectConfigEvents(); BindPerSkillModifierConfigs(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SetupWatcher(); ((BaseUnityPlugin)this).Config.Save(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void OnDestroy() { SaveWithRespectToConfigSet(); _watcher?.Dispose(); } private void OnGUI() { TrainingMeter.OnGUI(); } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = true; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000) { return; } lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { TouchGrassLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { TouchGrassLogger.LogDebug((object)"Reloading configuration..."); SaveWithRespectToConfigSet(reload: true); TouchGrassLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { TouchGrassLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = now; } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); TrainingObjectTuning.ApplyAll(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void BindPerSkillModifierConfigs() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown foreach (SkillType configurableSkillType in GetConfigurableSkillTypes()) { SkillType current = configurableSkillType; string name = ((object)(SkillType)(ref current)).ToString(); _skillGainRates[current] = config("4 - Skill Gain Rate", name, 1f, new ConfigDescription("Multiplier applied to this skill's gained experience. Vanilla equivalent is 1. 0 disables gains for this skill, 2 doubles them.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); _skillReductionRates[current] = config("5 - Skill Reduction Rate", name, 1f, new ConfigDescription("Multiplier applied to this skill's death skill loss. Vanilla equivalent is 1. 0 disables death loss for this skill, 2 doubles it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); } } private void BindTrainingObjectConfigEvents() { _trainingDummyHealth.SettingChanged += delegate { TrainingObjectTuning.ApplyAll(); }; _archeryTargetSkillMultiplier.SettingChanged += delegate { TrainingObjectTuning.ApplyAll(); }; } [IteratorStateMachine(typeof(<GetConfigurableSkillTypes>d__43))] private static IEnumerable<SkillType> GetConfigurableSkillTypes() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetConfigurableSkillTypes>d__43(-2); } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting; return val2; } private ConfigEntry<T> config<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 config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } static TouchGrassPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ConnectionError = ""; TouchGrassLogger = Logger.CreateLogSource("TouchGrass"); ConfigSync = new ConfigSync("sighsorry.TouchGrass") { DisplayName = "TouchGrass", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0" }; _serverConfigLocked = null; _locationFullEfficiencySeconds = null; _locationFadeSeconds = null; _locationMinimumMultiplier = null; _stationaryRadius = null; _fatigueStationarySkillGains = null; _fatigueStatusEffectDisplay = null; _trainingDummyHealth = null; _trainingDummyCrowdingRadius = null; _trainingDummyCrowdingMaxCount = null; _archeryTargetSkillMultiplier = null; _localTrainingDummyDamageType = null; _localTrainingDummyDamageAmount = null; _trainingMeterDisplay = null; _trainingMeterWindowSeconds = null; _skillGainRates = new Dictionary<SkillType, ConfigEntry<float>>(); _skillReductionRates = new Dictionary<SkillType, ConfigEntry<float>>(); } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] internal static class SkillsRaiseSkillPatch { private static bool Prefix(Skills __instance, SkillType skillType, ref float factor) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) float sourceFactor = factor; bool flag = SkillEarnGate.AdjustSkillGain(__instance, skillType, ref factor); TrainingMeter.RecordSkillGain(__instance, skillType, sourceFactor, flag ? factor : 0f); return flag; } } [HarmonyPatch(typeof(Skills), "LowerAllSkills")] internal static class SkillsLowerAllSkillsPatch { private static bool Prefix(Skills __instance, float factor) { return SkillEarnGate.AdjustSkillReduction(__instance, factor); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class CharacterRPCDamagePatch { private static void Prefix(Character __instance, HitData hit, out TrainingMeter.IncomingDamageState __state) { TrainingDummyDamageTest.OverrideLocalIncomingDamage(__instance, hit); __state = TrainingMeter.BeforeIncomingDamage(__instance, hit); } private static void Postfix(Character __instance, TrainingMeter.IncomingDamageState __state) { TrainingMeter.AfterIncomingDamage(__instance, __state); } } [HarmonyPatch(typeof(Character), "Awake")] internal static class CharacterAwakePatch { private static void Postfix(Character __instance) { TrainingDummyRegistry.Register(__instance); TrainingObjectTuning.ApplyTrainingDummy(__instance); } } [HarmonyPatch(typeof(Character), "OnDestroy")] internal static class CharacterOnDestroyPatch { private static void Prefix(Character __instance) { TrainingDummyRegistry.Unregister(__instance); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class CharacterDamagePatch { private static void Prefix(Character __instance, HitData hit) { TrainingMeter.RecordOutgoingDamage(__instance, hit); } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class PlayerTryPlacePiecePatch { private static bool Prefix(Player __instance, Piece piece, ref bool __result) { if (TrainingDummyCrowding.CanPlace(__instance, piece)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] internal static class HumanoidBlockAttackPatch { private static void Postfix(Humanoid __instance, HitData hit) { TrainingMeter.RecordBlockResult(__instance, hit); } } [HarmonyPatch(typeof(ArcheryTarget), "Start")] internal static class ArcheryTargetStartPatch { private static void Postfix(ArcheryTarget __instance) { TrainingObjectTuning.ApplyArcheryTarget(__instance); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakePatch { private static void Postfix(ZNetScene __instance) { TrainingObjectTuning.ApplyPrefabTuning(__instance); } } [HarmonyPatch(typeof(Piece), "SetCreator")] internal static class PieceSetCreatorPatch { private static void Postfix(Piece __instance) { TrainingDummyDamageTest.StampPlacedDamageProfile(__instance); } } internal static class SkillEarnGate { internal readonly struct FatigueSnapshot { internal static readonly FatigueSnapshot Empty = new FatigueSnapshot(1f); internal readonly float FinalMultiplier; internal readonly float GlobalMultiplier; public FatigueSnapshot(float globalMultiplier) { FinalMultiplier = globalMultiplier; GlobalMultiplier = globalMultiplier; } } private struct LocationFatigueState { internal bool HasGain; internal bool HasAnchor; internal float LastGainTime; internal float ActiveSeconds; internal float AnchorX; internal float AnchorZ; internal void SetAnchor(Vector3 position) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) HasAnchor = true; AnchorX = position.x; AnchorZ = position.z; } internal void ClearGain() { HasGain = false; LastGainTime = 0f; ActiveSeconds = 0f; } internal void Clear() { HasAnchor = false; AnchorX = 0f; AnchorZ = 0f; ClearGain(); } } private const float TrainingChainGrace = 10f; private static readonly SkillType[] LocationFatigueSkillOrder; private static readonly HashSet<SkillType> LocationFatigueSkills; private static LocationFatigueState _locationFatigue; internal static bool AdjustSkillGain(Skills skills, SkillType skillType, ref float factor) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)skills == (Object)null || (Object)(object)skills.m_player != (Object)(object)Player.m_localPlayer) { return true; } if ((int)skillType == 0) { return true; } float sourceFactor = factor; factor *= GetPerSkillGainRate(skillType); if (ShouldApplyLocationFatigue(skillType, sourceFactor)) { factor *= GetLocationMultiplier(skills.m_player); } return factor > 0.0001f; } internal static bool AdjustSkillReduction(Skills skills, float factor) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)skills == (Object)null || (Object)(object)skills.m_player != (Object)(object)Player.m_localPlayer) { return true; } foreach (KeyValuePair<SkillType, Skill> skillDatum in skills.m_skillData) { float num = Mathf.Clamp01(factor * GetPerSkillReductionRate(skillDatum.Key)); Skill value = skillDatum.Value; value.m_level -= skillDatum.Value.m_level * num; skillDatum.Value.m_accumulator = 0f; } ((Character)skills.m_player).Message((MessageType)1, "$msg_skills_lowered", 0, (Sprite)null); return false; } internal static IReadOnlyList<SkillType> GetLocationFatigueSkillTypes() { return LocationFatigueSkillOrder; } private static bool ShouldApplyLocationFatigue(SkillType skillType, float sourceFactor) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 if (TouchGrassPlugin._fatigueStationarySkillGains.Value != TouchGrassPlugin.Toggle.On || !LocationFatigueSkills.Contains(skillType)) { return false; } if ((int)skillType == 108) { return IsNormalDodgeGain(sourceFactor); } return true; } private static float GetPerSkillGainRate(SkillType skillType) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return GetPerSkillConfigValue(TouchGrassPlugin._skillGainRates, skillType, 1f); } private static float GetPerSkillReductionRate(SkillType skillType) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return GetPerSkillConfigValue(TouchGrassPlugin._skillReductionRates, skillType, 1f); } private static float GetPerSkillConfigValue(IReadOnlyDictionary<SkillType, ConfigEntry<float>> configs, SkillType skillType, float fallback) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!configs.TryGetValue(skillType, out ConfigEntry<float> value)) { return fallback; } return value.Value; } private static float GetLocationMultiplier(Player player) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return 1f; } float time = Time.time; if (!IsInsideStationaryRadius(((Component)player).transform.position)) { ResetLocationFatigue(); FatigueStatusEffectManager.Update(player); return 1f; } float result = RecordLocationFatigueGain(ref _locationFatigue, time); FatigueStatusEffectManager.Update(player); return result; } private static float RecordLocationFatigueGain(ref LocationFatigueState state, float now) { if (state.HasGain) { float num = Mathf.Max(0f, now - state.LastGainTime); if (num <= 10f) { state.ActiveSeconds += num; } } state.HasGain = true; state.LastGainTime = now; state.ActiveSeconds = Mathf.Min(GetMaxTrackedActiveSeconds(), state.ActiveSeconds); return ComputeLocationMultiplier(state.ActiveSeconds); } private static void ResetLocationFatigue() { _locationFatigue.ClearGain(); } private static bool IsInsideStationaryRadius(Vector3 position) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) float value = TouchGrassPlugin._stationaryRadius.Value; if (value <= 0f) { return false; } if (!_locationFatigue.HasAnchor) { _locationFatigue.SetAnchor(position); return false; } float num = position.x - _locationFatigue.AnchorX; float num2 = position.z - _locationFatigue.AnchorZ; if (num * num + num2 * num2 > value * value) { _locationFatigue.SetAnchor(position); return false; } return true; } private static float ComputeLocationMultiplier(float activeSeconds) { float value = TouchGrassPlugin._locationFullEfficiencySeconds.Value; if (activeSeconds <= value) { return 1f; } float num = Mathf.Max(1f, TouchGrassPlugin._locationFadeSeconds.Value); float num2 = Mathf.Clamp01(TouchGrassPlugin._locationMinimumMultiplier.Value); float num3 = Mathf.Clamp01((activeSeconds - value) / num); return Mathf.Lerp(1f, num2, num3); } private static bool IsNormalDodgeGain(float sourceFactor) { return sourceFactor <= 0.11f; } internal static bool TryGetFatigueSnapshot(out FatigueSnapshot snapshot) { snapshot = FatigueSnapshot.Empty; if (TouchGrassPlugin._fatigueStationarySkillGains.Value != TouchGrassPlugin.Toggle.On || TouchGrassPlugin._fatigueStatusEffectDisplay.Value == TouchGrassPlugin.FatigueStatusEffectDisplay.Off || !_locationFatigue.HasGain) { return false; } float num = ComputeLocationMultiplier(_locationFatigue.ActiveSeconds); if (num >= 0.999f) { return false; } snapshot = new FatigueSnapshot(num); return true; } private static float GetMaxTrackedActiveSeconds() { return Mathf.Max(1f, TouchGrassPlugin._locationFullEfficiencySeconds.Value + TouchGrassPlugin._locationFadeSeconds.Value); } static SkillEarnGate() { SkillType[] array = new SkillType[20]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); LocationFatigueSkillOrder = (SkillType[])(object)array; LocationFatigueSkills = new HashSet<SkillType>(LocationFatigueSkillOrder); } } internal static class FatigueStatusEffectManager { private const string EffectObjectName = "TouchGrass_Fatigue"; private static TouchGrassFatigueStatusEffect? _prototype; private static int EffectHash => ((StatusEffect)Prototype).NameHash(); private static TouchGrassFatigueStatusEffect Prototype { get { if ((Object)(object)_prototype != (Object)null) { return _prototype; } _prototype = ScriptableObject.CreateInstance<TouchGrassFatigueStatusEffect>(); ((Object)_prototype).name = "TouchGrass_Fatigue"; ((StatusEffect)_prototype).m_name = "TouchGrass Fatigue"; ((StatusEffect)_prototype).m_category = "TouchGrass"; ((StatusEffect)_prototype).m_icon = CreateIcon(); ((StatusEffect)_prototype).m_startMessage = ""; ((StatusEffect)_prototype).m_stopMessage = ""; Object.DontDestroyOnLoad((Object)(object)_prototype); return _prototype; } } internal static void Update(Player player) { if ((Object)(object)player == (Object)null || ((Character)player).GetSEMan() == null) { return; } if (!SkillEarnGate.TryGetFatigueSnapshot(out var _)) { Remove(player); return; } SEMan sEMan = ((Character)player).GetSEMan(); if ((Object)(object)sEMan.GetStatusEffect(EffectHash) == (Object)null) { sEMan.AddStatusEffect((StatusEffect)(object)Prototype, false, 0, 0f); } } internal static void Remove(Player player) { if (player != null) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { sEMan.RemoveStatusEffect(EffectHash, true); } } } private static Sprite CreateIcon() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1 }; Color32[] array = (Color32[])(object)new Color32[1024]; Color32 val2 = default(Color32); ((Color32)(ref val2))..ctor((byte)0, (byte)0, (byte)0, (byte)0); Color32 val3 = default(Color32); ((Color32)(ref val3))..ctor((byte)89, (byte)162, (byte)105, byte.MaxValue); Color32 val4 = default(Color32); ((Color32)(ref val4))..ctor((byte)26, (byte)54, (byte)41, byte.MaxValue); Color32 val5 = default(Color32); ((Color32)(ref val5))..ctor((byte)202, (byte)231, (byte)179, byte.MaxValue); Vector2 val6 = default(Vector2); ((Vector2)(ref val6))..ctor(15.5f, 15.5f); for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { float num = Vector2.Distance(new Vector2((float)j, (float)i), val6); array[i * 32 + j] = ((num <= 14.5f) ? val4 : val2); if (num <= 12.5f) { array[i * 32 + j] = val3; } } } for (int k = 8; k <= 23; k++) { int num2 = 15 + Mathf.RoundToInt((float)(k - 16) * 0.15f); array[k * 32 + num2] = val5; array[k * 32 + num2 + 1] = val5; } for (int l = 8; l <= 16; l++) { int num3 = 16 - l; for (int m = 16; m <= 16 + num3; m++) { array[l * 32 + m] = val5; } } for (int n = 14; n <= 22; n++) { int num4 = n - 14; for (int num5 = 14 - num4; num5 <= 14; num5++) { array[n * 32 + num5] = val5; } } val.SetPixels32(array); val.Apply(false, true); Object.DontDestroyOnLoad((Object)(object)val); Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 32f); Object.DontDestroyOnLoad((Object)(object)obj); return obj; } } internal class TouchGrassFatigueStatusEffect : StatusEffect { public override bool CanAdd(Character character) { return (Object)(object)character == (Object)(object)Player.m_localPlayer; } public override bool IsDone() { SkillEarnGate.FatigueSnapshot snapshot; if (!((StatusEffect)this).IsDone()) { return !SkillEarnGate.TryGetFatigueSnapshot(out snapshot); } return true; } public override string GetIconText() { if (!SkillEarnGate.TryGetFatigueSnapshot(out var snapshot)) { return ""; } return $"{Mathf.RoundToInt(snapshot.FinalMultiplier * 100f)}%"; } public override string GetTooltipString() { return "Touch the grass Bro!"; } } internal static class SkillNameFormatter { internal static string FormatList(IReadOnlyList<SkillType> skillTypes) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < skillTypes.Count; i++) { if (i > 0) { stringBuilder.Append(", "); } stringBuilder.Append(Format(skillTypes[i])); } return stringBuilder.ToString(); } internal static string Format(SkillType skillType) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 string text = "$skill_" + ((object)(SkillType)(ref skillType)).ToString().ToLowerInvariant(); if (Localization.instance != null) { string text2 = Localization.instance.Localize(text); if (!string.IsNullOrWhiteSpace(text2) && text2 != text) { return text2; } } if ((int)skillType != 11) { return ((object)(SkillType)(ref skillType)).ToString(); } return "Fists"; } } internal static class TrainingDummyDamageTest { private readonly struct DamageProfile { internal readonly TouchGrassPlugin.TrainingDummyDamageType DamageType; internal readonly float Amount; public DamageProfile(TouchGrassPlugin.TrainingDummyDamageType damageType, float amount) { DamageType = damageType; Amount = amount; } } private const string TrainingDummyLocalizationKey = "$piece_trainingdummy"; private static readonly int PieceTrainingDummyPrefabHash = StringExtensionMethods.GetStableHashCode("piece_TrainingDummy"); private static readonly int TrainingDummyPrefabHash = StringExtensionMethods.GetStableHashCode("TrainingDummy"); private const string DummyDamageProfileVersionKey = "TouchGrass_DummyDamageProfileVersion"; private const string DummyDamageTypeKey = "TouchGrass_DummyDamageType"; private const string DummyDamageAmountKey = "TouchGrass_DummyDamageAmount"; private const int DummyDamageProfileVersion = 1; internal static void OverrideLocalIncomingDamage(Character victim, HitData hit) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (hit != null && !((Object)(object)victim == (Object)null) && !((Object)(object)victim != (Object)(object)Player.m_localPlayer)) { Character attacker = hit.GetAttacker(); if (IsTrainingDummy(attacker) && (TryGetStampedDamageProfile(attacker, out var profile) || TryGetLocalDamageProfile(out profile))) { hit.m_damage = BuildDamageTypes(profile.DamageType, profile.Amount); } } } internal static void StampPlacedDamageProfile(Piece piece) { if ((Object)(object)piece == (Object)null || !IsTrainingDummy(piece) || !TryGetLocalDamageProfile(out var profile)) { return; } ZNetView val = (((Object)(object)piece.m_nview != (Object)null) ? piece.m_nview : ((Component)piece).GetComponent<ZNetView>()); if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner()) { ZDO zDO = val.GetZDO(); if (zDO != null && piece.GetCreator() != 0L && zDO.GetInt("TouchGrass_DummyDamageType", -1) < 0) { zDO.Set("TouchGrass_DummyDamageProfileVersion", 1); zDO.Set("TouchGrass_DummyDamageType", (int)profile.DamageType); zDO.Set("TouchGrass_DummyDamageAmount", profile.Amount); } } } internal static bool IsTrainingDummy(Character attacker) { if ((Object)(object)attacker == (Object)null) { return false; } if (attacker.m_name == "$piece_trainingdummy") { return true; } if (HasTrainingDummyPrefab(attacker)) { return true; } return IsTrainingDummyPrefabName(Utils.GetPrefabName(((Component)attacker).gameObject)); } internal static bool IsTrainingDummy(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } if (piece.m_name == "$piece_trainingdummy") { return true; } ZNetView val = (((Object)(object)piece.m_nview != (Object)null) ? piece.m_nview : ((Component)piece).GetComponent<ZNetView>()); if ((Object)(object)val != (Object)null && val.IsValid() && val.GetZDO() != null && IsTrainingDummyPrefabHash(val.GetZDO().GetPrefab())) { return true; } return IsTrainingDummyPrefabName(Utils.GetPrefabName(((Component)piece).gameObject)); } internal static bool HasTrainingDummyPrefab(Character character) { if ((Object)(object)character == (Object)null) { return false; } ZNetView val = (((Object)(object)character.m_nview != (Object)null) ? character.m_nview : ((Component)character).GetComponent<ZNetView>()); if ((Object)(object)val == (Object)null) { return false; } if (val.IsValid() && val.GetZDO() != null && IsTrainingDummyPrefabHash(val.GetZDO().GetPrefab())) { return true; } return IsTrainingDummyPrefabName(val.GetPrefabName()); } internal static bool HasTrainingDummyZdoPrefab(Character character) { if ((Object)(object)character == (Object)null) { return false; } ZNetView val = (((Object)(object)character.m_nview != (Object)null) ? character.m_nview : ((Component)character).GetComponent<ZNetView>()); if ((Object)(object)val != (Object)null && val.IsValid() && val.GetZDO() != null) { return IsTrainingDummyPrefabHash(val.GetZDO().GetPrefab()); } return false; } private static bool TryGetStampedDamageProfile(Character attacker, out DamageProfile profile) { profile = default(DamageProfile); ZNetView component = ((Component)attacker).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return false; } ZDO zDO = component.GetZDO(); if (zDO == null) { return false; } int @int = zDO.GetInt("TouchGrass_DummyDamageType", -1); if (!Enum.IsDefined(typeof(TouchGrassPlugin.TrainingDummyDamageType), @int)) { return false; } float @float = zDO.GetFloat("TouchGrass_DummyDamageAmount", 0f); if (@float <= 0f) { return false; } profile = new DamageProfile((TouchGrassPlugin.TrainingDummyDamageType)@int, @float); return true; } private static bool TryGetLocalDamageProfile(out DamageProfile profile) { profile = new DamageProfile(TouchGrassPlugin._localTrainingDummyDamageType.Value, TouchGrassPlugin._localTrainingDummyDamageAmount.Value); return true; } private static bool IsTrainingDummyPrefabName(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return false; } if (!string.Equals(prefabName, "TrainingDummy", StringComparison.OrdinalIgnoreCase)) { return string.Equals(prefabName, "piece_TrainingDummy", StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsTrainingDummyPrefabHash(int prefabHash) { if (prefabHash != PieceTrainingDummyPrefabHash) { return prefabHash == TrainingDummyPrefabHash; } return true; } private static DamageTypes BuildDamageTypes(TouchGrassPlugin.TrainingDummyDamageType damageType, float amount) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) amount = Mathf.Clamp(amount, 1f, 500f); DamageTypes result = default(DamageTypes); switch (damageType) { case TouchGrassPlugin.TrainingDummyDamageType.Blunt: result.m_blunt = amount; break; case TouchGrassPlugin.TrainingDummyDamageType.Slash: result.m_slash = amount; break; case TouchGrassPlugin.TrainingDummyDamageType.Pierce: result.m_pierce = amount; break; case TouchGrassPlugin.TrainingDummyDamageType.Fire: result.m_fire = amount; break; case TouchGrassPlugin.TrainingDummyDamageType.Frost: result.m_frost = amount; break; case TouchGrassPlugin.TrainingDummyDamageType.Lightning: result.m_lightning = amount; break; case TouchGrassPlugin.TrainingDummyDamageType.Poison: result.m_poison = amount; break; case TouchGrassPlugin.TrainingDummyDamageType.Spirit: result.m_spirit = amount; break; default: result.m_blunt = amount; break; } return result; } } internal static class TrainingObjectTuning { private static readonly string[] TrainingDummyPrefabNames = new string[2] { "piece_TrainingDummy", "TrainingDummy" }; internal static void ApplyAll() { ApplyPrefabTuning(); ApplyLoadedTrainingDummies(); ApplyLoadedArcheryTargets(); } internal static void ApplyPrefabTuning(ZNetScene? scene = null) { if (scene == null) { scene = ZNetScene.instance; } if ((Object)(object)scene == (Object)null) { return; } string[] trainingDummyPrefabNames = TrainingDummyPrefabNames; foreach (string text in trainingDummyPrefabNames) { GameObject prefab = scene.GetPrefab(text); if (!((Object)(object)prefab == (Object)null)) { Character componentInChildren = prefab.GetComponentInChildren<Character>(true); if ((Object)(object)componentInChildren != (Object)null) { ApplyTrainingDummy(componentInChildren); } } } GameObject prefab2 = scene.GetPrefab("piece_ArcheryTarget"); if ((Object)(object)prefab2 != (Object)null) { ArcheryTarget componentInChildren2 = prefab2.GetComponentInChildren<ArcheryTarget>(true); if ((Object)(object)componentInChildren2 != (Object)null) { ApplyArcheryTarget(componentInChildren2); } } } internal static void ApplyTrainingDummy(Character character) { if (!((Object)(object)character == (Object)null) && TrainingDummyDamageTest.IsTrainingDummy(character)) { float trainingDummyHealth = GetTrainingDummyHealth(); ZNetView nview = character.m_nview; bool flag = (Object)(object)nview != (Object)null && nview.IsValid(); float num = Mathf.Max(0.001f, flag ? character.GetMaxHealth() : character.m_health); float num2 = Mathf.Max(0f, flag ? character.GetHealth() : num); float num3 = Mathf.Clamp01(num2 / num); bool flag2 = num2 >= num - 0.01f; character.m_health = trainingDummyHealth; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner()) { character.SetMaxHealth(trainingDummyHealth); character.SetHealth(flag2 ? trainingDummyHealth : Mathf.Clamp(trainingDummyHealth * num3, 0f, trainingDummyHealth)); } } } internal static void ApplyArcheryTarget(ArcheryTarget archeryTarget) { if (!((Object)(object)archeryTarget == (Object)null)) { archeryTarget.m_raiseSkillMultiplier = GetArcheryTargetSkillMultiplier(); } } private static void ApplyLoadedTrainingDummies() { foreach (Character loadedDummy in TrainingDummyRegistry.GetLoadedDummies()) { ApplyTrainingDummy(loadedDummy); } } private static void ApplyLoadedArcheryTargets() { ArcheryTarget[] array = Object.FindObjectsByType<ArcheryTarget>((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { ApplyArcheryTarget(array[i]); } } private static float GetTrainingDummyHealth() { return Mathf.Clamp(TouchGrassPlugin._trainingDummyHealth?.Value ?? 2500f, 1f, 100000f); } private static float GetArcheryTargetSkillMultiplier() { return Mathf.Clamp(TouchGrassPlugin._archeryTargetSkillMultiplier?.Value ?? 1f, 0f, 10f); } } internal static class TrainingDummyRegistry { private static readonly HashSet<Character> LoadedDummies = new HashSet<Character>(); internal static void Register(Character character) { if (!((Object)(object)character == (Object)null) && TrainingDummyDamageTest.HasTrainingDummyZdoPrefab(character)) { LoadedDummies.Add(character); } } internal static void Unregister(Character character) { if (!((Object)(object)character == (Object)null)) { LoadedDummies.Remove(character); } } internal static IReadOnlyCollection<Character> GetLoadedDummies() { LoadedDummies.RemoveWhere((Character character) => (Object)(object)character == (Object)null); return LoadedDummies; } } internal static class TrainingDummyCrowding { private const string CrowdedMessage = "Too crowded Bro!"; internal static bool CanPlace(Player player, Piece piece) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || !TrainingDummyDamageTest.IsTrainingDummy(piece) || (Object)(object)player.m_placementGhost == (Object)null) { return true; } if (!IsCrowded(player.m_placementGhost.transform.position)) { return true; } ((Character)player).Message((MessageType)2, "Too crowded Bro!", 0, (Sprite)null); return false; } private static bool IsCrowded(Vector3 position) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp(TouchGrassPlugin._trainingDummyCrowdingRadius?.Value ?? 4f, 0f, 50f); int num2 = Mathf.Clamp(TouchGrassPlugin._trainingDummyCrowdingMaxCount?.Value ?? 4, 0, 100); if (num <= 0f || num2 <= 0) { return false; } float num3 = num * num; int num4 = 0; foreach (Character loadedDummy in TrainingDummyRegistry.GetLoadedDummies()) { if ((Object)(object)loadedDummy == (Object)null) { continue; } Vector3 position2 = ((Component)loadedDummy).transform.position; float num5 = position2.x - position.x; float num6 = position2.z - position.z; if (!(num5 * num5 + num6 * num6 > num3)) { num4++; if (num4 >= num2) { return true; } } } return false; } } internal static class TrainingMeter { internal sealed class IncomingDamageState { internal bool IsTrainingDummyHit; internal bool WasBlocking; internal float Time; internal float Health; internal float RawDamage; internal float BlockedDamage; internal string RawDamageType = ""; internal DamageTypes DamageAfterBlock; } private readonly struct OutgoingDamageEvent { internal readonly float Time; internal readonly float ImpactDamage; internal readonly float StatusDamage; public OutgoingDamageEvent(float time, float impactDamage, float statusDamage) { Time = time; ImpactDamage = impactDamage; StatusDamage = statusDamage; } } private readonly struct IncomingDamageEvent { internal readonly float Time; internal readonly float RawDamage; internal readonly string RawDamageType; internal readonly float BlockedDamage; internal readonly float ResistReduction; internal readonly float ArmorReduction; internal readonly float FinalDamage; internal readonly float StatusDamage; internal readonly string StatusDamageType; public IncomingDamageEvent(float time, float rawDamage, string rawDamageType, float blockedDamage, float resistReduction, float armorReduction, float finalDamage, float statusDamage, string statusDamageType) { Time = time; RawDamage = rawDamage; RawDamageType = rawDamageType; BlockedDamage = blockedDamage; ResistReduction = resistReduction; ArmorReduction = armorReduction; FinalDamage = finalDamage; StatusDamage = statusDamage; StatusDamageType = statusDamageType; } } private readonly struct SkillGainEvent { internal readonly float Time; internal readonly SkillType SkillType; internal readonly float SourceExp; internal readonly float FinalExp; internal readonly float Efficiency; public SkillGainEvent(float time, SkillType skillType, float sourceExp, float finalExp, float efficiency) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Time = time; SkillType = skillType; SourceExp = sourceExp; FinalExp = finalExp; Efficiency = efficiency; } } private readonly struct DamageReductionBreakdown { internal readonly float ResistReduction; internal readonly float ArmorReduction; internal readonly float FinalDamage; internal readonly float StatusDamage; internal readonly string StatusDamageType; public DamageReductionBreakdown(float resistReduction, float armorReduction, float finalDamage, float statusDamage, string statusDamageType) { ResistReduction = resistReduction; ArmorReduction = armorReduction; FinalDamage = finalDamage; StatusDamage = statusDamage; StatusDamageType = statusDamageType; } } private const float HugeDamageThreshold = 1E+09f; private const string HudTitle = "TouchGrass Training Meter"; private const float HudPaddingX = 10f; private const float HudPaddingY = 8f; private const float HudLineSpacing = 2f; private const float HudHeaderSpacing = 4f; private const float HudHeightSafetyPadding = 8f; private const float HudHeightHoldSeconds = 0.35f; private const float HudScale = 1.6f; private const float HudBaseWidth = 420f; private const float DefaultHudAnchorX = 0.02f; private const float DefaultHudAnchorY = 0.86f; private static readonly List<OutgoingDamageEvent> OutgoingDamageEvents = new List<OutgoingDamageEvent>(); private static readonly List<IncomingDamageEvent> IncomingDamageEvents = new List<IncomingDamageEvent>(); private static readonly List<SkillGainEvent> SkillGainEvents = new List<SkillGainEvent>(); private static readonly HashSet<SkillType> DisplayedSkillTypes = new HashSet<SkillType> { (SkillType)1, (SkillType)2, (SkillType)3, (SkillType)4, (SkillType)5, (SkillType)6, (SkillType)7, (SkillType)8, (SkillType)14, (SkillType)11, (SkillType)12, (SkillType)13, (SkillType)9, (SkillType)10 }; private static float _lastTrainingDummyInteractionTime = -9999f; private static float _lastHudHeight; private static float _lastHudHeightHoldUntil = -9999f; private static Vector2 _hudPosition; private static Vector2 _hudDragOffset; private static bool _hudPositionInitialized; private static bool _isDraggingHud; private static IncomingDamageState? _activeIncomingDamageState; private static GUIStyle? _boxStyle; private static GUIStyle? _headerStyle; private static GUIStyle? _labelStyle; private static Texture2D? _backgroundTexture; private static float _styleScale = -1f; internal static void RecordOutgoingDamage(Character target, HitData hit) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (IsEnabled() && !((Object)(object)target == (Object)null) && hit != null && TrainingDummyDamageTest.IsTrainingDummy(target) && !((Object)(object)hit.GetAttacker() != (Object)(object)Player.m_localPlayer)) { float statusDamage = GetStatusDamage(hit.m_damage); float num = Mathf.Max(0f, ((DamageTypes)(ref hit.m_damage)).GetTotalDamage() - statusDamage); if ((!(num <= 0f) || !(statusDamage <= 0f)) && !(num + statusDamage > 1E+09f)) { float time = Time.time; MarkTrainingDummyInteraction(time); OutgoingDamageEvents.Add(new OutgoingDamageEvent(time, num, statusDamage)); Prune(time); } } } internal static IncomingDamageState BeforeIncomingDamage(Character victim, HitData hit) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) IncomingDamageState incomingDamageState = new IncomingDamageState(); if (!IsEnabled() || (Object)(object)victim == (Object)null || hit == null) { return incomingDamageState; } if ((Object)(object)victim != (Object)(object)Player.m_localPlayer) { return incomingDamageState; } _activeIncomingDamageState = null; if (!TrainingDummyDamageTest.IsTrainingDummy(hit.GetAttacker())) { return incomingDamageState; } _ = Player.m_localPlayer; float time = Time.time; MarkTrainingDummyInteraction(time); incomingDamageState.IsTrainingDummyHit = true; incomingDamageState.Time = time; incomingDamageState.Health = victim.GetHealth(); incomingDamageState.WasBlocking = hit.m_blockable && victim.IsBlocking(); incomingDamageState.RawDamage = ((DamageTypes)(ref hit.m_damage)).GetTotalDamage(); incomingDamageState.RawDamageType = GetDamageTypeText(hit.m_damage); incomingDamageState.DamageAfterBlock = ((DamageTypes)(ref hit.m_damage)).Clone(); _activeIncomingDamageState = incomingDamageState; return incomingDamageState; } internal static void AfterIncomingDamage(Character victim, IncomingDamageState state) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (state != null && state.IsTrainingDummyHit && !((Object)(object)victim == (Object)null) && !((Object)(object)victim != (Object)(object)Player.m_localPlayer)) { _activeIncomingDamageState = null; DamageReductionBreakdown damageReductionBreakdown = ComputeIncomingBreakdown(Player.m_localPlayer, state.DamageAfterBlock); IncomingDamageEvents.Add(new IncomingDamageEvent(state.Time, state.RawDamage, state.RawDamageType, state.BlockedDamage, damageReductionBreakdown.ResistReduction, damageReductionBreakdown.ArmorReduction, damageReductionBreakdown.FinalDamage, damageReductionBreakdown.StatusDamage, damageReductionBreakdown.StatusDamageType)); Prune(Time.time); } } internal static void RecordBlockResult(Humanoid blocker, HitData hit) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (_activeIncomingDamageState != null && _activeIncomingDamageState.IsTrainingDummyHit && !((Object)(object)blocker == (Object)null) && !((Object)(object)blocker != (Object)(object)Player.m_localPlayer) && hit != null) { _activeIncomingDamageState.DamageAfterBlock = ((DamageTypes)(ref hit.m_damage)).Clone(); _activeIncomingDamageState.BlockedDamage = Mathf.Max(0f, _activeIncomingDamageState.RawDamage - ((DamageTypes)(ref hit.m_damage)).GetTotalDamage()); } } internal static void RecordSkillGain(Skills skills, SkillType skillType, float sourceFactor, float finalFactor) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled() || (Object)(object)skills == (Object)null || (Object)(object)skills.m_player != (Object)(object)Player.m_localPlayer || (int)skillType == 0 || !DisplayedSkillTypes.Contains(skillType)) { return; } float time = Time.time; if (IsTrainingSessionActive(time)) { float num = ComputeSkillExperience(skills, skillType, sourceFactor); float num2 = ComputeSkillExperience(skills, skillType, Mathf.Max(0f, finalFactor)); if (!(num <= 0f) || !(num2 <= 0f)) { float efficiency = ((num > 0f) ? Mathf.Clamp01(num2 / num) : 1f); SkillGainEvents.Add(new SkillGainEvent(time, skillType, num, num2, efficiency)); Prune(time); } } } internal static void OnGUI() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled() || (Object)(object)Player.m_localPlayer == (Object)null) { StopHudDrag(); return; } float time = Time.time; Prune(time); if (!IsTrainingSessionActive(time)) { StopHudDrag(); return; } List<string> list = BuildHudLines(time); if (list.Count == 0) { StopHudDrag(); return; } float num = 1.6f; EnsureStyles(num); GUIStyle headerStyle = _headerStyle; GUIStyle labelStyle = _labelStyle; float num2 = 10f * num; float num3 = 8f * num; float num4 = 2f * num; float num5 = 4f * num; float hudWidth = GetHudWidth(); float num6 = hudWidth - num2 * 2f; float stableHudHeight = GetStableHudHeight(GetHudHeight(list, num6, headerStyle, labelStyle, num3, num4, num5, num), time); Rect val = GetHudRect(hudWidth, stableHudHeight); val = HandleHudDrag(val); GUI.Box(val, GUIContent.none, _boxStyle); float num7 = ((Rect)(ref val)).x + num2; float num8 = ((Rect)(ref val)).y + num3; float num9 = Mathf.Ceil(headerStyle.CalcHeight(new GUIContent("TouchGrass Training Meter"), num6)); GUI.Label(new Rect(num7, num8, num6, num9), "TouchGrass Training Meter", headerStyle); num8 += num9 + num5; foreach (string item in list) { float num10 = Mathf.Ceil(labelStyle.CalcHeight(new GUIContent(item), num6)); GUI.Label(new Rect(num7, num8, num6, num10), item, labelStyle); num8 += num10 + num4; } } private static List<string> BuildHudLines(float now) { //IL_0461: Unknown result type (might be due to invalid IL or missing references) float num = now - GetWindowSeconds(); float num2 = now; float num3 = 0f; float num4 = 0f; float num5 = 0f; int num6 = 0; int num7 = 0; OutgoingDamageEvent? outgoingDamageEvent = null; IncomingDamageEvent? incomingDamageEvent = null; SkillGainEvent? skillGainEvent = null; foreach (OutgoingDamageEvent outgoingDamageEvent2 in OutgoingDamageEvents) { if (!(outgoingDamageEvent2.Time < num)) { num2 = Mathf.Min(num2, outgoingDamageEvent2.Time); num3 = Mathf.Max(num3, outgoingDamageEvent2.Time); num4 += outgoingDamageEvent2.ImpactDamage; num5 += outgoingDamageEvent2.StatusDamage; num6++; if (!outgoingDamageEvent.HasValue || outgoingDamageEvent2.Time > outgoingDamageEvent.Value.Time) { outgoingDamageEvent = outgoingDamageEvent2; } } } foreach (IncomingDamageEvent incomingDamageEvent2 in IncomingDamageEvents) { if (!(incomingDamageEvent2.Time < num)) { num2 = Mathf.Min(num2, incomingDamageEvent2.Time); num3 = Mathf.Max(num3, incomingDamageEvent2.Time); num7++; if (!incomingDamageEvent.HasValue || incomingDamageEvent2.Time > incomingDamageEvent.Value.Time) { incomingDamageEvent = incomingDamageEvent2; } } } foreach (SkillGainEvent skillGainEvent2 in SkillGainEvents) { if (!(skillGainEvent2.Time < num)) { num2 = Mathf.Min(num2, skillGainEvent2.Time); num3 = Mathf.Max(num3, skillGainEvent2.Time); if (!skillGainEvent.HasValue || skillGainEvent2.Time > skillGainEvent.Value.Time) { skillGainEvent = skillGainEvent2; } } } if (num6 == 0 && num7 == 0 && !skillGainEvent.HasValue) { return new List<string>(); } if (num3 <= 0f) { num3 = now; } float num8 = Mathf.Max(1f, Mathf.Min(GetWindowSeconds(), now - num2)); float value = num4 / num8; float value2 = ((num6 > 0) ? (num4 / (float)num6) : 0f); List<string> list = new List<string>(); if (num6 > 0) { if (num5 > 0f) { list.Add("ToDummy: Status | Attempt | DPH | Hits | DPS | Time"); list.Add($"ToDummy: {FormatFloat(num5)} | {FormatFloat(num4)} | {FormatFloat(value2)} | {num6} | {FormatFloat(value)} | {FormatFloat(num8)}s"); } else { list.Add("ToDummy: Attempt | DPH | Hits | DPS | Time"); list.Add($"ToDummy: {FormatFloat(num4)} | {FormatFloat(value2)} | {num6} | {FormatFloat(value)} | {FormatFloat(num8)}s"); } } if (incomingDamageEvent.HasValue) { IncomingDamageEvent value3 = incomingDamageEvent.Value; string text = "Raw - Blocked - Resist - Armor = Final"; string text2 = string.Concat(FormatFloat(value3.RawDamage) + "(" + value3.RawDamageType + ") - " + FormatFloat(value3.BlockedDamage) + " - " + FormatSubtractedSigned(value3.ResistReduction) + " - " + FormatFloat(value3.ArmorReduction), " = ", FormatFloat(value3.FinalDamage)); if (value3.StatusDamage > 0f) { list.Add("FromDummy: " + text + " | Status"); list.Add("FromDummy: " + text2 + " | " + FormatFloat(value3.StatusDamage) + "(" + value3.StatusDamageType + ")"); } else { list.Add("FromDummy: " + text); list.Add("FromDummy: " + text2); } } if (skillGainEvent.HasValue) { SkillGainEvent value4 = skillGainEvent.Value; list.Add("Skill: " + FormatSkill(value4.SkillType) + " +" + FormatFloat(value4.FinalExp) + " / +" + FormatFloat(value4.SourceExp) + " exp (" + FormatPercent(value4.Efficiency) + ")"); } return list; } private static void MarkTrainingDummyInteraction(float now) { _lastTrainingDummyInteractionTime = now; } private static bool IsTrainingSessionActive(float now) { return now - _lastTrainingDummyInteractionTime <= GetWindowSeconds(); } private static bool IsEnabled() { if (TouchGrassPlugin._trainingMeterDisplay != null) { return TouchGrassPlugin._trainingMeterDisplay.Value != TouchGrassPlugin.TrainingMeterDisplay.Off; } return false; } private static float GetWindowSeconds() { return Mathf.Clamp(TouchGrassPlugin._trainingMeterWindowSeconds?.Value ?? 15f, 5f, 300f); } private static float GetHudWidth() { return Mathf.Clamp(672f, 240f, (float)Screen.width); } private static Rect GetHudRect(float width, float height) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!_hudPositionInitialized) { _hudPosition = new Vector2(0.02f * (float)Screen.width, 0.13999999f * (float)Screen.height); _hudPositionInitialized = true; } _hudPosition = ClampHudPosition(_hudPosition, width, height); return new Rect(_hudPosition.x, _hudPosition.y, width, height); } private static Rect HandleHudDrag(Rect rect) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected I4, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current == null) { return rect; } if (_isDraggingHud && !Input.GetMouseButton(0)) { StopHudDrag(); } EventType type = current.type; switch ((int)type) { case 0: if (current.button == 0 && ((Rect)(ref rect)).Contains(current.mousePosition)) { _isDraggingHud = true; _hudDragOffset = current.mousePosition - new Vector2(((Rect)(ref rect)).x, ((Rect)(ref rect)).y); current.Use(); } break; case 3: if (current.button == 0 && _isDraggingHud) { _hudPosition = ClampHudPosition(current.mousePosition - _hudDragOffset, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height); ((Rect)(ref rect)).position = _hudPosition; current.Use(); } break; case 1: if (current.button == 0 && _isDraggingHud) { _isDraggingHud = false; current.Use(); } break; } if (_isDraggingHud) { ((Rect)(ref rect)).position = _hudPosition; } return rect; } private static void StopHudDrag() { _isDraggingHud = false; } private static Vector2 ClampHudPosition(Vector2 position, float width, float height) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) position.x = Mathf.Clamp(position.x, 0f, Mathf.Max(0f, (float)Screen.width - width)); position.y = Mathf.Clamp(position.y, 0f, Mathf.Max(0f, (float)Screen.height - height)); return position; } private static float GetHudHeight(List<string> lines, float contentWidth, GUIStyle headerStyle, GUIStyle labelStyle, float paddingY, float lineSpacing, float headerSpacing, float scale) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown float num = paddingY * 2f; num += Mathf.Ceil(headerStyle.CalcHeight(new GUIContent("TouchGrass Training Meter"), contentWidth)) + headerSpacing; foreach (string line in lines) { num += Mathf.Ceil(labelStyle.CalcHeight(new GUIContent(line), contentWidth)) + lineSpacing; } return num + 8f * scale; } private static float GetStableHudHeight(float desiredHeight, float now) { if (desiredHeight >= _lastHudHeight) { _lastHudHeight = desiredHeight; _lastHudHeightHoldUntil = now + 0.35f; return desiredHeight; } if (now <= _lastHudHeightHoldUntil) { return _lastHudHeight; } _lastHudHeight = desiredHeight; _lastHudHeightHoldUntil = now + 0.35f; return desiredHeight; } private static void Prune(float now) { float oldest = now - GetWindowSeconds(); OutgoingDamageEvents.RemoveAll((OutgoingDamageEvent e) => e.Time < oldest); IncomingDamageEvents.RemoveAll((IncomingDamageEvent e) => e.Time < oldest); SkillGainEvents.RemoveAll((SkillGainEvent e) => e.Time < oldest); } private static float ComputeSkillExperience(Skills skills, SkillType skillType, float factor) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (factor <= 0f) { return 0f; } Skill skill = skills.GetSkill(skillType); if (skill == null || skill.m_level >= 100f) { return 0f; } return skill.m_info.m_increseStep * factor * Game.m_skillGainRate; } private static DamageReductionBreakdown ComputeIncomingBreakdown(Player player, DamageTypes damageAfterBlock) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) HitData val = new HitData { m_damage = ((DamageTypes)(ref damageAfterBlock)).Clone() }; float totalDamage = ((DamageTypes)(ref val.m_damage)).GetTotalDamage(); DamageModifier val2 = default(DamageModifier); val.ApplyResistance(((Character)player).GetDamageModifiers((WeakSpot)null), ref val2); float totalDamage2 = ((DamageTypes)(ref val.m_damage)).GetTotalDamage(); val.ApplyArmor(((Character)player).GetBodyArmor()); float totalDamage3 = ((DamageTypes)(ref val.m_damage)).GetTotalDamage(); float statusDamage = GetStatusDamage(val.m_damage); float num = Mathf.Max(0f, totalDamage3 - statusDamage); if (num <= 0.1f) { num = 0f; } return new DamageReductionBreakdown(totalDamage - totalDamage2, Mathf.Max(0f, totalDamage2 - totalDamage3), num, statusDamage, GetStatusDamageTypeText(val.m_damage)); } private static float GetStatusDamage(DamageTypes damage) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) return Mathf.Max(0f, damage.m_fire) + Mathf.Max(0f, damage.m_poison) + Mathf.Max(0f, damage.m_spirit); } private static string GetStatusDamageTypeText(DamageTypes damage) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) string name = "Status"; float value = 0f; UseIfHigher("Fire", damage.m_fire); UseIfHigher("Poison", damage.m_poison); UseIfHigher("Spirit", damage.m_spirit); if (!(value > 0f)) { return ""; } return name; void UseIfHigher(string candidateName, float candidateValue) { if (candidateValue > value) { name = candidateName; value = candidateValue; } } } private static string GetDamageTypeText(DamageTypes damage) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) string name = "Generic"; float value = damage.m_damage; UseIfHigher("Blunt", damage.m_blunt); UseIfHigher("Slash", damage.m_slash); UseIfHigher("Pierce", damage.m_pierce); UseIfHigher("Fire", damage.m_fire); UseIfHigher("Frost", damage.m_frost); UseIfHigher("Lightning", damage.m_lightning); UseIfHigher("Poison", damage.m_poison); UseIfHigher("Spirit", damage.m_spirit); if (!(value > 0f)) { return "None"; } return name; void UseIfHigher(string candidateName, float candidateValue) { if (candidateValue > value) { name = candidateName; value = candidateValue; } } } private static string FormatSkill(SkillType skillType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if ((int)skillType == 0) { return "-"; } return SkillNameFormatter.Format(skillType); } private static string FormatFloat(float value) { if (value >= 100f) { return value.ToString("0"); } if (!(value >= 10f)) { return value.ToString("0.00"); } return value.ToString("0.0"); } private static string FormatPercent(float value) { return $"{Mathf.RoundToInt(Mathf.Clamp01(value) * 100f)}%"; } private static string FormatSigned(float value) { if (!(value >= 0f)) { return "- " + FormatFloat(Mathf.Abs(value)); } return "+ " + FormatFloat(value); } private static string FormatSubtractedSigned(float value) { if (!(value >= 0f)) { return "(" + FormatSigned(value) + ")"; } return FormatFloat(value); } private static void EnsureStyles(float scale) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_00de: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Expected O, but got Unknown //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (_boxStyle == null || _headerStyle == null || _labelStyle == null || !Mathf.Approximately(_styleScale, scale)) { _styleScale = scale; if ((Object)(object)_backgroundTexture == (Object)null) { _backgroundTexture = new Texture2D(1, 1, (TextureFormat)4, false); _backgroundTexture.SetPixel(0, 0, new Color(0.04f, 0.06f, 0.05f, 0.82f)); _backgroundTexture.Apply(false, true); Object.DontDestroyOnLoad((Object)(object)_backgroundTexture); } GUIStyle val = new GUIStyle(GUI.skin.box); val.normal.background = _backgroundTexture; val.border = new RectOffset(Mathf.RoundToInt(8f * scale), Mathf.RoundToInt(8f * scale), Mathf.RoundToInt(8f * scale), Mathf.RoundToInt(8f * scale)); _boxStyle = val; _headerStyle = new GUIStyle(GUI.skin.label) { fontSize = Mathf.Max(8, Mathf.RoundToInt(14f * scale)), richText = false }; _headerStyle.normal.textColor = new Color(0.82f, 1f, 0.78f, 1f); _labelStyle = new GUIStyle(GUI.skin.label) { fontSize = Mathf.Max(8, Mathf.RoundToInt(13f * scale)), richText = false, wordWrap = true }; _labelStyle.normal.textColor = Color.white; } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] public static class RegisterAndCheckVersion { private static void Prefix(ZNetPeer peer, ref ZNet __instance) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown TouchGrassPlugin.TouchGrassLogger.LogDebug((object)"Registering version RPC handler"); peer.m_rpc.Register<ZPackage>("TouchGrass_VersionCheck", (Action<ZRpc, ZPackage>)RpcHandlers.RPC_TouchGrass_Version); TouchGrassPlugin.TouchGrassLogger.LogInfo((object)"Invoking version check"); ZPackage val = new ZPackage(); val.Write("1.0.0"); peer.m_rpc.Invoke("TouchGrass_VersionCheck", new object[1] { val }); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class VerifyClient { private static bool Prefix(ZRpc rpc, ZPackage pkg, ref ZNet __instance) { if (!__instance.IsServer() || RpcHandlers.ValidatedPeers.Contains(rpc)) { return true; } TouchGrassPlugin.TouchGrassLogger.LogWarning((object)("Peer (" + rpc.m_socket.GetHostName() + ") never sent version or couldn't due to previous disconnect, disconnecting")); rpc.Invoke("Error", new object[1] { 3 }); return false; } private static void Postfix(ZNet __instance) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "TouchGrassRequestAdminSync", new object[1] { (object)new ZPackage() }); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] public class ShowConnectionError { private static void Postfix(FejdStartup __instance) { if (__instance.m_connectionFailedPanel.activeSelf) { __instance.m_connectionFailedError.fontSizeMax = 25f; __instance.m_connectionFailedError.fontSizeMin = 15f; TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + TouchGrassPlugin.ConnectionError; } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] public static class RemoveDisconnectedPeerFromVerified { private static void Prefix(ZNetPeer peer, ref ZNet __instance) { if (__instance.IsServer()) { TouchGrassPlugin.TouchGrassLogger.LogInfo((object)("Peer (" + peer.m_rpc.m_socket.GetHostName() + ") disconnected, removing from validated list")); RpcHandlers.ValidatedPeers.Remove(peer.m_rpc); } } } public static class RpcHandlers { public static readonly List<ZRpc> ValidatedPeers = new List<ZRpc>(); public static void RPC_TouchGrass_Version(ZRpc rpc, ZPackage pkg) { string text = pkg.ReadString(); TouchGrassPlugin.TouchGrassLogger.LogInfo((object)("Version check, local: 1.0.0, remote: " + text)); if (text != "1.0.0") { TouchGrassPlugin.ConnectionError = "TouchGrass Installed: 1.0.0\n Needed: " + text; if (ZNet.instance.IsServer()) { TouchGrassPlugin.TouchGrassLogger.LogWarning((object)("Peer (" + rpc.m_socket.GetHostName() + ") has incompatible version, disconnecting...")); rpc.Invoke("Error", new object[1] { 3 }); } } else if (!ZNet.instance.IsServer()) { TouchGrassPlugin.TouchGrassLogger.LogInfo((object)"Received same version from server!"); } else { TouchGrassPlugin.TouchGrassLogger.LogInfo((object)("Adding peer (" + rpc.m_socket.GetHostName() + ") to validated list")); ValidatedPeers.Add(rpc); } } } } namespace ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal 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; base..ctor(); } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } internal abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; public readonly Type Type; private object? boxedValue; protected bool localIsOwner; public readonly int Priority; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] internal 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), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] internal 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) { ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; 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() { MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); 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> adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); }).ToList(); List<ZNetPeer> nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, 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_FromServerConfigSync); } } } 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(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List<ZPackage> Package = new List<ZPackage>(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; ((ZPlayFabSocket)this)..ctor(); } 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 if (!__instance.IsServer()) { return; } BufferingSocket bufferingSocket = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket); 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) { FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket"); object? value = fieldInfo.GetValue(val); ZPlayFabSocket val2 = (ZPlayFabSocket)((value is ZPlayFabSocket) ? value : null); if (val2 != null) { typeof(ZPlayFabSocket).GetField("m_remotePlayerId").SetValue(bufferingSocket, val2.m_remotePlayerId); } fieldInfo.SetValue(val, bufferingSocket); } if (__state == null) { __state = new Dictionary<Assembly, BufferingSocket>(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc) { ZRpc rpc2 = rpc; ZNet __instance2 = __instance; Dictionary<Assembly, BufferingSocket> __state2 = __state; ZNetPeer peer; if (__instance2.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance2).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc2.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc2, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state2[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> entries = new List<PackageEntry>(); if (configSync.CurrentVersion != null) { entries.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); entries.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc2.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc2.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance2).StartCoroutine(configSync.sendZPackage(new List<ZNetPeer> { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string section = null; public string key = null; public Type type = null; 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 = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; public string? DisplayName; public string? CurrentVersion; public string? MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet<ConfigSync> configSyncs; private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); private HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private static bool isServer; private static bool lockExempt; private OwnConfigEntryBase? lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>(); private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>(); private static long packageCounter; public bool IsL