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 Bizare Score HUD v1.0.1
BepInEx/plugins/BizareScoreHUD/BizareScoreHUD.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.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BizareScoreHUD.Compatibility; using BizareScoreHUD.Domain; using BizareScoreHUD.Networking; using BizareScoreHUD.Patches; using BizareScoreHUD.Persistence; using BizareScoreHUD.Tracking; using BizareScoreHUD.UI; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using Splatform; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using Valheim.SettingsGui; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BizareScoreHUD")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Score HUD for the Bizare Valheim modpack.")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+239dbd73cb87f90f4fab1f6ff6b95a7a8ea3bcf5")] [assembly: AssemblyProduct("BizareScoreHUD")] [assembly: AssemblyTitle("BizareScoreHUD")] [assembly: AssemblyVersion("1.0.1.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace BizareScoreHUD { [BepInPlugin("com.igentuman.bizarehud", "Score HUD for Bizare", "1.0.1")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.igentuman.bizarehud"; public const string PluginName = "Score HUD for Bizare"; public const string PluginVersion = "1.0.1"; private StatsRuntime? _runtime; private long _lastSecond = -1L; private Snapshot? _lastSnapshot; private ConfigEntry<float> _hudScale; private ConfigEntry<Vector2> _hudPosition; private Harmony? _patches; private float? _scalePreview; private StatsHud? _hud; private Hud? _gameHud; private bool _headless; internal static Plugin? Instance { get; private set; } internal ConfigEntry<float> HudScale => _hudScale; private void Awake() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) Instance = this; _runtime = new StatsRuntime(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); _patches = new Harmony("com.igentuman.bizarehud"); _patches.PatchAll(typeof(Plugin).Assembly); _headless = GUIManager.IsHeadless(); if (!_headless) { HudLocalization.Register(); _hudScale = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Scale", 0.65f, new ConfigDescription("Scale the entire HUD relative to the game's UI scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>())); _hudPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("HUD", "Position", new Vector2(-1f, -1f), "Normalized panel position from the top left (0–1). Negative values use the default position. Drag the header with inventory open to move it."); _hudScale.SettingChanged += OnPresentationChanged; } } private void Start() { try { ArenaAdapter.Install(_patches); ArenaAdapter.InstallTraders(_patches); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Compatibility adapter could not be installed: " + ex)); } } private void Update() { _runtime?.Tick(); } private void LateUpdate() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (_headless) { return; } Hud instance = Hud.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_rootObject == (Object)null) { RemoveHud(); return; } if ((Object)(object)_gameHud != (Object)(object)instance || (Object)(object)_hud == (Object)null) { RemoveHud(); _gameHud = instance; _hud = StatsHud.Create(instance.m_rootObject.transform); _hud.InitializePosition(_hudPosition.Value, delegate(Vector2 position) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) _hudPosition.Value = position; }); ApplyPresentation(); } _hud.SetVisible((Object)(object)Player.m_localPlayer != (Object)null && instance.IsVisible() && !((Component)instance.m_loadingScreen).gameObject.activeInHierarchy); long num = (long)(_runtime?.Elapsed.TotalSeconds ?? 0.0); if (_lastSnapshot != _runtime?.Current || _lastSecond != num) { _lastSnapshot = _runtime?.Current; _lastSecond = num; ApplyPresentation(); } _hud.UpdateLayout(); } private void OnPresentationChanged(object sender, EventArgs args) { ApplyPresentation(); } private void ApplyPresentation() { if (!((Object)(object)_hud == (Object)null)) { Snapshot snapshot = _runtime?.Current; _hud.SetValues((snapshot == null) ? HudDisplayValues.Empty : new HudDisplayValues(_runtime.Elapsed, snapshot.Personal.Kills, snapshot.Personal.Gold, snapshot.Personal.Score, snapshot.BestUnits / 1000000, snapshot.ConnectedPlayers >= 2, snapshot.Team.Kills, snapshot.Team.Gold, snapshot.Team.Score)); _hud.SetScale(_scalePreview ?? _hudScale.Value); } } internal void PreviewScale(float? scale) { _scalePreview = scale; ApplyPresentation(); } private void RemoveHud() { if ((Object)(object)_hud != (Object)null) { _hud.FinishDrag(); Object.Destroy((Object)(object)((Component)_hud).gameObject); } _hud = null; _gameHud = null; } private void OnDestroy() { _runtime?.Dispose(); ArenaAdapter.Clear(); VanillaSettingsIntegration.Shutdown(); Harmony? patches = _patches; if (patches != null) { patches.UnpatchSelf(); } Instance = null; if (!_headless && _hudScale != null) { _hudScale.SettingChanged -= OnPresentationChanged; } RemoveHud(); } } } namespace BizareScoreHUD.UI { public readonly struct HudDisplayValues { public static HudDisplayValues Empty => default(HudDisplayValues); public TimeSpan Time { get; } public long Kills { get; } public long GoldEarned { get; } public long Score { get; } public long BestScore { get; } public bool ShowTeam { get; } public long TeamKills { get; } public long TeamGold { get; } public long TeamScore { get; } public HudDisplayValues(TimeSpan time, long kills, long goldEarned, long score, long bestScore, bool showTeam = false, long teamKills = 0L, long teamGold = 0L, long teamScore = 0L) { Time = time; Kills = kills; GoldEarned = goldEarned; Score = score; BestScore = bestScore; ShowTeam = showTeam; TeamKills = teamKills; TeamGold = teamGold; TeamScore = teamScore; } } public sealed class HudDragHandle : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IBeginDragHandler, IDragHandler, IEndDragHandler { private static readonly FieldRef<InventoryGui, ItemData> DragItem = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_dragItem"); private StatsHud _hud; private Vector2 _offset; private bool _dragging; internal static bool CanDrag { get { if (InventoryGui.IsVisible() && (Object)(object)InventoryGui.instance != (Object)null && DragItem.Invoke(InventoryGui.instance) == null && !InventoryGui.instance.m_splitDialog.IsActive && (Object)(object)Settings.instance == (Object)null && !Menu.IsVisible() && !Console.IsVisible() && !Minimap.IsOpen()) { if (!((Object)(object)TextViewer.instance == (Object)null)) { return !TextViewer.instance.IsVisible(); } return true; } return false; } } internal void Initialize(StatsHud hud) { _hud = hud; } public void OnPointerDown(PointerEventData data) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((int)data.button == 0) { Begin(data.position); } } public void OnBeginDrag(PointerEventData data) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((int)data.button == 0) { Begin(data.position); } } private void Begin(Vector2 screenPosition) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (!_dragging && CanDrag && _hud.PointerToParent(screenPosition, _hud.EventCamera, out var point)) { _offset = _hud.PanelPosition - point; _dragging = true; } } public void OnDrag(PointerEventData data) { //IL_001e: 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) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (_dragging) { Vector2 point; if (!CanDrag) { Finish(); } else if (_hud.PointerToParent(data.position, _hud.EventCamera, out point)) { _hud.MoveTo(point + _offset); } } } internal void UpdatePointer() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0054: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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) if (!CanDrag || !Application.isFocused) { Finish(); return; } Vector2 val = Vector2.op_Implicit(ZInput.pointerPosition); if (!_dragging && ZInput.GetMouseButtonDown(0) && _hud.ContainsDragPoint(val)) { Begin(val); } if (_dragging) { if (_hud.PointerToParent(val, _hud.EventCamera, out var point)) { _hud.MoveTo(point + _offset); } if (ZInput.GetMouseButtonUp(0) || !ZInput.GetMouseButton(0)) { Finish(); } } } public void OnEndDrag(PointerEventData data) { Finish(); } internal void Finish() { if (_dragging) { _dragging = false; _hud.SavePosition(); } } private void OnDisable() { Finish(); } } internal static class HudLocalization { internal static void Register() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "English"; localization.AddTranslation(ref text, new Dictionary<string, string> { { "bizarehud_time", "Time" }, { "bizarehud_kills", "Kills" }, { "bizarehud_gold", "Gold Earned" }, { "bizarehud_score", "Score" }, { "bizarehud_best", "Best score" }, { "bizarehud_personal", "Personal" }, { "bizarehud_team", "Team" }, { "bizarehud_drag", "Drag panel to move" }, { "bizarehud_scale", "Bizare HUD Scale" } }); text = "Russian"; localization.AddTranslation(ref text, new Dictionary<string, string> { { "bizarehud_time", "Время" }, { "bizarehud_kills", "Убийства" }, { "bizarehud_gold", "Заработано золота" }, { "bizarehud_score", "Очки" }, { "bizarehud_best", "Рекорд" }, { "bizarehud_personal", "Личная статистика" }, { "bizarehud_team", "Команда" }, { "bizarehud_drag", "Перетащите панель для перемещения" }, { "bizarehud_scale", "Масштаб HUD Bizare" } }); } internal static string Text(string key) { return Localization.instance.Localize("$bizarehud_" + key); } } public sealed class StatsHud : MonoBehaviour { private HudDisplayValues _values; private Text _dragLabel; private Text _time; private Text _kills; private Text _goldEarned; private Text _score; private Text _bestScore; private Text _personal; private Text _teamTitle; private Text _teamKills; private Text _teamGold; private Text _teamScore; private RectTransform _rect; private HudDragHandle _handle; private RectTransform _dragHeader; private Canvas _canvas; private Image _background; private bool _editable; private Vector2 _position = new Vector2(-1f, -1f); private Action<Vector2>? _savePosition; private const float HandleHeight = 24f; private const float HorizontalPadding = 12f; private const float VerticalPadding = 8f; private RectTransform ParentRect => (RectTransform)((Transform)_rect).parent; internal Vector2 PanelPosition => _rect.anchoredPosition; internal Camera EventCamera { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) Canvas rootCanvas = _canvas.rootCanvas; if ((int)rootCanvas.renderMode != 0) { return rootCanvas.worldCamera; } return null; } } public static StatsHud Create(Transform parent) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0086: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("BizareScoreHUD", new Type[6] { typeof(RectTransform), typeof(CanvasGroup), typeof(Canvas), typeof(GraphicRaycaster), typeof(CanvasRenderer), typeof(Image) }); val.transform.SetParent(parent, false); val.layer = 5; Image component = val.GetComponent<Image>(); ((Graphic)component).color = Color32.op_Implicit(new Color32((byte)36, (byte)36, (byte)36, (byte)180)); ((Graphic)component).raycastTarget = false; CanvasGroup component2 = val.GetComponent<CanvasGroup>(); component2.interactable = true; component2.blocksRaycasts = true; component2.ignoreParentGroups = true; Canvas component3 = val.GetComponent<Canvas>(); component3.overrideSorting = false; component3.sortingOrder = 2001; StatsHud statsHud = val.AddComponent<StatsHud>(); statsHud._rect = val.GetComponent<RectTransform>(); statsHud._canvas = component3; statsHud._background = component; RectTransform rect = statsHud._rect; RectTransform rect2 = statsHud._rect; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f, 1f); rect2.anchorMax = val2; rect.anchorMin = val2; statsHud._rect.pivot = new Vector2(0f, 1f); statsHud._rect.anchoredPosition = new Vector2(16f, -200f); statsHud._rect.sizeDelta = new Vector2(640f, 204f); statsHud._handle = val.AddComponent<HudDragHandle>(); statsHud._handle.Initialize(statsHud); Font builtinResource = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf"); statsHud._time = statsHud.CreateRow("Time", builtinResource, 24, Color.white, 0f, 34f); statsHud._kills = statsHud.CreateRow("Kills", builtinResource, 36, Color.red, 34f, 40f); statsHud._goldEarned = statsHud.CreateRow("GoldEarned", builtinResource, 36, Color32.op_Implicit(new Color32(byte.MaxValue, (byte)215, (byte)0, byte.MaxValue)), 74f, 42f); statsHud._score = statsHud.CreateRow("Score", builtinResource, 36, Color32.op_Implicit(new Color32(byte.MaxValue, (byte)165, (byte)0, byte.MaxValue)), 116f, 40f); statsHud._bestScore = statsHud.CreateRow("BestScore", builtinResource, 24, Color32.op_Implicit(new Color32(byte.MaxValue, (byte)165, (byte)0, byte.MaxValue)), 156f, 32f); statsHud._personal = statsHud.CreateRow("Personal", builtinResource, 24, Color.white, 0f, 34f); statsHud._teamTitle = statsHud.CreateRow("TeamTitle", builtinResource, 24, Color.white, 222f, 34f); statsHud._teamKills = statsHud.CreateRow("TeamKills", builtinResource, 36, Color.red, 256f, 40f); statsHud._teamGold = statsHud.CreateRow("TeamGold", builtinResource, 36, Color32.op_Implicit(new Color32(byte.MaxValue, (byte)215, (byte)0, byte.MaxValue)), 296f, 42f); statsHud._teamScore = statsHud.CreateRow("TeamScore", builtinResource, 36, Color32.op_Implicit(new Color32(byte.MaxValue, (byte)165, (byte)0, byte.MaxValue)), 338f, 40f); statsHud.CreateHandle(builtinResource); Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(statsHud.RefreshLocalization)); return statsHud; } public void SetValues(HudDisplayValues values) { //IL_02fd: Unknown result type (might be due to invalid IL or missing references) _values = values; SetText(_personal, HudLocalization.Text("personal")); SetText(_teamTitle, HudLocalization.Text("team")); SetText(_dragLabel, HudLocalization.Text("drag")); long num = values.Time.Ticks / 36000000000L; SetText(_time, string.Format(CultureInfo.InvariantCulture, HudLocalization.Text("time") + ": {0:00}:{1:00}:{2:00}", num, values.Time.Minutes, values.Time.Seconds)); SetText(_kills, HudLocalization.Text("kills") + ": " + values.Kills.ToString(CultureInfo.InvariantCulture)); SetText(_goldEarned, HudLocalization.Text("gold") + ": " + values.GoldEarned.ToString(CultureInfo.InvariantCulture)); SetText(_score, HudLocalization.Text("score") + ": " + values.Score.ToString(CultureInfo.InvariantCulture)); SetText(_bestScore, HudLocalization.Text("best") + ": " + values.BestScore.ToString(CultureInfo.InvariantCulture)); SetText(_teamKills, HudLocalization.Text("kills") + ": " + values.TeamKills.ToString(CultureInfo.InvariantCulture)); SetText(_teamGold, HudLocalization.Text("gold") + ": " + values.TeamGold.ToString(CultureInfo.InvariantCulture)); SetText(_teamScore, HudLocalization.Text("score") + ": " + values.TeamScore.ToString(CultureInfo.InvariantCulture)); Text[] array = (Text[])(object)new Text[5] { _personal, _teamTitle, _teamKills, _teamGold, _teamScore }; for (int i = 0; i < array.Length; i++) { ((Component)array[i]).gameObject.SetActive(values.ShowTeam); } Text[] array2 = (Text[])(object)new Text[5] { _time, _kills, _goldEarned, _score, _bestScore }; float[] array3 = new float[5] { 0f, 34f, 74f, 116f, 156f }; for (int j = 0; j < array2.Length; j++) { ((Graphic)array2[j]).rectTransform.anchoredPosition = new Vector2(12f, -8f - array3[j] - (values.ShowTeam ? 34f : 0f)); } _rect.SetSizeWithCurrentAnchors((Axis)1, (values.ShowTeam ? 378f : 188f) + 16f); float num2 = Mathf.Max(new float[5] { _time.preferredWidth, _kills.preferredWidth, _goldEarned.preferredWidth, _score.preferredWidth, _bestScore.preferredWidth }); if (values.ShowTeam) { num2 = Mathf.Max(new float[6] { num2, _personal.preferredWidth, _teamTitle.preferredWidth, _teamKills.preferredWidth, _teamGold.preferredWidth, _teamScore.preferredWidth }); } num2 = Mathf.Max(num2, _dragLabel.preferredWidth); _rect.SetSizeWithCurrentAnchors((Axis)0, Mathf.Max(280f, num2) + 24f); } private void RefreshLocalization() { SetValues(_values); } private void OnDestroy() { Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(RefreshLocalization)); } public void SetScale(float scale) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) ((Transform)_rect).localScale = Vector3.one * Mathf.Clamp(scale, 0.5f, 2f); } public void InitializePosition(Vector2 position, Action<Vector2> savePosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _position = position; _savePosition = savePosition; } internal bool ContainsDragPoint(Vector2 point) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!RectTransformUtility.RectangleContainsScreenPoint(_rect, point, EventCamera)) { return RectTransformUtility.RectangleContainsScreenPoint(_dragHeader, point, EventCamera); } return true; } internal bool PointerToParent(Vector2 screen, Camera camera, out Vector2 point) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) bool result = RectTransformUtility.ScreenPointToLocalPointInRectangle(ParentRect, screen, camera, ref point); Vector2 val = point; Rect rect = ParentRect.rect; float xMin = ((Rect)(ref rect)).xMin; rect = ParentRect.rect; point = val - new Vector2(xMin, ((Rect)(ref rect)).yMax); return result; } private void GetPositionBounds(out Vector2 minimum, out Vector2 maximum) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_008e: 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) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) Camera eventCamera = EventCamera; PointerToParent(Vector2.zero, eventCamera, out var point); PointerToParent(new Vector2((float)Screen.width, (float)Screen.height), eventCamera, out var point2); float x = ((Transform)_rect).localScale.x; float x2 = point.x; float y = point.y; Rect rect = _rect.rect; minimum = new Vector2(x2, y + ((Rect)(ref rect)).height * x); float x3 = point2.x; rect = _rect.rect; maximum = new Vector2(x3 - ((Rect)(ref rect)).width * x, point2.y - 24f * x); maximum.x = Mathf.Max(minimum.x, maximum.x); minimum.y = Mathf.Min(minimum.y, maximum.y); } public void UpdateLayout() { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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) //IL_00a0: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_handle == (Object)null)) { bool flag = ((Component)this).gameObject.activeInHierarchy && HudDragHandle.CanDrag; if (flag != _editable) { _editable = flag; _canvas.overrideSorting = flag; ((Graphic)_background).raycastTarget = flag; ((Component)_dragHeader).gameObject.SetActive(flag); } if (!flag) { _handle.Finish(); } GetPositionBounds(out var minimum, out var maximum); Vector2 val = ((_position.x < 0f || _position.y < 0f) ? new Vector2(16f, -200f) : new Vector2(Mathf.Lerp(minimum.x, maximum.x, _position.x), Mathf.Lerp(maximum.y, minimum.y, _position.y))); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(Mathf.Clamp(val.x, minimum.x, maximum.x), Mathf.Clamp(val.y, minimum.y, maximum.y)); if (_rect.anchoredPosition != val2) { _rect.anchoredPosition = val2; } if (flag) { _handle.UpdatePointer(); } } } internal void MoveTo(Vector2 position) { //IL_0010: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_0090: Unknown result type (might be due to invalid IL or missing references) GetPositionBounds(out var minimum, out var maximum); _rect.anchoredPosition = new Vector2(Mathf.Clamp(position.x, minimum.x, maximum.x), Mathf.Clamp(position.y, minimum.y, maximum.y)); _position = new Vector2(Mathf.InverseLerp(minimum.x, maximum.x, _rect.anchoredPosition.x), Mathf.InverseLerp(maximum.y, minimum.y, _rect.anchoredPosition.y)); } internal void SavePosition() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) _savePosition?.Invoke(_position); } public void FinishDrag() { _handle.Finish(); } private void CreateHandle(Font font) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_009f: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: 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) GameObject val = new GameObject("DragHeader", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); val.transform.SetParent(((Component)this).transform, false); val.layer = 5; RectTransform val2 = (_dragHeader = val.GetComponent<RectTransform>()); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = Vector2.one; val2.pivot = new Vector2(0f, 0f); val2.sizeDelta = new Vector2(0f, 24f); ((Graphic)val.GetComponent<Image>()).color = Color32.op_Implicit(new Color32((byte)36, (byte)36, (byte)36, (byte)220)); GameObject val3 = new GameObject("Label", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Text) }); val3.transform.SetParent(val.transform, false); val3.layer = 5; RectTransform component = val3.GetComponent<RectTransform>(); component.anchorMax = Vector2.one; component.sizeDelta = Vector2.zero; Text component2 = val3.GetComponent<Text>(); component2.font = font; component2.fontSize = 16; component2.alignment = (TextAnchor)4; ((Graphic)component2).color = Color.white; _dragLabel = component2; component2.text = HudLocalization.Text("drag"); ((Graphic)component2).raycastTarget = false; val.SetActive(false); } public void SetVisible(bool visible) { if (((Component)this).gameObject.activeSelf != visible) { ((Component)this).gameObject.SetActive(visible); } } private Text CreateRow(string name, Font font, int size, Color color, float top, float height) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Text) }); val.transform.SetParent(((Component)this).transform, false); val.layer = 5; RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = Vector2.one; component.pivot = new Vector2(0f, 1f); component.anchoredPosition = new Vector2(12f, 0f - top - 8f); component.sizeDelta = new Vector2(-24f, height); Text component2 = val.GetComponent<Text>(); component2.font = font; component2.fontSize = size; component2.fontStyle = (FontStyle)0; ((Graphic)component2).color = color; component2.alignment = (TextAnchor)0; component2.supportRichText = false; component2.resizeTextForBestFit = false; component2.horizontalOverflow = (HorizontalWrapMode)1; component2.verticalOverflow = (VerticalWrapMode)1; ((Graphic)component2).raycastTarget = false; return component2; } private static void SetText(Text label, string value) { if (label.text != value) { label.text = value; } } } public sealed class VanillaSettingsIntegration : MonoBehaviour { [HarmonyPatch(typeof(GraphicsSettings), "Initialize")] private static class InitializePatch { private static void Postfix(GraphicsSettings __instance) { Install(__instance); } } [HarmonyPatch(typeof(Settings), "ApplyAndClose")] private static class SavePatch { private static void Prefix() { Commit(); } } [HarmonyPatch(typeof(Settings), "OnBack")] private static class BackPatch { private static void Prefix() { ClosePreview(); } } private static VanillaSettingsIntegration? _active; private Slider _slider; private TMP_Text _value; private TMP_Text _label; private List<Slider> _navigation; private Plugin _plugin; private bool _finished; private bool _disposed; internal static void Install(GraphicsSettings graphics) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) Settings componentInParent = ((Component)graphics).GetComponentInParent<Settings>(); if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)(object)componentInParent == (Object)null) && !((Object)(object)((Component)componentInParent).GetComponent<VanillaSettingsIntegration>() != (Object)null)) { GameObject value = Traverse.Create((object)graphics).Field<GameObject>("m_qualitySliderPrefab").Value; GameObject val = Object.Instantiate<GameObject>(value, value.transform.parent); ((Object)val).name = "BizareHudScale"; VanillaSettingsIntegration vanillaSettingsIntegration = (_active = ((Component)componentInParent).gameObject.AddComponent<VanillaSettingsIntegration>()); vanillaSettingsIntegration._plugin = Plugin.Instance; vanillaSettingsIntegration._slider = val.GetComponent<Slider>(); vanillaSettingsIntegration._slider.onValueChanged = new SliderEvent(); vanillaSettingsIntegration._slider.minValue = 50f; vanillaSettingsIntegration._slider.maxValue = 200f; vanillaSettingsIntegration._slider.wholeNumbers = true; vanillaSettingsIntegration._label = ((Component)val.transform.Find("Label")).GetComponent<TMP_Text>(); vanillaSettingsIntegration.RefreshLocalization(); Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(vanillaSettingsIntegration.RefreshLocalization)); vanillaSettingsIntegration._value = ((Component)val.transform.Find("Info/Value")).GetComponent<TMP_Text>(); ((Component)val.transform.Find("Info/Warning")).gameObject.SetActive(false); vanillaSettingsIntegration._navigation = Traverse.Create((object)graphics).Field<List<Slider>>("m_dynamicQualitySliders").Value; int count = vanillaSettingsIntegration._navigation.Count; val.transform.SetSiblingIndex((count > 0) ? (((Component)vanillaSettingsIntegration._navigation[count - 1]).transform.GetSiblingIndex() + 1) : (value.transform.GetSiblingIndex() + 1)); vanillaSettingsIntegration._navigation.Add(vanillaSettingsIntegration._slider); vanillaSettingsIntegration.SyncConfig(null, EventArgs.Empty); ((UnityEvent<float>)(object)vanillaSettingsIntegration._slider.onValueChanged).AddListener((UnityAction<float>)vanillaSettingsIntegration.OnScaleChanged); vanillaSettingsIntegration._plugin.HudScale.SettingChanged += vanillaSettingsIntegration.SyncConfig; val.SetActive(true); RectTransform value2 = Traverse.Create((object)graphics).Field<RectTransform>("m_listRoot").Value; VerticalLayoutGroup value3 = Traverse.Create((object)graphics).Field<VerticalLayoutGroup>("m_verticalLayoutGroup").Value; Rect rect = value2.rect; float height = ((Rect)(ref rect)).height; rect = ((RectTransform)val.transform).rect; value2.SetSizeWithCurrentAnchors((Axis)1, height + ((Rect)(ref rect)).height + ((HorizontalOrVerticalLayoutGroup)value3).spacing); LayoutRebuilder.MarkLayoutForRebuild(value2); } } private void RefreshLocalization() { _label.text = HudLocalization.Text("scale"); } private void SyncConfig(object sender, EventArgs args) { _slider.SetValueWithoutNotify(_plugin.HudScale.Value * 100f); _value.text = Mathf.RoundToInt(_slider.value) + "%"; _plugin.PreviewScale(null); } private void OnScaleChanged(float percent) { _value.text = Mathf.RoundToInt(percent) + "%"; _plugin.PreviewScale(percent / 100f); } internal static void Commit() { if (!((Object)(object)_active == (Object)null) && !_active._finished) { VanillaSettingsIntegration active = _active; active._finished = true; active._plugin.HudScale.Value = active._slider.value / 100f; active._plugin.PreviewScale(null); } } internal static void ClosePreview() { if (!((Object)(object)_active == (Object)null)) { _active._plugin.PreviewScale(null); } } internal static void Shutdown() { if (!((Object)(object)_active == (Object)null)) { VanillaSettingsIntegration? active = _active; active.Cleanup(); Object.Destroy((Object)(object)active); } } private void OnDestroy() { Cleanup(); } private void Cleanup() { if (!_disposed) { _disposed = true; Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(RefreshLocalization)); if ((Object)(object)_slider != (Object)null) { ((UnityEvent<float>)(object)_slider.onValueChanged).RemoveListener((UnityAction<float>)OnScaleChanged); _navigation?.Remove(_slider); } if ((Object)(object)_plugin != (Object)null) { _plugin.HudScale.SettingChanged -= SyncConfig; _plugin.PreviewScale(null); } if ((Object)(object)_active == (Object)(object)this) { _active = null; } if ((Object)(object)_slider != (Object)null) { Object.Destroy((Object)(object)((Component)_slider).gameObject); } } } } } namespace BizareScoreHUD.Tracking { public static class InventoryTracking { private sealed class Before { public int Count; public List<LotRange> Ranges = new List<LotRange>(); } private sealed class Operation { private readonly Dictionary<ItemData, Before> _before = new Dictionary<ItemData, Before>(); private readonly HashSet<Inventory> _inventories = new HashSet<Inventory>(); private readonly Dictionary<ItemData, ItemDrop?> _loose = new Dictionary<ItemData, ItemDrop>(); public readonly List<ItemDrop> Outputs = new List<ItemDrop>(); private readonly bool _reward = RewardDepth > 0; public void Add(Inventory inventory) { if (!_inventories.Add(inventory)) { return; } bool fresh = inventory != Local; foreach (ItemData item in inventory.GetAllItems().Where(Currency)) { Capture(item, fresh); } if (inventory == Local) { Audited.Add(inventory); } } public unsafe void AddLoose(ItemData item, ItemDrop? drop) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (!_inventories.Any((Inventory i) => i.ContainsItem(item))) { if (!_loose.ContainsKey(item) || (Object)(object)drop != (Object)null) { _loose[item] = drop; } string stable = null; ZNetView val = (((Object)(object)drop == (Object)null) ? null : ((Component)drop).GetComponent<ZNetView>()); if ((Object)(object)val != (Object)null && val.IsValid()) { string obj = StatsRuntime.Instance?.WorldId; ZDOID uid = val.GetZDO().m_uid; stable = "drop:" + obj + ":" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); } Capture(item, (Object)(object)drop != (Object)null || _reward, stable); } } private void Capture(ItemData item, bool fresh, string? stable = null) { if (!_before.ContainsKey(item)) { _before.Add(item, new Before { Count = item.m_stack, Ranges = Provenance.Copy(Read(item, fresh, stable)) }); } } public void Finish() { HashSet<ItemData> hashSet = new HashSet<ItemData>(); foreach (Inventory inventory in _inventories) { foreach (ItemData item in inventory.GetAllItems().Where(Currency)) { hashSet.Add(item); } } foreach (KeyValuePair<ItemData, ItemDrop> item2 in _loose) { if (item2.Value != null && (Object)(object)item2.Value != (Object)null && (Object)(object)((Component)item2.Value).GetComponent<ZNetView>() != (Object)null && ((Component)item2.Value).GetComponent<ZNetView>().IsValid()) { hashSet.Add(item2.Key); } } foreach (ItemDrop output in Outputs) { if ((Object)(object)output != (Object)null) { hashSet.Add(output.m_itemData); } } Dictionary<ItemData, List<LotRange>> dictionary = new Dictionary<ItemData, List<LotRange>>(); List<LotRange> list = new List<LotRange>(); foreach (KeyValuePair<ItemData, Before> item3 in _before) { List<LotRange> list2 = Provenance.Copy(item3.Value.Ranges); long quantity = (hashSet.Contains(item3.Key) ? Math.Min(item3.Value.Count, item3.Key.m_stack) : 0); dictionary[item3.Key] = Provenance.Take(list2, quantity); list.AddRange(list2); } foreach (ItemData item4 in hashSet) { if (!dictionary.TryGetValue(item4, out var value)) { value = (dictionary[item4] = new List<LotRange>()); } long num = item4.m_stack - value.Sum((LotRange r) => r.Count); long val = list.Sum((LotRange r) => r.Count); long num2 = Math.Min(num, val); value.AddRange(Provenance.Take(list, num2)); num -= num2; if (num > 0) { if (!_reward) { StatsRuntime.Instance?.Warn("Unclassified positive currency mutation held neutral: " + num); } LotRange lotRange = Provenance.Create(num, _reward, StatsRuntime.Instance?.RunId ?? ""); lotRange.World = StatsRuntime.Instance?.WorldId ?? ""; value.Add(lotRange); } Write(item4, value); } if (Local != null && _inventories.Contains(Local)) { Credit(Local); } foreach (ItemDrop item5 in _loose.Values.Concat<ItemDrop>(Outputs)) { if ((Object)(object)item5 != (Object)null) { SaveDrop?.Invoke(item5, null); } } } } private const string Key = "com.igentuman.bizarehud.lots.v1"; private static Operation? _operation; private static int _depth; public static int Loading; public static int RewardDepth; private static readonly HashSet<Inventory> Audited = new HashSet<Inventory>(); private static readonly MethodInfo? SaveDrop = AccessTools.Method(typeof(ItemDrop), "Save", (Type[])null, (Type[])null); private static readonly FieldInfo? DropInstances = AccessTools.Field(typeof(ItemDrop), "s_instances"); private static Inventory? Local { get { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { return ((Humanoid)Player.m_localPlayer).GetInventory(); } return null; } } public static bool Currency(ItemData item) { if ((Object)(object)item.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name == "Coins"; } return false; } public static void Clear() { _operation = null; _depth = (Loading = (RewardDepth = 0)); Audited.Clear(); } public static void Audit(Inventory inventory) { if (_operation != null || Loading > 0) { return; } foreach (ItemData item in inventory.GetAllItems().Where(Currency)) { Read(item, fresh: false); } Audited.Add(inventory); if (StatsRuntime.Instance == null || StatsRuntime.Instance.RunId.Length <= 0) { return; } try { Credit(inventory); } catch (Exception ex) { StatsRuntime.Instance.Warn("Pending carrier acquisition deferred: " + ex.Message); } } private static List<LotRange> Read(ItemData item, bool fresh, string? stableLot = null) { if (item.m_customData.TryGetValue("com.igentuman.bizarehud.lots.v1", out var value)) { try { List<LotRange> list = Json.Read<List<LotRange>>(value); if (list.Count <= 256 && list.All((LotRange r) => r.Count > 0 && r.Start >= 0 && r.Start <= long.MaxValue - r.Count) && list.Sum((LotRange r) => r.Count) == item.m_stack) { string world = StatsRuntime.Instance?.WorldId ?? ""; if (world.Length > 0 && list.Any((LotRange r) => r.World.Length > 0 && r.World != world)) { foreach (LotRange item2 in list) { item2.Fresh = false; } Write(item, list); } return list; } } catch (Exception ex) when (ex is JsonException || ex is OverflowException || ex is InvalidOperationException) { } StatsRuntime.Instance?.Warn("Invalid currency provenance quarantined as neutral"); fresh = false; } List<LotRange> list2 = new List<LotRange>(); if (item.m_stack > 0) { LotRange lotRange = Provenance.Create(item.m_stack, fresh, StatsRuntime.Instance?.RunId ?? ""); lotRange.World = StatsRuntime.Instance?.WorldId ?? ""; if (stableLot != null) { lotRange.Lot = stableLot; } list2.Add(lotRange); } Write(item, list2); return list2; } private static void Write(ItemData item, List<LotRange> ranges) { string text = Json.Write(ranges); if (!item.m_customData.TryGetValue("com.igentuman.bizarehud.lots.v1", out var value) || value != text) { item.m_customData["com.igentuman.bizarehud.lots.v1"] = text; } } public static void Begin(Inventory? inventory, object[] args) { if (Loading > 0 || StatsRuntime.Instance == null || StatsRuntime.Instance.RunId.Length == 0) { return; } if (_operation == null) { _operation = new Operation(); } _depth++; if (inventory != null) { _operation.Add(inventory); } foreach (object obj in args) { Inventory val = (Inventory)((obj is Inventory) ? obj : null); if (val != null) { _operation.Add(val); } ItemData val2 = (ItemData)((obj is ItemData) ? obj : null); if (val2 != null && Currency(val2)) { _operation.AddLoose(val2, null); } } } public static void Pickup(Humanoid humanoid, GameObject go) { ItemDrop val = (((Object)(object)go == (Object)null) ? null : go.GetComponent<ItemDrop>()); if ((Object)(object)val != (Object)null) { val.Load(); } Begin(humanoid.GetInventory(), Array.Empty<object>()); if (_operation != null && (Object)(object)val != (Object)null && Currency(val.m_itemData)) { _operation.AddLoose(val.m_itemData, val); } } public static void GroundStack(ItemDrop receiver) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) Begin(null, Array.Empty<object>()); if (_operation == null || !Currency(receiver.m_itemData)) { return; } ItemDrop[] array = ((DropInstances?.GetValue(null) as IEnumerable<ItemDrop>) ?? Array.Empty<ItemDrop>()).ToArray(); foreach (ItemDrop val in array) { if (!((Object)(object)val == (Object)null) && Currency(val.m_itemData) && !(Vector3.Distance(((Component)receiver).transform.position, ((Component)val).transform.position) > 4f)) { ZNetView component = ((Component)val).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner()) { val.Load(); _operation.AddLoose(val.m_itemData, val); } } } } public static void Output(ItemDrop drop) { if (_operation != null && (Object)(object)drop != (Object)null && Currency(drop.m_itemData)) { _operation.Outputs.Add(drop); } } public static void End() { if (_operation == null || _depth == 0 || --_depth != 0) { return; } Operation operation = _operation; _operation = null; try { operation.Finish(); } catch (Exception ex) { StatsRuntime.Instance?.Warn("Currency reconciliation deferred: " + ex); } } private static void Credit(Inventory inventory) { foreach (ItemData item in inventory.GetAllItems().Where(Currency)) { List<LotRange> list = Read(item, fresh: false); foreach (IGrouping<string, LotRange> item2 in from r in list where r.Fresh && r.Run.Length > 0 group r by r.Run) { if (!StatsRuntime.Instance.Submit(new Report { Kind = "gold", Run = item2.Key, Ranges = Provenance.Copy(item2) })) { throw new InvalidOperationException("Acquisition remains unacknowledged in its carrier"); } } foreach (LotRange item3 in list) { item3.Fresh = false; } Write(item, list); } } } public sealed class RewardScope : IDisposable { private bool _disposed; public RewardScope(Inventory inventory) { InventoryTracking.RewardDepth++; InventoryTracking.Begin(inventory, Array.Empty<object>()); } public void Dispose() { if (_disposed) { return; } _disposed = true; try { InventoryTracking.End(); } finally { InventoryTracking.RewardDepth--; } } } } namespace BizareScoreHUD.Persistence { public static class Json { public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings { ContractResolver = (IContractResolver)new DefaultContractResolver { NamingStrategy = (NamingStrategy)new CamelCaseNamingStrategy { ProcessDictionaryKeys = false, OverrideSpecifiedNames = true } }, MaxDepth = 64, TypeNameHandling = (TypeNameHandling)0 }; public static string Write(object value) { return JsonConvert.SerializeObject(value, Settings); } public static T Read<T>(string value) { T val = JsonConvert.DeserializeObject<T>(value, Settings); if (val == null) { throw new InvalidDataException("Empty JSON"); } return val; } public static T Clone<T>(T value) { return Read<T>(Write(value)); } } public sealed class DeathsConverter : JsonConverter<Dictionary<string, string>> { public override Dictionary<string, string> ReadJson(JsonReader reader, Type objectType, Dictionary<string, string>? existingValue, bool hasExistingValue, JsonSerializer serializer) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 Dictionary<string, string> dictionary = existingValue ?? new Dictionary<string, string>(); if ((int)reader.TokenType == 11) { return dictionary; } if ((int)reader.TokenType == 2) { foreach (JToken item in JArray.Load(reader)) { dictionary[((string)item) ?? throw new InvalidDataException("Invalid death record")] = ""; } return dictionary; } foreach (JProperty item2 in JObject.Load(reader).Properties()) { dictionary[item2.Name] = ((string)item2.Value) ?? ""; } return dictionary; } public override void WriteJson(JsonWriter writer, Dictionary<string, string>? value, JsonSerializer serializer) { writer.WriteStartObject(); if (value != null) { foreach (KeyValuePair<string, string> item in value) { writer.WritePropertyName(item.Key); writer.WriteValue(item.Value); } } writer.WriteEndObject(); } } public sealed class JournalRecord { public long Revision; public bool Delta; public string State = ""; public string Hash = ""; } public sealed class JournalChange { public string[] Path = Array.Empty<string>(); public bool Remove; public JToken? Value; } public sealed class BestFile { public int SchemaVersion = 1; public Dictionary<string, long> Checkpoints = new Dictionary<string, long>(); public List<BestRecord> Players = new List<BestRecord>(); } public sealed class StateRepository { public static bool SyncWrites; private readonly string _root; private readonly string _directory; private readonly string _journal; private JObject? _lastPersisted; public StateRepository(string root, string world) { _root = root; _directory = Path.Combine(root, world); Directory.CreateDirectory(_directory); _journal = Path.Combine(_directory, "journal.jsonl"); } private static string Hash(string value) { using SHA256 sHA = SHA256.Create(); return Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(value))); } private static T? LoadWithBackup<T>(string path) where T : class { if (!File.Exists(path) && !File.Exists(path + ".bak")) { return null; } string[] array = new string[2] { path, path + ".bak" }; foreach (string path2 in array) { if (!File.Exists(path2)) { continue; } try { string text = File.ReadAllText(path2); if (typeof(T) == typeof(BestFile)) { ValidateBests(text); } return Json.Read<T>(text); } catch (JsonException) { } catch (InvalidDataException) { } } throw new InvalidDataException("Cannot recover " + path); } private static void ValidateBests(string json) { JObject obj = JObject.Parse(json); if ((int?)obj["schemaVersion"] > 1) { throw new NotSupportedException("Unsupported newer best-score schema"); } if ((int?)obj["schemaVersion"] != 1) { throw new InvalidDataException("Invalid best-score schema"); } JToken obj2 = obj["players"]; JToken obj3 = ((obj2 is JArray) ? obj2 : null) ?? throw new InvalidDataException("Missing best-score records"); HashSet<string> hashSet = new HashSet<string>(); foreach (JToken item in (JArray)obj3) { string text = (string)item[(object)"participantId"]; long? num = (long?)item[(object)"scoreUnits"]; long? num2 = (long?)item[(object)"score"]; if (string.IsNullOrEmpty(text) || !hashSet.Add(text) || !num.HasValue || num < 0 || num2 != num / 1000000) { throw new InvalidDataException("Inconsistent best-score record"); } } } private WorldState? Replay(string journal) { JObject document; return Replay(journal, out document); } private WorldState? Replay(string journal, out JObject? document) { document = null; if (!File.Exists(journal)) { return null; } string text = File.ReadAllText(journal); int num = text.LastIndexOf('\n'); if (num < text.Length - 1) { using FileStream fileStream = new FileStream(journal, FileMode.Open, FileAccess.Write, FileShare.Read); fileStream.SetLength(Encoding.UTF8.GetByteCount(text.Substring(0, num + 1))); fileStream.Flush(SyncWrites); text = text.Substring(0, num + 1); } JObject val = null; long num2 = 0L; string[] array = text.Split('\n'); foreach (string text2 in array) { if (text2.Length == 0) { continue; } JournalRecord journalRecord = Json.Read<JournalRecord>(text2); if (journalRecord.Hash != Hash(journalRecord.State)) { throw new InvalidDataException("Journal checksum mismatch"); } if (val != null && journalRecord.Revision <= num2) { throw new InvalidDataException("Unordered journal"); } if (journalRecord.Delta) { if (val == null) { throw new InvalidDataException("Journal delta without initial state"); } foreach (JournalChange item in Json.Read<List<JournalChange>>(journalRecord.State)) { Apply(val, item); } } else { val = JObject.Parse(journalRecord.State); } if ((int?)val["schemaVersion"] != 1 || (long?)val["revision"] != journalRecord.Revision) { throw new InvalidDataException("Unsupported journal schema/revision"); } num2 = journalRecord.Revision; } document = val; if (val != null) { return Json.Read<WorldState>(((JToken)val).ToString((Formatting)0, Array.Empty<JsonConverter>())); } return null; } private static void Apply(JObject root, JournalChange change) { if (change.Path.Length == 0) { throw new InvalidDataException("Invalid journal path"); } JObject val = root; foreach (string item in change.Path.Take(change.Path.Length - 1)) { JToken obj = val[item]; val = (JObject)(object)(((obj is JObject) ? obj : null) ?? throw new InvalidDataException("Invalid journal parent")); } string text = change.Path[change.Path.Length - 1]; if (change.Remove) { val.Remove(text); return; } JObject obj2 = val; JToken? value = change.Value; obj2[text] = (JToken)(((object)((value != null) ? value.DeepClone() : null)) ?? ((object)JValue.CreateNull())); } private static void Diff(JObject before, JObject after, List<string> path, List<JournalChange> changes) { foreach (JProperty item in before.Properties()) { if (after.Property(item.Name) == null) { changes.Add(new JournalChange { Path = path.Concat(new string[1] { item.Name }).ToArray(), Remove = true }); } } foreach (JProperty item2 in after.Properties()) { List<string> list = path.Concat(new string[1] { item2.Name }).ToList(); JProperty val = before.Property(item2.Name); if (val != null && JToken.DeepEquals(val.Value, item2.Value)) { continue; } JToken obj = ((val != null) ? val.Value : null); JObject val2 = (JObject)(object)((obj is JObject) ? obj : null); if (val2 != null) { JToken value = item2.Value; JObject val3 = (JObject)(object)((value is JObject) ? value : null); if (val3 != null) { Diff(val2, val3, list, changes); continue; } } changes.Add(new JournalChange { Path = list.ToArray(), Value = item2.Value.DeepClone() }); } } public WorldState Load(string world, ArenaDifficulty difficulty) { JObject document; WorldState worldState = Replay(_journal, out document) ?? LoadWithBackup<WorldState>(Path.Combine(_directory, "state.json")); _lastPersisted = document; if (worldState == null) { worldState = new WorldState { World = world }; worldState.Start(difficulty); } if (worldState.SchemaVersion != 1 || worldState.World != world || !worldState.Runs.ContainsKey(worldState.CurrentRun)) { throw new InvalidDataException("Invalid world state"); } RestoreParticipantKeys(worldState); BestFile bestFile; string[] files; try { bestFile = LoadWithBackup<BestFile>(Path.Combine(_root, "best_scores.json")) ?? new BestFile(); } catch (InvalidDataException) { bestFile = new BestFile(); files = Directory.GetFiles(_root, "journal.jsonl", SearchOption.AllDirectories); foreach (string journal in files) { WorldState worldState2 = Replay(journal); if (worldState2 != null) { Merge(bestFile, worldState2); } } if (bestFile.Checkpoints.Count == 0) { throw; } } if (bestFile.SchemaVersion != 1) { throw new InvalidDataException("Unsupported best-score schema"); } files = Directory.GetFiles(_root, "journal.jsonl", SearchOption.AllDirectories); foreach (string journal2 in files) { WorldState worldState3 = Replay(journal2); if (worldState3 != null) { Merge(bestFile, worldState3); } } foreach (BestRecord player in bestFile.Players) { if (!worldState.Bests.TryGetValue(player.ParticipantId, out BestRecord value) || player.ScoreUnits > value.ScoreUnits) { worldState.Bests[player.ParticipantId] = player; } } AtomicWrite(Path.Combine(_root, "best_scores.json"), JsonConvert.SerializeObject((object)bestFile, (Formatting)1, Json.Settings)); return worldState; } private static void Merge(BestFile file, WorldState state) { RestoreParticipantKeys(state); foreach (BestRecord best in state.Bests.Values) { BestRecord bestRecord = file.Players.Find((BestRecord p) => p.ParticipantId == best.ParticipantId); Participant value; if (bestRecord == null) { file.Players.Add(best); } else if (best.ScoreUnits > bestRecord.ScoreUnits) { file.Players.Remove(bestRecord); file.Players.Add(best); } else if (state.Participants.TryGetValue(best.ParticipantId, out value)) { bestRecord.Nickname = value.Nickname; } } file.Checkpoints[state.World] = state.Revision; } private static void RestoreParticipantKeys(WorldState state) { KeyValuePair<string, Participant>[] array = state.Participants.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<string, Participant> keyValuePair = array[i]; string id = keyValuePair.Value.Id; if (keyValuePair.Key == id) { continue; } if (id.Length == 0 || state.Participants.ContainsKey(id)) { throw new InvalidDataException("Conflicting participant identity in saved state"); } state.Participants.Remove(keyValuePair.Key); state.Participants.Add(id, keyValuePair.Value); foreach (Run value2 in state.Runs.Values) { if (value2.Players.TryGetValue(keyValuePair.Key, out Counters value)) { if (value2.Players.ContainsKey(id)) { throw new InvalidDataException("Conflicting participant counters in saved state"); } value2.Players.Remove(keyValuePair.Key); value2.Players.Add(id, value); } } } KeyValuePair<string, BestRecord>[] array2 = state.Bests.ToArray(); for (int i = 0; i < array2.Length; i++) { KeyValuePair<string, BestRecord> keyValuePair2 = array2[i]; string participantId = keyValuePair2.Value.ParticipantId; if (!(keyValuePair2.Key == participantId)) { if (participantId.Length == 0 || state.Bests.ContainsKey(participantId)) { throw new InvalidDataException("Conflicting best-score identity in saved state"); } state.Bests.Remove(keyValuePair2.Key); state.Bests.Add(participantId, keyValuePair2.Value); } } } public void Append(WorldState state) { JObject val = JObject.Parse(Json.Write(state)); bool flag = _lastPersisted != null; string text; if (flag) { List<JournalChange> list = new List<JournalChange>(); Diff(_lastPersisted, val, new List<string>(), list); text = Json.Write(list); } else { text = ((JToken)val).ToString((Formatting)0, Array.Empty<JsonConverter>()); } byte[] bytes = Encoding.UTF8.GetBytes(Json.Write(new JournalRecord { Revision = state.Revision, Delta = flag, State = text, Hash = Hash(text) }) + "\n"); using FileStream fileStream = new FileStream(_journal, FileMode.Append, FileAccess.Write, FileShare.Read); fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(SyncWrites); _lastPersisted = val; } public void Checkpoint(WorldState state) { AtomicWrite(Path.Combine(_directory, "state.json"), Json.Write(state)); BestFile bestFile = LoadWithBackup<BestFile>(Path.Combine(_root, "best_scores.json")) ?? new BestFile(); Merge(bestFile, state); AtomicWrite(Path.Combine(_root, "best_scores.json"), JsonConvert.SerializeObject((object)bestFile, (Formatting)1, Json.Settings)); } public static void AtomicWrite(string path, string data) { Directory.CreateDirectory(Path.GetDirectoryName(path)); string text = path + ".tmp"; using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None)) { byte[] bytes = Encoding.UTF8.GetBytes(data); fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(SyncWrites); } if (File.Exists(path)) { File.Replace(text, path, path + ".bak"); } else { File.Move(text, path); } } } } namespace BizareScoreHUD.Patches { [HarmonyPatch] internal static class InventoryMutationPatch { private static IEnumerable<MethodBase> TargetMethods() { return from m in typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where new string[7] { "AddItem", "RemoveItem", "RemoveOneItem", "MoveAll", "MoveItemToThis", "StackAll", "MoveInventoryToGrave" }.Contains(m.Name) select m; } private static void Prefix(Inventory __instance, object[] __args, out bool __state) { __state = InventoryTracking.Loading == 0; if (__state) { InventoryTracking.Begin(__instance, __args); } } private static Exception? Finalizer(Exception? __exception, bool __state) { if (__state) { InventoryTracking.End(); } return __exception; } } [HarmonyPatch] internal static class InventoryLoadPatch { private static IEnumerable<MethodBase> TargetMethods() { return from m in typeof(Inventory).GetMethods() where m.Name == "Load" select m; } private static void Prefix() { InventoryTracking.Loading++; } private static Exception? Finalizer(Exception? __exception) { InventoryTracking.Loading--; return __exception; } } [HarmonyPatch(typeof(Humanoid), "Pickup")] internal static class PickupPatch { private static void Prefix(Humanoid __instance, GameObject go) { InventoryTracking.Pickup(__instance, go); } private static Exception? Finalizer(Exception? __exception) { InventoryTracking.End(); return __exception; } } [HarmonyPatch(typeof(Humanoid), "DropItem")] internal static class DropPatch { private static void Prefix(Humanoid __instance, Inventory inventory, ItemData item) { InventoryTracking.Begin(inventory ?? __instance.GetInventory(), new object[1] { item }); } private static Exception? Finalizer(Exception? __exception) { InventoryTracking.End(); return __exception; } } [HarmonyPatch(typeof(ItemDrop), "DropItem")] internal static class DropOutputPatch { private static void Postfix(ItemDrop __result) { InventoryTracking.Output(__result); } } [HarmonyPatch(typeof(ItemDrop), "AutoStackItems")] internal static class GroundStackPatch { private static void Prefix(ItemDrop __instance) { InventoryTracking.GroundStack(__instance); } private static Exception? Finalizer(Exception? __exception) { InventoryTracking.End(); return __exception; } } [HarmonyPatch(typeof(StoreGui), "SellItem")] internal static class SalePatch { internal static void Prefix() { InventoryTracking.RewardDepth++; InventoryTracking.Begin(((Object)(object)Player.m_localPlayer == (Object)null) ? null : ((Humanoid)Player.m_localPlayer).GetInventory(), Array.Empty<object>()); } internal static Exception? Finalizer(Exception? __exception) { try { InventoryTracking.End(); return __exception; } finally { InventoryTracking.RewardDepth--; } } } [HarmonyPatch(typeof(Character), "ApplyDamage")] internal static class DamagePatch { private static void Prefix(Character __instance, HitData hit) { if (__instance.IsPlayer() || __instance.IsTamed() || __instance.IsDead() || hit.GetTotalDamage() <= 0.1f) { return; } Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (val == null || !BaseAI.IsEnemy(__instance, (Character)(object)val)) { return; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner()) { ZDO zDO = component.GetZDO(); long playerID = val.GetPlayerID(); long ticks = ZNet.instance.GetTime().Ticks; if (zDO.GetLong(ZdoKeys.Attacker, 0L) != playerID || ticks - zDO.GetLong(ZdoKeys.HitTime, 0L) >= 10000000) { zDO.Set(ZdoKeys.Attacker, playerID); zDO.Set(ZdoKeys.HitTime, ticks); } } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class DeathPatch { private static void Prefix(Character __instance) { if (__instance.IsPlayer() || __instance.IsTamed()) { return; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); StatsRuntime instance = StatsRuntime.Instance; if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || instance == null) { return; } ZDO zDO = component.GetZDO(); string text = zDO.GetString(ZdoKeys.Wave, ""); long num = zDO.GetLong(ZdoKeys.Attacker, 0L); long num2 = zDO.GetLong(ZdoKeys.HitTime, 0L); if (num != 0L && ZNet.instance.GetTime().Ticks - num2 <= 300000000) { string text2 = zDO.GetString(ZdoKeys.Run, ""); if (text.Length == 0 || text2.Length == 0) { instance.Warn("Unresolved arena kill: " + ((Object)__instance).name); return; } instance.Submit(new Report { Kind = "kill", Run = text2, Victim = ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString(), Wave = text, Killer = num, Level = __instance.GetLevel(), Boss = __instance.IsBoss() }); } } } [HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")] internal static class PreserveVictimPatch { private static void Prefix(ZDOMan __instance, ZDOID uid) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = __instance.GetZDO(uid); if (zDO != null) { StatsRuntime.Instance?.PreserveVictim(zDO); } } } } namespace BizareScoreHUD.Networking { public sealed class Report { public int Protocol = 1; public string Id = ""; public string World = ""; public string Kind = ""; public string Run = ""; public string Victim = ""; public string Wave = ""; public long Killer; public int Level; public bool Boss; public List<LotRange> Ranges = new List<LotRange>(); } public sealed class Snapshot { public int Protocol = 1; public long Revision; public string World = ""; public string Run = ""; public long StartedUtcTicks; public long ServerUtcTicks; public string Ack = ""; public string Error = ""; public Counters Personal = new Counters(); public Counters Team = new Counters(); public long BestUnits; public int ConnectedPlayers; public List<ArenaEvent> Events = new List<ArenaEvent>(); public List<Wave> EventWaves = new List<Wave>(); } public sealed class StatsRuntime : IDisposable { private const string ReportRpc = "BizareHUD.Report.v1"; private const string SnapshotRpc = "BizareHUD.Snapshot.v1"; private readonly ManualLogSource _log; private readonly ConfigEntry<ArenaDifficulty> _difficulty; private readonly string _root; private ZRoutedRpc? _rpc; private ZNet? _net; private StateRepository? _repository; private WorldState? _state; private bool _failed; private float _nextPoll; private float _nextCheckpoint; private readonly Dictionary<long, string> _bindings = new Dictionary<long, string>(); private readonly Dictionary<long, float> _lastRequest = new Dictionary<long, float>(); private readonly List<Report> _pending = new List<Report>(); private readonly Dictionary<string, int> _attempts = new Dictionary<string, int>(); private readonly Dictionary<string, float> _retryAt = new Dictionary<string, float>(); private string? _pendingPath; private long _pendingCharacter; private float _receivedAt; private bool _journalDirty; private bool _pendingDirty; private bool _publishDirty; private long _checkpointed = -1L; public static StatsRuntime? Instance { get; private set; } public Snapshot? Current { get; private set; } public WorldState? State => _state; public bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public string RunId { get { object obj; if (!_failed) { if (!IsServer) { obj = Current?.Run; if (obj == null) { return ""; } } else { obj = _state?.CurrentRun; if (obj == null) { return ""; } } } else { obj = ""; } return (string)obj; } } public string WorldId { get { object obj; if (IsServer) { obj = _state?.World ?? ""; } else { obj = Current?.World; if (obj == null) { return ""; } } return (string)obj; } } private int Connected => _net.GetPeers().Count((ZNetPeer p) => p.IsReady() && p.m_playerID != 0) + (((Object)(object)Player.m_localPlayer != (Object)null) ? 1 : 0); public TimeSpan Elapsed { get { if (Current != null) { return TimeSpan.FromSeconds(Math.Max(0.0, (double)(Current.ServerUtcTicks - Current.StartedUtcTicks) / 10000000.0 + (double)Time.unscaledTime - (double)_receivedAt)); } return TimeSpan.Zero; } } public StatsRuntime(ConfigFile config, ManualLogSource log) { Instance = this; _log = log; _difficulty = config.Bind<ArenaDifficulty>("Server", "Difficulty", ArenaDifficulty.Normal, "Server-owned scoring difficulty. Changes apply at the next accepted arena reset."); _root = config.Bind<string>("Server", "DataDirectory", Path.Combine(Paths.ConfigPath, "BizareScoreHUD", "data"), "Server statistics and shared best scores directory.").Value; StateRepository.SyncWrites = config.Bind<bool>("Server", "SyncWrites", false, "Force every statistics write to disk (fsync). Protects against power loss, but each kill or sale then stalls on slow drives.").Value; } public void Warn(string message) { _log.LogWarning((object)message); } private void EndSession() { Flush(); if (_state != null && !_failed) { TryCheckpoint(); } _state = null; _repository = null; _bindings.Clear(); _lastRequest.Clear(); Current = null; _pending.Clear(); _attempts.Clear(); _retryAt.Clear(); _pendingPath = null; _pendingCharacter = 0L; _failed = false; _journalDirty = (_pendingDirty = (_publishDirty = false)); _checkpointed = -1L; InventoryTracking.Clear(); ArenaAdapter.Clear(); } public void Tick() { if ((Object)(object)_net != (Object)(object)ZNet.instance || _rpc != ZRoutedRpc.instance) { EndSession(); _net = ZNet.instance; _rpc = ZRoutedRpc.instance; if ((Object)(object)_net == (Object)null || _rpc == null) { return; } _rpc.Register<string>("BizareHUD.Report.v1", (Action<long, string>)OnReport); _rpc.Register<string>("BizareHUD.Snapshot.v1", (Action<long, string>)OnSnapshot); if (IsServer) { try { string text = _net.GetWorldUID().ToString(CultureInfo.InvariantCulture); _repository = new StateRepository(_root, text); _state = _repository.Load(text, _difficulty.Value); if (GUIManagerHeadless()) { _pendingPath = Path.Combine(_root, text, "server_pending.json"); _pendingCharacter = -1L; if (File.Exists(_pendingPath)) { _pending.AddRange(Json.Read<List<Report>>(File.ReadAllText(_pendingPath))); } } } catch (Exception ex) { _failed = true; _log.LogError((object)("Statistics storage could not be loaded; accounting stopped: " + ex)); } } } if ((Object)(object)_net == (Object)null || _rpc == null || _failed) { return; } if (Time.unscaledTime >= _nextPoll) { _nextPoll = Time.unscaledTime + 2f; if ((Object)(object)Player.m_localPlayer != (Object)null) { LoadPending(); Submit(new Report { Kind = "hello", World = WorldId }); InventoryTracking.Audit(((Humanoid)Player.m_localPlayer).GetInventory()); } int num = 0; Report[] array = _pending.ToArray(); foreach (Report report in array) { if (num == 32) { break; } if (!(report.World != WorldId) && (!_retryAt.TryGetValue(report.Id, out var value) || !(Time.unscaledTime < value))) { int value2; int num2 = (_attempts.TryGetValue(report.Id, out value2) ? value2 : 0); _attempts[report.Id] = num2 + 1; _retryAt[report.Id] = Time.unscaledTime + Math.Min(60f, 2f * (float)(1 << Math.Min(num2, 5))); Send(report); num++; } } if (IsServer) { _publishDirty = true; } } if (_publishDirty) { _publishDirty = false; Publish(); } if (_state != null && Time.unscaledTime >= _nextCheckpoint) { _nextCheckpoint = Time.unscaledTime + 15f; TryCheckpoint(); } Flush(); } private void Flush() { if (_failed) { return; } if (_journalDirty && _state != null && _repository != null) { _journalDirty = false; try { _repository.Append(_state); } catch (Exception ex) { _failed = true; _log.LogError((object)("Durable statistics commit failed; accounting stopped: " + ex)); return; } } if (!_pendingDirty || _pendingPath == null) { return; } _pendingDirty = false; try { StateRepository.AtomicWrite(_pendingPath, Json.Write(_pending)); } catch (Exception ex2) { _failed = true; _log.LogError((object)("Pending observations could not be saved; accounting stopped: " + ex2)); } } private void LoadPending() { long playerID = Player.m_localPlayer.GetPlayerID(); if (playerID == _pendingCharacter) { return; } Flush(); _pending.Clear(); _attempts.Clear(); _retryAt.Clear(); _pendingCharacter = playerID; _pendingPath = Path.Combine(Paths.ConfigPath, "BizareScoreHUD", "pending", playerID.ToString(CultureInfo.InvariantCulture) + ".json"); if (!File.Exists(_pendingPath)) { return; } try { _pending.AddRange(Json.Read<List<Report>>(File.ReadAllText(_pendingPath))); } catch (Exception ex) { _failed = true; _log.LogError((object)("Cannot recover pending observations: " + ex)); } } private void SavePending() { if (_pendingPath != null) { _pendingDirty = true; } } private static bool GUIManagerHeadless() { return GUIManager.IsHeadless(); } public bool Submit(Report report) { if (_failed || _rpc == null) { return false; } if (report.Kind != "hello") { if (string.IsNullOrEmpty(report.Run) || string.IsNullOrEmpty(WorldId)) { Warn("Unresolved observation before server run binding: " + report.Kind); return false; } report.Id = ((report.Id.Length == 0) ? Guid.NewGuid().ToString("N") : report.Id); report.World = WorldId; if ((Object)(object)Player.m_localPlayer != (Object)null) { LoadPending(); } if (_failed) { return false; } if (_pending.Count >= 4096) { Warn("Pending report capacity reached; observation deferred"); return false; } _pending.Add(report); SavePending(); } try { Send(report); } catch (Exception ex) { Warn("Observation remains queued: " + ex.Message); } return true; } private void Send(Report report) { if (IsServer) { OnReport(ZNet.GetUID(), Json.Write(report)); return; } ZNet? net = _net; if (((net != null) ? net.GetServerPeer() : null) != null) { _rpc.InvokeRoutedRPC(_net.GetServerPeer().m_uid, "BizareHUD.Report.v1", new object[1] { Json.Write(report) }); } } private unsafe string? Bind(long sender) { //IL_00be: 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) long playerID; string nickname; string text; if (sender == ZNet.GetUID()) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return null; } playerID = Player.m_localPlayer.GetPlayerID(); nickname = Player.m_localPlayer.GetPlayerName(); text = ((object)Unsafe.As<PlatformUserID, PlatformUserID>(ref UserInfo.GetLocalUser().UserId)/*cast due to .constrained prefix*/).ToString(); } else { ZNetPeer peer = _net.GetPeer(sender); if (peer == null || !peer.IsReady() || peer.m_playerID == 0L) { return null; } playerID = peer.m_playerID; nickname = peer.m_playerName; text = peer.m_socket.GetHostName(); if (((object)peer.m_socket).GetType().Name == "ZSteamSocket") { text = ((object)new PlatformUserID("Steam", text)/*cast due to .constrained prefix*/).ToString(); } else { PlatformUserID val = default(PlatformUserID); if (!PlatformUserID.TryParse(text, ref val)) { throw new InvalidOperationException("Authenticated platform account unavailable"); } text = ((object)(*(PlatformUserID*)(&val))/*cast due to .constrained prefix*/).ToString(); } } if (text.Length == 0 || playerID == 0L) { return null; } string id = text + ":" + playerID.ToString(CultureInfo.InvariantCulture); _bindings[sender] = id; if (!_state.Participants.TryGetValue(id, out Participant value) || value.Nickname != nickname) { Commit(delegate(WorldState s) { s.Join(id, nickname); }); } return id; } private void OnReport(long sender, string payload) { //IL_023c: Unknown result type (might be due to invalid IL or missing references) if (!IsServer || _state == null || _failed || payload.Length > 65536) { return; } Report report = null; try { report = Json.Read<Report>(payload); if (report.Protocol != 1) { throw new InvalidOperationException("Unsupported statistics protocol"); } string player = Bind(sender); if (player == null && sender == ZNet.GetUID() && report.Kind == "kill") { player = FindParticipant(report.Killer); } if (player == null) { return; } if (report.Kind == "hello") { if (!_lastRequest.TryGetValue(sender, out var value) || Time.unscaledTime - value >= 1f) { _lastRequest[sender] = Time.unscaledTime; Reply(sender, player, "", ""); } return; } if (report.World != _state.World || report.Id.Length != 32 || !_state.Runs.ContainsKey(report.Run)) { throw new InvalidOperationException("Wrong world/run or invalid event"); } string kind = report.Kind; if (!(kind == "gold")) { if (!(kind == "kill")) { throw new InvalidOperationException("Unknown observation kind"); } if (!_state.Deaths.ContainsKey(report.Victim)) { if (!TryId(report.Victim, out var id)) { throw new InvalidOperationException("Invalid victim"); } ZDO zDO = ZDOMan.instance.GetZDO(id); MobRecord value2; MobRecord evidence = ((zDO != null) ? Observe(zDO) : (_state.Mobs.TryGetValue(report.Victim, out value2) ? value2 : null)); if (evidence == null || evidence.Owner != sender) { throw new InvalidOperationException("Victim not owned by reporting peer"); } string wave = evidence.Wave; if (wave != report.Wave || !_state.Waves.TryGetValue(wave, out Wave value3) || value3.Run != report.Run) { throw new InvalidOperationException("Missing server wave association"); } if (evidence.Boss != report.Boss) { throw new InvalidOperationException("Unclassified custom boss override"); } if (evidence.Tamed) { throw new InvalidOperationException("Ineligible victim"); } string killer = FindParticipant(report.Killer); if (killer == null) { throw new InvalidOperationException("Unresolved killer identity"); } int level = evidence.Level; Commit(delegate(WorldState s) { s.Kill(report.Victim, wave, killer, level, evidence.Boss); }); } } else { if (report.Ranges.Count > 256) { throw new InvalidOperationException("Too many currency ranges"); } Commit(delegate(WorldState s) { s.Acquire(player, report.Run, report.Ranges); }); } Reply(sender, player, report.Id, ""); _publishDirty = true; } catch (Exception ex) { Warn("Rejected statistics observation: " + ex.Message); if (report != null && _bindings.TryGetValue(sender, out string value4)) { Reply(sender, value4, "", ex.Message); } } } private string? FindParticipant(long character) { foreach (ZNetPeer peer in _net.GetPeers()) { if (peer.m_playerID == character) { return Bind(peer.m_uid); } } if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == character) { return Bind(ZNet.GetUID()); } return _state.Participants.Keys.SingleOrDefault((string id) => id.EndsWith(":" + character.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal)); } public static bool TryId(string text, out ZDOID id) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //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) id = ZDOID.None; string[] array = text.Split(':'); if (array.Length != 2 || !long.TryParse(array[0], out var result) || !uint.TryParse(array[1], out var result2)) { return false; } id = new ZDOID(result, result2); return true; } public void Commit(Action<WorldState> reduce) { if (_state != null && _repository != null && !_failed) { reduce(_state); _state.Revision = checked(_state.Revision + 1); _journalDirty = true; } } public Wave? AcceptWave(ZDO arena, int tier, int stage) { //IL_0087: 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) if (_state == null) { return null; } Wave wave = new Wave { Id = Guid.NewGuid().ToString("N"), Run = _state.CurrentRun, Arena = ((object)Unsafe.As<ZDOID, ZDOID>(ref arena.m_uid)/*cast due to .constrained prefix*/).ToString(), Sequence = _state.Revision + 1, Tier = tier, Stage = stage, GlobalNumber = ScorePolicy.GlobalWave(tier, stage), X = arena.GetPosition().x, Z = arena.GetPosition().z }; Commit(delegate(WorldState s) { s.Waves.Add(wave.Id, wave); }); return wave; } public void Reset(string activation) { Commit(delegate(WorldState s) { s.Reset(activation, _difficulty.Value); }); Publish(); } public void Associate(ZDO zdo, Wave wave) { if (zdo.GetString(ZdoKeys.Wave, "").Length == 0 && (IsServer || zdo.IsOwner())) { zdo.Set(ZdoKeys.Wave, wave.Id); zdo.Set(ZdoKeys.Run, wave.Run); } } public void EventStarted(RandomEvent? ev, Vector3 position, Wave? context) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!IsServer || _state == null) { return; } Commit(delegate(WorldState s) { s.Events.RemoveAll(delegate(ArenaEvent e) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (ev != null) { Vector2 val = new Vector2(e.X - position.x, e.Z - position.z); return ((Vector2)(ref val)).sqrMagnitude < 1f; } return true; }); if (ev != null && context != null) { s.Events.Add(new ArenaEvent { Name = ev.m_name, Wave = context.Id, X = position.x, Z = position.z }); } }); Publish(); } public Wave? EventWave(RandomEvent ev) { ArenaEvent entry = ((!IsServer) ? Current?.Events : _state?.Events)?.LastOrDefault(delegate(ArenaEvent e) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (e.Name == ev.m_name) { Vector2 val = new Vector2(e.X - ev.m_pos.x, e.Z - ev.m_pos.z); return ((Vector2)(ref val)).sqrMagnitude < 1f; } return false; }); if (entry == null) { return null; } if (!IsServer) { return Current?.EventWaves.Find((Wave w) => w.Id == entry.Wave); } if (!_state.Waves.TryGetValue(entry.Wave, out Wave value)) { return null; } return value; } public MobRecord? Observe(ZDO zdo) { if (_state == null || (Object)(object)ZNetScene.instance == (Object)null) { return null; } string text = zdo.GetString(ZdoKeys.Wave, ""); if (text.Length == 0 || !_state.Waves.ContainsKey(text)) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab()); Character val = (((Object)(object)prefab == (Object)null) ? null : prefab.GetComponent<Character>()); if ((Object)(object)val == (Object)null || val.IsPlayer()) { return null; } return new MobRecord { Wave = text, Owner = zdo.GetOwner(), Level = zdo.GetInt(ZDOVars.s_level, 1), Boss = val.IsBoss(), Tamed = zdo.GetBool(ZDOVars.s_tamed, false), LastAttacker = zdo.GetLong(ZdoKeys.Attacker, 0L), HitTicks = zdo.GetLong(ZdoKeys.HitTime, 0L) }; } public void PreserveVictim(ZDO zdo) { if (!IsServer || _state == null) { return; } MobRecord evidence = Observe(zdo); if (evidence == null) { return; } string id = ((object)Unsafe.As<ZDOID, ZDOID>(ref zdo.m_uid)/*cast due to .constrained prefix*/).ToString(); if (!_state.Deaths.ContainsKey(id) && (evidence.Owner != ZNet.GetUID() || !((Object)(object)Player.m_localPlayer == (Object)null))) { Commit(delegate(WorldState s) { s.Mobs[id] = evidence; }); } } private void Reply(long sender, string player, string ack, string error) { WorldState s = _state; BestRecord value; string text = Json.Write(new Snapshot { Revision = s.Revision, World = s.World, Run = s.CurrentRun, StartedUtcTicks = s.Active.StartedUtcTicks, ServerUtcTicks = DateTime.UtcNow.Ticks, Events = s.Events, EventWaves = s.Events.Select((ArenaEvent e) => s.Waves[e.Wave]).Distinct().ToList(), Ack = ack, Error = error, Personal = s.Get(s.CurrentRun, player), Team = s.Team(), BestUnits = (s.Bests.TryGetValue(player, out value) ? value.ScoreUnits : 0), ConnectedPlayers = Connected }); if (sender == ZNet.GetUID()) { OnSnapshot(sender, text); return; } _rpc.InvokeRoutedRPC(sender, "BizareHUD.Snapshot.v1", new object[1] { text }); } public void Publish() { if (_state == null || _failed) { return; } ZNetPeer[] array = (from p in _net.GetPeers() where p.IsReady() && p.m_playerID != 0 select p).ToArray(); foreach (ZNetPeer val in array) { string text = Bind(val.m_uid); if (text != null) { Reply(val.m_uid, text, "", ""); } } if ((Object)(object)Player.m_localPlayer != (Object)null) { string text2 = Bind(ZNet.GetUID()); if (text2 != null) { Reply(ZNet.GetUID(), text2, "", ""); } } } private void OnSnapshot(long sender, string payload) { long num; if (!IsServer) { ZNet? net = _net; num = ((net == null) ? ((long?)null) : net.GetServerPeer()?.m_uid).GetValueOrDefault(); } else { num = ZNet.GetUID(); } long num2 = num; if (sender != num2 || payload.Length > 65536) { return; } try { Snapshot snapshot = Json.Read<Snapshot>(payload); if (snapshot.Protocol == 1) { if (snapshot.Ack.Length > 0 && _pending.RemoveAll((Report r) => r.Id == snapshot.Ack && r.World == snapshot.World) > 0) { _attempts.Remove(snapshot.Ack); _retryAt.Remove(snapshot.Ack); SavePending(); } if (Current == null || !(Current.World == snapshot.World) || snapshot.Revision >= Current.Revision) { Current = snapshot; _receivedAt = Time.unscaledTime; } } } catch (Exception ex) { Warn("Invalid statistics snapshot: " + ex.Message); } } private void TryCheckpoint() { if (_state == null || _repository == null || _failed) { return; } Flush(); if (_failed || _state.Revision == _checkpointed) { return; } try { _repository.Checkpoint(_state); _checkpointed = _state.Revision; } catch (Exception ex) { _log.LogError((object)("Statistics checkpoint failed; journal retained: " + ex)); } } public void Dispose() { EndSession(); Instance = null; } } internal static class ZdoKeys { public static readonly int Wave = StringExtensionMethods.GetStableHashCode("bizarehud.wave"); public static readonly int Run = StringExtensionMethods.GetStableHashCode("bizarehud.run"); public static readonly int Attacker = StringExtensionMethods.GetStableHashCode("bizarehud.attacker"); public static readonly int HitTime = StringExtensionMethods.GetStableHashCode("bizarehud.hitTime"); } } namespace BizareScoreHUD.Domain { public static class Provenance { public static List<LotRange> Take(List<LotRange> source, long quantity) { if (quantity < 0 || source.Sum((LotRange r) => r.Count) < quantity) { throw new InvalidOperationException("Currency conservation failure"); } List<LotRange> list = new List<LotRange>(); while (quantity > 0) { LotRange lotRange = source[0]; long num = Math.Min(quantity, lotRange.Count); list.Add(lotRange.Slice(0L, num)); quantity -= num; if (num == lotRange.Count) { source.RemoveAt(0); continue; } checked { lotRange.Start += num; } lotRange.Count -= num; } return list; } public static List<LotRange> Copy(IEnumerable<LotRange> source) { return source.Select((LotRange r) => r.Slice(0L, r.Count)).ToList(); } public static LotRange Create(long count, bool fresh, string run) { return new LotRange { Lot = Guid.NewGuid().ToString("N"), Count = count, Fresh = fresh, Run = run }; } } public enum ArenaDifficulty { Easy, Normal, Hard, UltraHard } public static class ScorePolicy { public const long UnitsPerPoint = 1000000L; public static readonly string[] StageKeys = new string[8] { "meadowstage", "blackforeststage", "swampstage", "mountstage", "plainsstage", "mistlandsstage", "ashlandsstage", "deepnorthstage" }; public static readonly int[] StageCounts = new int[8] { 4, 8, 8, 6, 6, 6, 8, 7 }; public static long KillUnits(int tier, int wave, int level, bool boss, ArenaDifficulty difficulty) { if (tier < 1 || tier > 100 || wave < 1 || wave > 100000) { throw new ArgumentOutOfRangeException("wave"); } if (!boss && (level < 1 || level > 3)) { throw new ArgumentOutOfRangeException("level", "Unsupported mob level"); } int num = difficulty switch { ArenaDifficulty.Easy => 75, ArenaDifficulty.Normal => 100, ArenaDifficulty.Hard => 125, ArenaDifficulty.UltraHard => 150, _ => throw new ArgumentOutOfRangeException("difficulty"), }; long num2; checked { num2 = unchecked((long)(boss ? 100 : 10)) * unchecked((long)(boss ? 100 : checked(100 + (level - 1) * 25))) * (9 + tier) * (99 + wave) * num; } if (num2 % 10 != 0L) { throw new InvalidOperationException("Inexact scoring rule"); } return num2 / 10; } public static int GlobalWave(int tier, int stage) { if (tier < 1 || tier > StageCounts.Length || stage < 1 || stage > StageCounts[tier - 1]) { throw new ArgumentOutOfRangeException("stage"); } int num = stage; for (int i = 0; i < tier - 1; i++) { num += StageCounts[i]; } return num; } } public sealed class Counters { public long ScoreUnits; public long Kills; public long Gold; public long Score => ScoreUnits / 1000000; } public sealed class Participant { public string Id = ""; public string Nickname = ""; } public sealed class BestRecord { public string ParticipantId = ""; public string Nickname = ""; public long ScoreUnits; public string World = ""; public string Run = ""; public ArenaDifficulty Difficulty; public int RuleVersion = 1; public long Score => ScoreUnits / 1000000; } public sealed class Wave { public string Id = ""; public string Run = ""; public string Arena = ""; public long Sequence; public int Tier; public int Stage; public int GlobalNumber; public float X; public float Z; } public sealed class Run { public string Id = Guid.NewGuid().ToString("N"); public long StartedUtcTicks = DateTime.UtcNow.Ticks; public ArenaDiffic