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 NuageReport v1.0.0
NuageReport.Core.dll
Decompiled 8 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using Microsoft.CodeAnalysis; using NuageReport.Core.Model; [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("TheoHay, GangDesNuages")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f7dcda716f6ccd2361b1be02417714f4cb5ee1f9")] [assembly: AssemblyProduct("NuageReport.Core")] [assembly: AssemblyTitle("NuageReport.Core")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TheoHay/NuageREPO")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace NuageReport.Core.Reporting { public sealed class DisplayReportFormatter { public string Format(RunReport report, DisplayReportMode mode) { if (report == null) { throw new ArgumentNullException("report"); } RunReportSummary summary = report.Summary; GlobalReportSummary global = summary.Global; StringBuilder stringBuilder = new StringBuilder(); AppendSection(stringBuilder, "Global data"); AppendCountAndValue(stringBuilder, "Objects in map", global.ObjectsAtStart, global.ObjectsAtStartValue); AppendCountAndValue(stringBuilder, "Objects extracted", global.ObjectsExtracted, global.ObjectsExtractedValue); stringBuilder.Append("Objects detected: ").Append(global.ObjectsDetected.ToString(CultureInfo.InvariantCulture)).Append(" / ") .Append(global.ObjectsAtStart.ToString(CultureInfo.InvariantCulture)) .Append(" (") .Append(global.ObjectsDetectedPercent.ToString("0.##", CultureInfo.InvariantCulture)) .AppendLine("%)"); stringBuilder.Append("Objects missed: ").AppendLine(global.ObjectsMissed.ToString(CultureInfo.InvariantCulture)); stringBuilder.Append("Objects damaged: ").AppendLine(global.ObjectsDamaged.ToString(CultureInfo.InvariantCulture)); stringBuilder.Append("Objects destroyed: ").AppendLine(global.ObjectsDestroyed.ToString(CultureInfo.InvariantCulture)); AppendValue(stringBuilder, "Total value lost", global.ObjectsValueLost); stringBuilder.AppendLine(); AppendCountAndValue(stringBuilder, "Monster loot orbs collected", global.OrbsCollected, global.OrbsExtractedValue); stringBuilder.Append("Monster loot orbs destroyed: ").Append(global.OrbsDestroyed.ToString(CultureInfo.InvariantCulture)).Append(" ($") .Append(global.OrbsLostValue.ToString(CultureInfo.InvariantCulture)) .AppendLine(" lost)"); stringBuilder.AppendLine(); stringBuilder.Append("Damage dealt to monsters: ").AppendLine(global.MonsterDamageTaken.ToString(CultureInfo.InvariantCulture)); stringBuilder.Append("Monsters killed: ").AppendLine(global.MonstersKilled.ToString(CultureInfo.InvariantCulture)); stringBuilder.Append("Damage taken by players: ").AppendLine(global.PlayerDamageTaken.ToString(CultureInfo.InvariantCulture)); AppendSection(stringBuilder, "Player data"); if (summary.Players.Count == 0) { stringBuilder.AppendLine("No player-specific stats recorded yet."); } else { foreach (PlayerReportSummary player in summary.Players) { string value = (string.IsNullOrWhiteSpace(player.DisplayName) ? player.PlayerId : player.DisplayName); stringBuilder.AppendLine(value); stringBuilder.Append(" Cart/cashout objects: ").Append(player.ObjectsPutInCartOrCashout.ToString(CultureInfo.InvariantCulture)).Append(" ($") .Append(player.ObjectsPutInCartOrCashoutValue.ToString(CultureInfo.InvariantCulture)) .AppendLine(")"); stringBuilder.Append(" Extracted weight: ").Append(player.ExtractedObjectWeight.ToString("0.##", CultureInfo.InvariantCulture)).Append(" total, ") .Append(player.AverageExtractedObjectWeight.ToString("0.##", CultureInfo.InvariantCulture)) .AppendLine(" avg/object"); stringBuilder.Append(" Average extracted value: $").Append(player.AverageExtractedObjectValue.ToString(CultureInfo.InvariantCulture)).AppendLine("/object"); stringBuilder.Append(" Objects detected: ").Append(player.ObjectsDetected.ToString(CultureInfo.InvariantCulture)).Append(" ($") .Append(player.ObjectsDetectedValue.ToString(CultureInfo.InvariantCulture)) .AppendLine(")"); stringBuilder.Append(" Damage taken: ").AppendLine(player.DamageTaken.ToString(CultureInfo.InvariantCulture)); stringBuilder.AppendLine(); } } return stringBuilder.ToString().TrimEnd(); } private static void AppendSection(StringBuilder builder, string title) { if (builder.Length > 0) { builder.AppendLine(); } builder.AppendLine(title.ToUpperInvariant()); builder.AppendLine("----------------------------------------"); } private static void AppendValue(StringBuilder builder, string label, int value) { builder.Append(label).Append(": $").AppendLine(value.ToString(CultureInfo.InvariantCulture)); } private static void AppendCountAndValue(StringBuilder builder, string label, int count, int value) { builder.Append(label).Append(": ").Append(count.ToString(CultureInfo.InvariantCulture)) .Append(" ($") .Append(value.ToString(CultureInfo.InvariantCulture)) .AppendLine(")"); } } public enum DisplayReportMode { Summary, Live } public sealed class JsonReportFormatter { public string Format(RunReport report) { if (report == null) { throw new ArgumentNullException("report"); } RunReportSummary summary = report.Summary; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("{"); AppendProperty(stringBuilder, 1, "schemaVersion", "1", comma: true); AppendProperty(stringBuilder, 1, "runId", report.RunId, comma: true); AppendProperty(stringBuilder, 1, "startedAt", report.StartedAt.ToString("O", CultureInfo.InvariantCulture), comma: true); AppendProperty(stringBuilder, 1, "endedAt", report.EndedAt?.ToString("O", CultureInfo.InvariantCulture), comma: true); AppendGlobal(stringBuilder, summary.Global); stringBuilder.AppendLine(","); AppendPlayers(stringBuilder, summary); stringBuilder.AppendLine(","); AppendCosmeticObjects(stringBuilder, summary); stringBuilder.AppendLine(); stringBuilder.AppendLine("}"); return stringBuilder.ToString(); } private static void AppendGlobal(StringBuilder builder, GlobalReportSummary global) { Indent(builder, 1).AppendLine("\"global\": {"); AppendProperty(builder, 2, "objectsAtStart", global.ObjectsAtStart, comma: true); AppendProperty(builder, 2, "objectsAtStartValue", global.ObjectsAtStartValue, comma: true); AppendProperty(builder, 2, "objectsExtracted", global.ObjectsExtracted, comma: true); AppendProperty(builder, 2, "objectsExtractedValue", global.ObjectsExtractedValue, comma: true); AppendProperty(builder, 2, "objectsDetected", global.ObjectsDetected, comma: true); AppendProperty(builder, 2, "objectsDetectedPercent", global.ObjectsDetectedPercent, comma: true); AppendProperty(builder, 2, "objectsMissed", global.ObjectsMissed, comma: true); AppendProperty(builder, 2, "objectsDamaged", global.ObjectsDamaged, comma: true); AppendProperty(builder, 2, "objectsDestroyed", global.ObjectsDestroyed, comma: true); AppendProperty(builder, 2, "objectsValueLost", global.ObjectsValueLost, comma: true); AppendProperty(builder, 2, "orbsCollected", global.OrbsCollected, comma: true); AppendProperty(builder, 2, "orbsDestroyed", global.OrbsDestroyed, comma: true); AppendProperty(builder, 2, "orbsExtractedValue", global.OrbsExtractedValue, comma: true); AppendProperty(builder, 2, "orbsLostValue", global.OrbsLostValue, comma: true); AppendProperty(builder, 2, "cosmeticObjectsExisting", global.CosmeticObjectsExisting, comma: true); AppendProperty(builder, 2, "cosmeticObjectsFound", global.CosmeticObjectsFound, comma: true); AppendProperty(builder, 2, "monsterDamageTaken", global.MonsterDamageTaken, comma: true); AppendProperty(builder, 2, "monstersKilled", global.MonstersKilled, comma: true); AppendProperty(builder, 2, "playerDamageTaken", global.PlayerDamageTaken, comma: false); Indent(builder, 1).Append('}'); } private static void AppendPlayers(StringBuilder builder, RunReportSummary summary) { Indent(builder, 1).AppendLine("\"players\": ["); for (int i = 0; i < summary.Players.Count; i++) { PlayerReportSummary playerReportSummary = summary.Players[i]; Indent(builder, 2).AppendLine("{"); AppendProperty(builder, 3, "playerId", playerReportSummary.PlayerId, comma: true); AppendProperty(builder, 3, "displayName", playerReportSummary.DisplayName, comma: true); AppendProperty(builder, 3, "objectsPutInCartOrCashout", playerReportSummary.ObjectsPutInCartOrCashout, comma: true); AppendProperty(builder, 3, "objectsPutInCartOrCashoutValue", playerReportSummary.ObjectsPutInCartOrCashoutValue, comma: true); AppendProperty(builder, 3, "extractedObjectWeight", playerReportSummary.ExtractedObjectWeight, comma: true); AppendProperty(builder, 3, "averageExtractedObjectWeight", playerReportSummary.AverageExtractedObjectWeight, comma: true); AppendProperty(builder, 3, "averageExtractedObjectValue", playerReportSummary.AverageExtractedObjectValue, comma: true); AppendProperty(builder, 3, "objectsDetected", playerReportSummary.ObjectsDetected, comma: true); AppendProperty(builder, 3, "objectsDetectedValue", playerReportSummary.ObjectsDetectedValue, comma: true); AppendProperty(builder, 3, "damageDealt", playerReportSummary.DamageDealt, comma: true); AppendProperty(builder, 3, "damageTaken", playerReportSummary.DamageTaken, comma: false); Indent(builder, 2).Append('}'); if (i < summary.Players.Count - 1) { builder.Append(','); } builder.AppendLine(); } Indent(builder, 1).Append(']'); } private static void AppendCosmeticObjects(StringBuilder builder, RunReportSummary summary) { Indent(builder, 1).AppendLine("\"cosmeticObjects\": {"); AppendValuableList(builder, 2, "existing", summary.CosmeticObjectsExisting, comma: true); AppendValuableList(builder, 2, "found", summary.CosmeticObjectsFound, comma: false); Indent(builder, 1).Append('}'); } private static void AppendValuableList(StringBuilder builder, int depth, string name, IReadOnlyList<ValuableRef> values, bool comma) { Indent(builder, depth).Append('"').Append(Escape(name)).AppendLine("\": ["); for (int i = 0; i < values.Count; i++) { ValuableRef valuableRef = values[i]; Indent(builder, depth + 1).Append("{ "); AppendInlineProperty(builder, "runtimeId", valuableRef.RuntimeId, comma: true); AppendInlineProperty(builder, "name", valuableRef.Name, comma: true); AppendInlineProperty(builder, "valueAtEvent", valuableRef.ValueAtEvent, comma: true); AppendInlineProperty(builder, "weightAtEvent", valuableRef.WeightAtEvent, comma: false); builder.Append(" }"); if (i < values.Count - 1) { builder.Append(','); } builder.AppendLine(); } Indent(builder, depth).Append(']'); if (comma) { builder.Append(','); } builder.AppendLine(); } private static void AppendProperty(StringBuilder builder, int depth, string name, string? value, bool comma) { Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": "); if (value == null) { builder.Append("null"); } else { builder.Append('"').Append(Escape(value)).Append('"'); } AppendLineEnding(builder, comma); } private static void AppendProperty(StringBuilder builder, int depth, string name, int? value, bool comma) { Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": "); builder.Append(value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : "null"); AppendLineEnding(builder, comma); } private static void AppendProperty(StringBuilder builder, int depth, string name, int value, bool comma) { Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ") .Append(value.ToString(CultureInfo.InvariantCulture)); AppendLineEnding(builder, comma); } private static void AppendProperty(StringBuilder builder, int depth, string name, double value, bool comma) { Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ") .Append(value.ToString("0.##", CultureInfo.InvariantCulture)); AppendLineEnding(builder, comma); } private static void AppendProperty(StringBuilder builder, int depth, string name, float value, bool comma) { Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ") .Append(value.ToString("0.###", CultureInfo.InvariantCulture)); AppendLineEnding(builder, comma); } private static void AppendProperty(StringBuilder builder, int depth, string name, bool value, bool comma) { Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ") .Append(value ? "true" : "false"); AppendLineEnding(builder, comma); } private static void AppendInlineProperty(StringBuilder builder, string name, string? value, bool comma) { builder.Append('"').Append(Escape(name)).Append("\": "); if (value == null) { builder.Append("null"); } else { builder.Append('"').Append(Escape(value)).Append('"'); } if (comma) { builder.Append(", "); } } private static void AppendInlineProperty(StringBuilder builder, string name, int? value, bool comma) { builder.Append('"').Append(Escape(name)).Append("\": "); builder.Append(value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : "null"); if (comma) { builder.Append(", "); } } private static void AppendInlineProperty(StringBuilder builder, string name, float? value, bool comma) { builder.Append('"').Append(Escape(name)).Append("\": "); builder.Append(value.HasValue ? value.Value.ToString("0.###", CultureInfo.InvariantCulture) : "null"); if (comma) { builder.Append(", "); } } private static void AppendLineEnding(StringBuilder builder, bool comma) { if (comma) { builder.Append(','); } builder.AppendLine(); } private static StringBuilder Indent(StringBuilder builder, int depth) { return builder.Append(' ', depth * 2); } private static string Escape(string value) { StringBuilder stringBuilder = new StringBuilder(value.Length + 8); foreach (char c in value) { switch (c) { case '\\': stringBuilder.Append("\\\\"); continue; case '"': stringBuilder.Append("\\\""); continue; case '\b': stringBuilder.Append("\\b"); continue; case '\f': stringBuilder.Append("\\f"); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\r': stringBuilder.Append("\\r"); continue; case '\t': stringBuilder.Append("\\t"); continue; } if (char.IsControl(c)) { StringBuilder stringBuilder2 = stringBuilder.Append("\\u"); int num = c; stringBuilder2.Append(num.ToString("x4", CultureInfo.InvariantCulture)); } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } } public sealed class LiveReportPayloadFormatter { public string Format(RunReport report) { if (report == null) { throw new ArgumentNullException("report"); } return new JsonReportFormatter().Format(report); } } public sealed class TextReportFormatter { public string Format(RunReport report) { if (report == null) { throw new ArgumentNullException("report"); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Run: " + report.RunId); stringBuilder.AppendLine($"Started: {report.StartedAt:u}"); stringBuilder.AppendLine("Ended: " + (report.EndedAt.HasValue ? report.EndedAt.Value.ToString("u") : "active")); stringBuilder.AppendLine($"Objects at start: {report.Summary.Global.ObjectsAtStart} (${report.Summary.Global.ObjectsAtStartValue})"); stringBuilder.AppendLine($"Discovered valuables: {report.Summary.Global.ObjectsDetected} ({report.Summary.Global.ObjectsDetectedPercent:0.##}%)"); stringBuilder.AppendLine($"First carted valuables: {report.FirstCartedCount}"); stringBuilder.AppendLine($"Extracted value: ${report.Summary.Global.ObjectsExtractedValue}"); stringBuilder.AppendLine($"Lost/destroyed value: ${report.Summary.Global.ObjectsValueLost}"); stringBuilder.AppendLine($"Monster damage taken: {report.Summary.Global.MonsterDamageTaken}"); stringBuilder.AppendLine($"Player damage taken: {report.Summary.Global.PlayerDamageTaken}"); stringBuilder.AppendLine(); stringBuilder.AppendLine("Events:"); foreach (RunEvent @event in report.Events) { stringBuilder.Append("- "); stringBuilder.Append(@event.Timestamp.ToString("u")); stringBuilder.Append(' '); stringBuilder.Append(@event.Type); stringBuilder.Append(" ["); stringBuilder.Append(@event.Confidence); stringBuilder.Append(']'); if (@event.Valuable != null) { stringBuilder.Append(" valuable="); stringBuilder.Append(@event.Valuable); } if (@event.Player != null) { stringBuilder.Append(" player="); stringBuilder.Append(@event.Player); } if (!string.IsNullOrWhiteSpace(@event.Message)) { stringBuilder.Append(" — "); stringBuilder.Append(@event.Message); } stringBuilder.AppendLine(); } return stringBuilder.ToString(); } } } namespace NuageReport.Core.Recording { public interface IClock { DateTimeOffset UtcNow { get; } } public interface IRunEventSink { void Record(RunEvent runEvent); } public sealed class RunRecorder : IRunEventSink { private readonly IClock _clock; private readonly List<RunEvent> _events = new List<RunEvent>(); private readonly Dictionary<string, ValuableRef> _spawnedValuables = new Dictionary<string, ValuableRef>(StringComparer.Ordinal); private readonly HashSet<string> _discoveredValuables = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _firstCartedValuables = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _extractedValuables = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _destroyedValuables = new HashSet<string>(StringComparer.Ordinal); private readonly Dictionary<string, PlayerRef> _lockedValuableAttribution = new Dictionary<string, PlayerRef>(StringComparer.Ordinal); private readonly HashSet<string> _spawnedOrbs = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _extractedOrbs = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _destroyedOrbs = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _cosmeticObjectsExisting = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _cosmeticObjectsFound = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _killedMonsters = new HashSet<string>(StringComparer.Ordinal); private string? _activeRunId; private DateTimeOffset _startedAt; private DateTimeOffset? _endedAt; public bool IsActive => _activeRunId != null; public string? ActiveRunId => _activeRunId; public int EventVersion { get; private set; } public event Action<RunEvent>? EventRecorded; public RunRecorder(IClock clock) { _clock = clock ?? throw new ArgumentNullException("clock"); } public void StartRun(string runId, string? levelName) { if (string.IsNullOrWhiteSpace(runId)) { throw new ArgumentException("Run id is required.", "runId"); } _activeRunId = runId; _startedAt = _clock.UtcNow; _endedAt = null; EventVersion = 0; _events.Clear(); _spawnedValuables.Clear(); _discoveredValuables.Clear(); _firstCartedValuables.Clear(); _extractedValuables.Clear(); _destroyedValuables.Clear(); _lockedValuableAttribution.Clear(); _spawnedOrbs.Clear(); _extractedOrbs.Clear(); _destroyedOrbs.Clear(); _cosmeticObjectsExisting.Clear(); _cosmeticObjectsFound.Clear(); _killedMonsters.Clear(); Record(new RunEvent(RunEventType.LevelStarted, _startedAt, ConfidenceLevel.High) { LevelName = levelName, Message = "Level started." }); } public bool TryRecordValuableSpawned(ValuableRef valuable, string? levelName = null) { if (valuable == null) { throw new ArgumentNullException("valuable"); } if (!IsActive) { return false; } if (_spawnedValuables.TryGetValue(valuable.RuntimeId, out ValuableRef value)) { _spawnedValuables[valuable.RuntimeId] = Merge(value, valuable); if ((valuable.ValueAtEvent.HasValue && value.ValueAtEvent != valuable.ValueAtEvent) || (valuable.WeightAtEvent.HasValue && value.WeightAtEvent != valuable.WeightAtEvent)) { Record(new RunEvent(RunEventType.ValuableValueUpdated, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = _spawnedValuables[valuable.RuntimeId], Message = "Valuable value/weight updated." }); } return false; } _spawnedValuables.Add(valuable.RuntimeId, valuable); Record(new RunEvent(RunEventType.ValuableSpawned, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = valuable, Message = "Valuable spawned." }); return true; } public bool TryRecordValuableDiscovered(ValuableRef valuable, PlayerRef? player = null, ConfidenceLevel confidence = ConfidenceLevel.High, string? levelName = null) { if (valuable == null) { throw new ArgumentNullException("valuable"); } if (!IsActive || !_discoveredValuables.Add(valuable.RuntimeId)) { return false; } Record(new RunEvent(RunEventType.ValuableDiscovered, _clock.UtcNow, confidence) { LevelName = levelName, Valuable = valuable, Player = player, Message = ((player == null) ? "Valuable discovered." : "Valuable discovered by player.") }); return true; } public bool TryRecordFirstCarted(ValuableRef valuable, string? levelName = null) { return TryRecordFirstCarted(valuable, null, ConfidenceLevel.High, levelName); } public bool TryRecordFirstCarted(ValuableRef valuable, PlayerRef? player, ConfidenceLevel confidence = ConfidenceLevel.Medium, string? levelName = null) { if (valuable == null) { throw new ArgumentNullException("valuable"); } if (!IsActive || !_firstCartedValuables.Add(valuable.RuntimeId)) { return false; } LockAttributionIfAvailable(valuable.RuntimeId, player); Record(new RunEvent(RunEventType.ValuableFirstCarted, _clock.UtcNow, confidence) { LevelName = levelName, Valuable = valuable, Player = (GetLockedAttribution(valuable.RuntimeId) ?? player), Message = "Valuable entered a cart or cashout zone for the first confirmed time." }); return true; } public bool TryRecordValuableExtracted(ValuableRef valuable, PlayerRef? player = null, ConfidenceLevel confidence = ConfidenceLevel.High, string? levelName = null) { if (valuable == null) { throw new ArgumentNullException("valuable"); } if (!IsActive || !_extractedValuables.Add(valuable.RuntimeId)) { return false; } LockAttributionIfAvailable(valuable.RuntimeId, player); Record(new RunEvent(RunEventType.ValuableExtracted, _clock.UtcNow, confidence) { LevelName = levelName, Valuable = valuable, Player = (GetLockedAttribution(valuable.RuntimeId) ?? player), Message = ((player == null) ? "Valuable extracted." : "Valuable extracted with player attribution.") }); return true; } public void RecordValuableDamaged(ValuableRef valuable, int valueLost, string? levelName = null) { if (valuable == null) { throw new ArgumentNullException("valuable"); } if (IsActive) { int num = Math.Max(0, valueLost); Record(new RunEvent(RunEventType.ValuableDamaged, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = valuable, DeltaValue = ((num != 0) ? (-num) : 0), Message = ((num == 0) ? "Valuable damaged." : "Valuable lost value from damage.") }); } } public bool TryRecordValuableDestroyed(ValuableRef valuable, string? levelName = null) { if (valuable == null) { throw new ArgumentNullException("valuable"); } if (!IsActive || !_destroyedValuables.Add(valuable.RuntimeId)) { return false; } Record(new RunEvent(RunEventType.ValuableDestroyed, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = valuable, Message = "Valuable destroyed." }); return true; } public bool TryRecordOrbSpawned(ValuableRef orb, string? rarity = null, string? levelName = null) { if (orb == null) { throw new ArgumentNullException("orb"); } if (!IsActive || !_spawnedOrbs.Add(orb.RuntimeId)) { return false; } RunEvent runEvent = new RunEvent(RunEventType.OrbSpawned, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = orb, Message = "Monster loot orb spawned." }; AddTag(runEvent, "rarity", rarity); Record(runEvent); return true; } public bool TryRecordOrbExtracted(ValuableRef orb, string? rarity = null, string? levelName = null) { if (orb == null) { throw new ArgumentNullException("orb"); } if (!IsActive || !_extractedOrbs.Add(orb.RuntimeId)) { return false; } RunEvent runEvent = new RunEvent(RunEventType.OrbExtracted, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = orb, Message = "Monster loot orb extracted." }; AddTag(runEvent, "rarity", rarity); Record(runEvent); return true; } public bool TryRecordOrbDestroyed(ValuableRef orb, string? rarity = null, string? levelName = null) { if (orb == null) { throw new ArgumentNullException("orb"); } if (!IsActive || !_destroyedOrbs.Add(orb.RuntimeId)) { return false; } RunEvent runEvent = new RunEvent(RunEventType.OrbDestroyed, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = orb, Message = "Monster loot orb destroyed." }; AddTag(runEvent, "rarity", rarity); Record(runEvent); return true; } public bool TryRecordCosmeticObjectExisting(ValuableRef cosmeticObject, string? rarity = null, string? levelName = null) { if (cosmeticObject == null) { throw new ArgumentNullException("cosmeticObject"); } if (!IsActive || !_cosmeticObjectsExisting.Add(cosmeticObject.RuntimeId)) { return false; } RunEvent runEvent = new RunEvent(RunEventType.CosmeticObjectExisting, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = cosmeticObject, Message = "Cosmetic object existed in the level." }; AddTag(runEvent, "rarity", rarity); Record(runEvent); return true; } public bool TryRecordCosmeticObjectFound(ValuableRef cosmeticObject, string? rarity = null, string? levelName = null) { if (cosmeticObject == null) { throw new ArgumentNullException("cosmeticObject"); } if (!IsActive || !_cosmeticObjectsFound.Add(cosmeticObject.RuntimeId)) { return false; } RunEvent runEvent = new RunEvent(RunEventType.CosmeticObjectFound, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Valuable = cosmeticObject, Message = "Cosmetic object was found/extracted." }; AddTag(runEvent, "rarity", rarity); Record(runEvent); return true; } public void RecordMonsterDamaged(string monsterId, string monsterName, int damageAmount, string? levelName = null) { if (!string.IsNullOrWhiteSpace(monsterId) && damageAmount > 0 && IsActive) { RunEvent runEvent = new RunEvent(RunEventType.MonsterDamaged, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, DamageAmount = damageAmount, Message = "Monster took damage." }; AddTag(runEvent, "monsterId", monsterId); AddTag(runEvent, "monsterName", string.IsNullOrWhiteSpace(monsterName) ? monsterId : monsterName); Record(runEvent); } } public bool TryRecordMonsterKilled(string monsterId, string monsterName, string? levelName = null) { if (string.IsNullOrWhiteSpace(monsterId) || !IsActive || !_killedMonsters.Add(monsterId)) { return false; } RunEvent runEvent = new RunEvent(RunEventType.MonsterKilled, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Message = "Monster killed." }; AddTag(runEvent, "monsterId", monsterId); AddTag(runEvent, "monsterName", string.IsNullOrWhiteSpace(monsterName) ? monsterId : monsterName); Record(runEvent); return true; } public void RecordPlayerDamaged(PlayerRef player, int damageAmount, string? levelName = null) { if (player == null) { throw new ArgumentNullException("player"); } if (damageAmount > 0 && IsActive) { Record(new RunEvent(RunEventType.PlayerDamaged, _clock.UtcNow, ConfidenceLevel.High) { LevelName = levelName, Player = player, DamageAmount = damageAmount, Message = "Player took damage." }); } } public void Record(RunEvent runEvent) { if (runEvent == null) { throw new ArgumentNullException("runEvent"); } _events.Add(runEvent); EventVersion++; this.EventRecorded?.Invoke(runEvent); } public RunReport EndRun(string? levelName = null) { if (_activeRunId == null) { throw new InvalidOperationException("Cannot end a run before StartRun has been called."); } _endedAt = _clock.UtcNow; Record(new RunEvent(RunEventType.LevelEnded, _endedAt.Value, ConfidenceLevel.High) { LevelName = levelName, Message = "Level ended." }); RunReport result = Snapshot(); _activeRunId = null; return result; } public RunReport Snapshot() { if (_activeRunId == null) { throw new InvalidOperationException("No active run is available."); } return new RunReport(_activeRunId, _startedAt, _endedAt, _events.ToArray()); } private void LockAttributionIfAvailable(string runtimeId, PlayerRef? player) { if (player != null && !_lockedValuableAttribution.ContainsKey(runtimeId)) { _lockedValuableAttribution.Add(runtimeId, player); } } private PlayerRef? GetLockedAttribution(string runtimeId) { if (!_lockedValuableAttribution.TryGetValue(runtimeId, out PlayerRef value)) { return null; } return value; } private static ValuableRef Merge(ValuableRef previous, ValuableRef next) { return new ValuableRef(previous.RuntimeId, string.IsNullOrWhiteSpace(next.Name) ? previous.Name : next.Name, next.ValueAtEvent ?? previous.ValueAtEvent, next.WeightAtEvent ?? previous.WeightAtEvent); } private static void AddTag(RunEvent runEvent, string key, string? value) { if (!string.IsNullOrWhiteSpace(value)) { runEvent.Tags[key] = value; } } } public sealed class SystemClock : IClock { public DateTimeOffset UtcNow => DateTimeOffset.UtcNow; } } namespace NuageReport.Core.Model { public enum ConfidenceLevel { Low, Medium, High } public sealed class GlobalReportSummary { public int ObjectsAtStart { get; set; } public int ObjectsAtStartValue { get; set; } public int ObjectsExtracted { get; set; } public int ObjectsExtractedValue { get; set; } public int ObjectsDetected { get; set; } public double ObjectsDetectedPercent { get; set; } public int ObjectsMissed { get; set; } public int ObjectsDamaged { get; set; } public int ObjectsDestroyed { get; set; } public int ObjectsValueLost { get; set; } public int OrbsCollected { get; set; } public int OrbsDestroyed { get; set; } public int OrbsExtractedValue { get; set; } public int OrbsLostValue { get; set; } public int CosmeticObjectsExisting { get; set; } public int CosmeticObjectsFound { get; set; } public int MonsterDamageTaken { get; set; } public int MonstersKilled { get; set; } public int PlayerDamageTaken { get; set; } } public sealed class PlayerRef { public string Id { get; } public string DisplayName { get; } public PlayerRef(string id, string displayName) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Player id is required.", "id"); } Id = id; DisplayName = (string.IsNullOrWhiteSpace(displayName) ? id : displayName); } public override string ToString() { return DisplayName; } } public sealed class PlayerReportSummary { public string PlayerId { get; set; } = string.Empty; public string DisplayName { get; set; } = string.Empty; public int ObjectsPutInCartOrCashout { get; set; } public int ObjectsPutInCartOrCashoutValue { get; set; } public float ExtractedObjectWeight { get; set; } public float AverageExtractedObjectWeight { get; set; } public int AverageExtractedObjectValue { get; set; } public int ObjectsDetected { get; set; } public int ObjectsDetectedValue { get; set; } public int DamageDealt { get; set; } public int DamageTaken { get; set; } } public sealed class RunEvent { public RunEventType Type { get; } public DateTimeOffset Timestamp { get; } public ConfidenceLevel Confidence { get; } public string? LevelName { get; set; } public string? Message { get; set; } public ValuableRef? Valuable { get; set; } public PlayerRef? Player { get; set; } public int? DeltaValue { get; set; } public int? DamageAmount { get; set; } public IDictionary<string, string> Tags { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public RunEvent(RunEventType type, DateTimeOffset timestamp, ConfidenceLevel confidence) { Type = type; Timestamp = timestamp; Confidence = confidence; } } public enum RunEventType { Unknown, LevelStarted, LevelEnded, ValuableSpawned, ValuableValueUpdated, ValuableDiscovered, ValuableFirstCarted, ValuableExtracted, ValuableDamaged, ValuableDestroyed, OrbSpawned, OrbExtracted, OrbDestroyed, CosmeticObjectExisting, CosmeticObjectFound, MonsterDamaged, MonsterKilled, PlayerDamaged, PlayerDied, PlayerDisconnected, ShopEntered, QuotaEvaluated } public sealed class RunReport { public string RunId { get; } public DateTimeOffset StartedAt { get; } public DateTimeOffset? EndedAt { get; } public IReadOnlyList<RunEvent> Events { get; } public RunReportSummary Summary => RunReportSummary.Build(this); public int DiscoveredCount => Events.Count((RunEvent e) => e.Type == RunEventType.ValuableDiscovered); public int FirstCartedCount => Events.Count((RunEvent e) => e.Type == RunEventType.ValuableFirstCarted); public int ExtractedValue => Events.Where((RunEvent e) => e.Type == RunEventType.ValuableExtracted).Sum((RunEvent e) => e.Valuable?.ValueAtEvent ?? e.DeltaValue.GetValueOrDefault()); public int LostOrDestroyedValue => (from e in Events where e.Type == RunEventType.ValuableDestroyed || e.Type == RunEventType.ValuableDamaged where e.DeltaValue.HasValue && e.DeltaValue.Value < 0 select e).Sum((RunEvent e) => -e.DeltaValue.Value); public RunReport(string runId, DateTimeOffset startedAt, DateTimeOffset? endedAt, IReadOnlyList<RunEvent> events) { if (string.IsNullOrWhiteSpace(runId)) { throw new ArgumentException("Run id is required.", "runId"); } RunId = runId; StartedAt = startedAt; EndedAt = endedAt; Events = events ?? throw new ArgumentNullException("events"); } } public sealed class RunReportSummary { private sealed class PlayerAccumulator { public PlayerRef Player { get; } public HashSet<string> CartOrCashoutValuables { get; } = new HashSet<string>(StringComparer.Ordinal); public HashSet<string> ExtractedValuables { get; } = new HashSet<string>(StringComparer.Ordinal); public Dictionary<string, ValuableRef> DetectedValuables { get; } = new Dictionary<string, ValuableRef>(StringComparer.Ordinal); public int DamageTaken { get; set; } public PlayerAccumulator(PlayerRef player) { Player = player; } public PlayerReportSummary ToSummary(Dictionary<string, ValuableRef> latestValuables) { Dictionary<string, ValuableRef> latestValuables2 = latestValuables; int objectsPutInCartOrCashoutValue = CartOrCashoutValuables.Sum((string id) => ValueFor(latestValuables2, id)); int num = ExtractedValuables.Sum((string id) => ValueFor(latestValuables2, id)); float num2 = ExtractedValuables.Sum((string id) => WeightFor(latestValuables2, id)); return new PlayerReportSummary { PlayerId = Player.Id, DisplayName = Player.DisplayName, ObjectsPutInCartOrCashout = CartOrCashoutValuables.Count, ObjectsPutInCartOrCashoutValue = objectsPutInCartOrCashoutValue, ExtractedObjectWeight = num2, AverageExtractedObjectWeight = ((ExtractedValuables.Count == 0) ? 0f : (num2 / (float)ExtractedValuables.Count)), AverageExtractedObjectValue = ((ExtractedValuables.Count != 0) ? (num / ExtractedValuables.Count) : 0), ObjectsDetected = DetectedValuables.Count, ObjectsDetectedValue = DetectedValuables.Values.Sum((ValuableRef v) => v.ValueAtEvent ?? ValueFor(latestValuables2, v.RuntimeId)), DamageDealt = 0, DamageTaken = DamageTaken }; } } public GlobalReportSummary Global { get; } public IReadOnlyList<PlayerReportSummary> Players { get; } public IReadOnlyList<ValuableRef> CosmeticObjectsExisting { get; } public IReadOnlyList<ValuableRef> CosmeticObjectsFound { get; } private RunReportSummary(GlobalReportSummary global, IReadOnlyList<PlayerReportSummary> players, IReadOnlyList<ValuableRef> cosmeticObjectsExisting, IReadOnlyList<ValuableRef> cosmeticObjectsFound) { Global = global; Players = players; CosmeticObjectsExisting = cosmeticObjectsExisting; CosmeticObjectsFound = cosmeticObjectsFound; } public static RunReportSummary Build(RunReport report) { if (report == null) { throw new ArgumentNullException("report"); } Dictionary<string, ValuableRef> initialValuables = new Dictionary<string, ValuableRef>(StringComparer.Ordinal); Dictionary<string, ValuableRef> latestValuables = new Dictionary<string, ValuableRef>(StringComparer.Ordinal); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); HashSet<string> hashSet3 = new HashSet<string>(StringComparer.Ordinal); HashSet<string> hashSet4 = new HashSet<string>(StringComparer.Ordinal); HashSet<string> hashSet5 = new HashSet<string>(StringComparer.Ordinal); HashSet<string> hashSet6 = new HashSet<string>(StringComparer.Ordinal); Dictionary<string, ValuableRef> dictionary = new Dictionary<string, ValuableRef>(StringComparer.Ordinal); Dictionary<string, ValuableRef> dictionary2 = new Dictionary<string, ValuableRef>(StringComparer.Ordinal); HashSet<string> hashSet7 = new HashSet<string>(StringComparer.Ordinal); Dictionary<string, PlayerAccumulator> dictionary3 = new Dictionary<string, PlayerAccumulator>(StringComparer.Ordinal); Dictionary<string, PlayerRef> dictionary4 = new Dictionary<string, PlayerRef>(StringComparer.Ordinal); Dictionary<string, PlayerRef> dictionary5 = new Dictionary<string, PlayerRef>(StringComparer.Ordinal); Dictionary<string, int> dictionary6 = new Dictionary<string, int>(StringComparer.Ordinal); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; foreach (RunEvent @event in report.Events) { if (@event.Valuable != null) { latestValuables[@event.Valuable.RuntimeId] = Merge(latestValuables, @event.Valuable); } switch (@event.Type) { case RunEventType.ValuableSpawned: if (@event.Valuable != null) { initialValuables[@event.Valuable.RuntimeId] = Merge(initialValuables, @event.Valuable); } break; case RunEventType.ValuableValueUpdated: if (@event.Valuable != null && initialValuables.ContainsKey(@event.Valuable.RuntimeId)) { initialValuables[@event.Valuable.RuntimeId] = Merge(initialValuables, @event.Valuable); } break; case RunEventType.ValuableDiscovered: if (@event.Valuable != null) { hashSet2.Add(@event.Valuable.RuntimeId); if (@event.Player != null) { GetPlayer(dictionary3, @event.Player).DetectedValuables[@event.Valuable.RuntimeId] = @event.Valuable; } } break; case RunEventType.ValuableFirstCarted: if (@event.Valuable != null && @event.Player != null) { dictionary4[@event.Valuable.RuntimeId] = @event.Player; } break; case RunEventType.ValuableExtracted: if (@event.Valuable != null) { hashSet.Add(@event.Valuable.RuntimeId); if (@event.Player != null) { dictionary5[@event.Valuable.RuntimeId] = @event.Player; } } break; case RunEventType.ValuableDamaged: if (@event.Valuable != null) { hashSet3.Add(@event.Valuable.RuntimeId); } if (@event.DeltaValue.HasValue && @event.DeltaValue.Value < 0) { AddValueLost(dictionary6, @event.Valuable?.RuntimeId, -@event.DeltaValue.Value); } break; case RunEventType.ValuableDestroyed: if (@event.Valuable != null) { hashSet4.Add(@event.Valuable.RuntimeId); AddValueLost(dictionary6, @event.Valuable.RuntimeId, ValueFor(latestValuables, @event.Valuable.RuntimeId)); } break; case RunEventType.OrbExtracted: if (@event.Valuable != null && hashSet5.Add(@event.Valuable.RuntimeId)) { num += @event.Valuable.ValueAtEvent.GetValueOrDefault(); } break; case RunEventType.OrbDestroyed: if (@event.Valuable != null && hashSet6.Add(@event.Valuable.RuntimeId)) { num2 += @event.Valuable.ValueAtEvent.GetValueOrDefault(); } break; case RunEventType.CosmeticObjectExisting: if (@event.Valuable != null) { dictionary[@event.Valuable.RuntimeId] = Merge(dictionary, @event.Valuable); } break; case RunEventType.CosmeticObjectFound: if (@event.Valuable != null) { dictionary2[@event.Valuable.RuntimeId] = Merge(dictionary2, @event.Valuable); } break; case RunEventType.MonsterDamaged: num3 += @event.DamageAmount.GetValueOrDefault(); break; case RunEventType.MonsterKilled: hashSet7.Add(TagValue(@event, "monsterId", @event.Message ?? @event.Timestamp.ToUnixTimeMilliseconds().ToString())); break; case RunEventType.PlayerDamaged: num4 += @event.DamageAmount.GetValueOrDefault(); if (@event.Player != null) { GetPlayer(dictionary3, @event.Player).DamageTaken += @event.DamageAmount.GetValueOrDefault(); } break; } } foreach (KeyValuePair<string, PlayerRef> item in dictionary4) { GetPlayer(dictionary3, item.Value).CartOrCashoutValuables.Add(item.Key); } foreach (KeyValuePair<string, PlayerRef> item2 in dictionary5) { PlayerRef value; PlayerRef playerRef = (dictionary4.TryGetValue(item2.Key, out value) ? value : item2.Value); PlayerAccumulator player = GetPlayer(dictionary3, playerRef); player.ExtractedValuables.Add(item2.Key); if (!dictionary4.ContainsKey(item2.Key)) { player.CartOrCashoutValuables.Add(item2.Key); } } GlobalReportSummary global = new GlobalReportSummary { ObjectsAtStart = initialValuables.Count, ObjectsAtStartValue = initialValuables.Values.Sum((ValuableRef v) => v.ValueAtEvent.GetValueOrDefault()), ObjectsExtracted = hashSet.Count, ObjectsExtractedValue = hashSet.Sum((string id) => ValueFor(latestValuables, id)), ObjectsDetected = hashSet2.Count, ObjectsDetectedPercent = ((initialValuables.Count == 0) ? 0.0 : Math.Round((double)hashSet2.Count / (double)initialValuables.Count * 100.0, 2)), ObjectsMissed = Math.Max(0, initialValuables.Count - hashSet2.Count), ObjectsDamaged = hashSet3.Count, ObjectsDestroyed = hashSet4.Count, ObjectsValueLost = dictionary6.Sum((KeyValuePair<string, int> pair) => Math.Min(pair.Value, (ValueFor(initialValuables, pair.Key) == 0) ? pair.Value : ValueFor(initialValuables, pair.Key))), OrbsCollected = hashSet5.Count, OrbsDestroyed = hashSet6.Count, OrbsExtractedValue = num, OrbsLostValue = num2, CosmeticObjectsExisting = dictionary.Count, CosmeticObjectsFound = dictionary2.Count, MonsterDamageTaken = num3, MonstersKilled = hashSet7.Count, PlayerDamageTaken = num4 }; List<PlayerReportSummary> players = (from p in dictionary3.Values.OrderBy<PlayerAccumulator, string>((PlayerAccumulator p) => p.Player.DisplayName, StringComparer.OrdinalIgnoreCase) select p.ToSummary(latestValuables)).ToList(); return new RunReportSummary(global, players, dictionary.Values.OrderBy<ValuableRef, string>((ValuableRef v) => v.Name, StringComparer.OrdinalIgnoreCase).ToList(), dictionary2.Values.OrderBy<ValuableRef, string>((ValuableRef v) => v.Name, StringComparer.OrdinalIgnoreCase).ToList()); } private static void AddValueLost(Dictionary<string, int> values, string? runtimeId, int valueLost) { if (!string.IsNullOrWhiteSpace(runtimeId) && valueLost > 0) { values[runtimeId] = (values.TryGetValue(runtimeId, out var value) ? (value + valueLost) : valueLost); } } private static PlayerAccumulator GetPlayer(Dictionary<string, PlayerAccumulator> players, PlayerRef playerRef) { if (!players.TryGetValue(playerRef.Id, out PlayerAccumulator value)) { value = new PlayerAccumulator(playerRef); players.Add(playerRef.Id, value); } return value; } private static ValuableRef Merge(Dictionary<string, ValuableRef> refs, ValuableRef next) { if (!refs.TryGetValue(next.RuntimeId, out ValuableRef value)) { return next; } return new ValuableRef(next.RuntimeId, string.IsNullOrWhiteSpace(next.Name) ? value.Name : next.Name, next.ValueAtEvent ?? value.ValueAtEvent, next.WeightAtEvent ?? value.WeightAtEvent); } private static int ValueFor(Dictionary<string, ValuableRef> refs, string runtimeId) { if (!refs.TryGetValue(runtimeId, out ValuableRef value)) { return 0; } return value.ValueAtEvent.GetValueOrDefault(); } private static float WeightFor(Dictionary<string, ValuableRef> refs, string runtimeId) { if (!refs.TryGetValue(runtimeId, out ValuableRef value)) { return 0f; } return value.WeightAtEvent.GetValueOrDefault(); } private static string TagValue(RunEvent runEvent, string key, string fallback) { if (!runEvent.Tags.TryGetValue(key, out string value) || string.IsNullOrWhiteSpace(value)) { return fallback; } return value; } } public sealed class ValuableRef { public string RuntimeId { get; } public string Name { get; } public int? ValueAtEvent { get; } public float? WeightAtEvent { get; } public ValuableRef(string runtimeId, string name, int? valueAtEvent = null, float? weightAtEvent = null) { if (string.IsNullOrWhiteSpace(runtimeId)) { throw new ArgumentException("Runtime id is required.", "runtimeId"); } RuntimeId = runtimeId; Name = (string.IsNullOrWhiteSpace(name) ? runtimeId : name); ValueAtEvent = valueAtEvent; WeightAtEvent = weightAtEvent; } public override string ToString() { if (!ValueAtEvent.HasValue) { return Name; } return $"{Name} (${ValueAtEvent.Value})"; } } }
NuageReport.Plugin.dll
Decompiled 8 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using NuageReport.Core.Model; using NuageReport.Core.Recording; using NuageReport.Core.Reporting; using NuageReport.Plugin.Config; using NuageReport.Plugin.Infrastructure; using NuageReport.Plugin.Networking; using NuageReport.Plugin.Patches; using NuageReport.Plugin.Reports; using NuageReport.Plugin.UI; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using UnityEngine.UI; [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("TheoHay, GangDesNuages")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f7dcda716f6ccd2361b1be02417714f4cb5ee1f9")] [assembly: AssemblyProduct("NuageReport.Plugin")] [assembly: AssemblyTitle("NuageReport.Plugin")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TheoHay/NuageREPO")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace NuageReport.Plugin { [BepInPlugin("com.theohay.nuagereport", "NuageReport", "1.0.0")] public sealed class NuageReportPlugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <PollingLoop>d__52 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private float <resumeAt>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PollingLoop>d__52(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_0056; } <>1__state = -1; LogVerbose("NuageReport polling coroutine started."); goto IL_0021; IL_0056: if (Time.unscaledTime < <resumeAt>5__2) { <>2__current = null; <>1__state = 1; return true; } goto IL_0021; IL_0021: TickSurvivingSystems("Plugin.PollingLoop", forcePoll: true); <resumeAt>5__2 = Time.unscaledTime + 0.5f; goto IL_0056; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string PluginGuid = "com.theohay.nuagereport"; public const string PluginName = "NuageReport"; public const string PluginVersion = "1.0.0"; private const float InitialReportCatchupDelaySeconds = 3f; private const float ReportCatchupIntervalSeconds = 2f; private Harmony? _harmony; private static bool _initialObjectBackfillDone; private static readonly PollingRecorder SharedPollingRecorder = new PollingRecorder(); private static readonly ReportSnapshotFactory SnapshotFactory = new ReportSnapshotFactory(); private static GameObject? _runnerObject; private static float _nextLiveReportSyncAt; private static float _nextReportCatchupRequestAt; private static string? _lastLiveReportRunId; private static int _lastLiveReportVersionPublished = -1; private bool _isQuitting; internal static ManualLogSource Log { get; private set; } = null; internal static RunRecorder Recorder { get; private set; } = null; internal static ReportExporter Exporter { get; private set; } = null; internal static ReportSyncService? ReportSync { get; private set; } internal static ReportUiController? ReportUi { get; private set; } internal static bool CanRecord => PatchSafety.Try("NuageReport Plugin.CanRecord", () => PluginConfig.Instance.EnableRecording != null && PluginConfig.Instance.IsModActive() && HostAuthority.IsAuthoritativeHost(), fallback: false); private void Awake() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_024c: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); PluginConfig.Instance.Bind(((BaseUnityPlugin)this).Config); PluginPaths.Initialize("NuageReport"); Recorder = new RunRecorder((IClock)new SystemClock()); Recorder.EventRecorded += LogRecordedEvent; Exporter = new ReportExporter(); ReportSync = new ReportSyncService(); ReportUi = new ReportUiController(ReportSync, CreateLiveReportSnapshot); ReportSync.FinalReportReceived += HandleFinalReportReceived; ReportSync.LiveReportReceived += HandleLiveReportReceived; ReportUi.Initialize(); _harmony = new Harmony("com.theohay.nuagereport"); WriteStartupMarker(); InstallPatchFamilies(); Log.LogInfo((object)("NuageReport 1.0.0 loaded. " + $"Recording={PluginConfig.Instance.EnableRecording.Value}, " + $"JsonOutput={PluginConfig.Instance.EnableJsonOutput.Value}, " + "RecordingAuthority=Host, " + $"LiveReport={PluginConfig.Instance.IsLiveReportEnabled()}, " + $"ShareReports={PluginConfig.Instance.ShouldShareReports()}, " + $"VerboseLogging={PluginConfig.Instance.ShouldLogVerbose()}, " + $"LogHookHits={PluginConfig.Instance.LogHookHits.Value}, " + $"LogRecordedEvents={PluginConfig.Instance.LogRecordedEvents.Value}, " + $"CartConfirmation={PluginConfig.Instance.CartConfirmationSeconds.Value:0.###}s, " + $"LiveReportSyncInterval={EffectiveLiveReportSyncIntervalSeconds():0.###}s, " + $"ReportMenuKeybind={PluginConfig.Instance.EffectiveReportMenuShortcut()}, " + "Reports path: " + PluginPaths.ReportsDirectory)); CreatePollingRunner(); ((MonoBehaviour)this).StartCoroutine(PollingLoop()); LogVerbose("Polling coroutine requested from Awake."); } private void CreatePollingRunner() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown try { if ((Object)(object)_runnerObject != (Object)null) { LogVerbose("NuageReport polling runner already exists."); return; } _runnerObject = new GameObject("NuageReport Polling Runner"); _runnerObject.AddComponent<PollingRunner>(); LogVerbose("NuageReport polling runner GameObject created."); } catch (Exception arg) { Log.LogError((object)$"Failed to create NuageReport polling runner: {arg}"); } } private void OnEnable() { LogVerbose("NuageReport OnEnable reached."); } private void Start() { LogVerbose("NuageReport Start reached."); } private void InstallPatchFamilies() { if (_harmony == null) { return; } Type[] array = new Type[6] { typeof(RunLifecyclePatches), typeof(ValuableLifecyclePatches), typeof(CosmeticObjectLifecyclePatches), typeof(CartDetectionPatches), typeof(DamagePatches), typeof(InputManagerPatches) }; int num = 0; Type[] array2 = array; foreach (Type type in array2) { string name = type.Name; try { int num2 = CountInstalledPatches(); _harmony.PatchAll(type); int num3 = CountInstalledPatches(); num++; Log.LogInfo((object)$"Installed patch family: {name} ({num3 - num2} patch methods, {num3} total owned patch methods)."); } catch (Exception arg) { Log.LogError((object)$"Failed to install patch family '{name}'. Other patch families will still be attempted. {arg}"); } } Log.LogInfo((object)$"Installed {num}/{array.Length} NuageReport patch families."); } private static void WriteStartupMarker() { try { Directory.CreateDirectory(PluginPaths.RootDirectory); File.WriteAllText(PluginPaths.StartupMarkerPath, string.Format("{0} {1} reached Awake at {2:O}.{3}", "NuageReport", "1.0.0", DateTimeOffset.UtcNow, Environment.NewLine) + $"JSON output enabled: {PluginConfig.Instance.EnableJsonOutput.Value}{Environment.NewLine}" + "Reports path: " + PluginPaths.ReportsDirectory + Environment.NewLine); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"Failed to write startup marker: {arg}"); } } } private void OnDestroy() { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"NuageReport OnDestroy reached. isQuitting={_isQuitting}. Keeping patches/runner alive unless the app is quitting."); } if (!_isQuitting) { CreatePollingRunner(); return; } try { RunRecorder recorder = Recorder; if (recorder != null && recorder.IsActive) { EndRunIfActive(null); } if (recorder != null) { recorder.EventRecorded -= LogRecordedEvent; } if (ReportSync != null) { ReportSync.FinalReportReceived -= HandleFinalReportReceived; ReportSync.LiveReportReceived -= HandleLiveReportReceived; ReportSync.Dispose(); } if ((Object)(object)_runnerObject != (Object)null) { Object.Destroy((Object)(object)_runnerObject); _runnerObject = null; } Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception arg) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)$"Failed to unpatch Harmony cleanly: {arg}"); } } } private void OnApplicationQuit() { _isQuitting = true; LogVerbose("NuageReport OnApplicationQuit reached."); } private void Update() { TickSurvivingSystems("Plugin.Update"); } private void OnGUI() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (PluginConfig.Instance.IsModActive() && current != null && (int)current.type == 4 && ReportUi != null && ReportUi.HandleKeyEvent(current.keyCode, "Plugin.OnGUI")) { current.Use(); } } internal static void TickSurvivingSystems(string source, bool forcePoll = false) { PatchSafety.LogHookHit("NuageReport TickSurvivingSystems", source, 3); if (!PluginConfig.Instance.IsModActive()) { ReportUi?.Deactivate(); ReportSync?.Dispose(); return; } ReportUi?.Tick(source); EnsureReportSyncSubscribedIfSafe(); RequestReportCatchupIfDue(); PublishLiveReportIfDue(); SharedPollingRecorder.Tick(forcePoll); } private static void EnsureReportSyncSubscribedIfSafe() { if (ReportSync != null && PluginConfig.Instance.ShouldShareReports()) { string levelName = RunLifecyclePatches.CurrentLevelName(); if (RunLifecyclePatches.IsRecordableLevel(levelName)) { ReportSync.EnsureSubscribed(); } } } private static void PublishLiveReportIfDue() { if (ReportSync == null || !PluginConfig.Instance.ShouldShareLiveReport() || !Recorder.IsActive || !ReportSync.IsRoomReady || !CanRecord || !HostAuthority.IsAuthoritativeHost()) { return; } float num = EffectiveLiveReportSyncIntervalSeconds(); if (Time.unscaledTime < _nextLiveReportSyncAt) { return; } string activeRunId = Recorder.ActiveRunId; int eventVersion = Recorder.EventVersion; if (_lastLiveReportRunId == activeRunId && _lastLiveReportVersionPublished == eventVersion) { _nextLiveReportSyncAt = Time.unscaledTime + num; return; } _nextLiveReportSyncAt = Time.unscaledTime + num; ReportSnapshot reportSnapshot = CreateLiveReportSnapshot(); if (reportSnapshot != null) { ReportSync.PublishLiveReport(reportSnapshot); _lastLiveReportRunId = activeRunId; _lastLiveReportVersionPublished = eventVersion; } } private static void RequestReportCatchupIfDue() { if (ReportSync == null || !PluginConfig.Instance.ShouldShareReports()) { return; } string levelName = RunLifecyclePatches.CurrentLevelName(); if (!RunLifecyclePatches.IsRecordableLevel(levelName) || !ReportSync.IsRoomReady || HostAuthority.IsAuthoritativeHost()) { return; } bool flag = ReportSync.LatestFinalReport == null; bool flag2 = PluginConfig.Instance.IsLiveReportEnabled() && ReportSync.LatestLiveReport == null; if (flag || flag2) { if (_nextReportCatchupRequestAt <= 0f) { _nextReportCatchupRequestAt = Time.unscaledTime + 3f; } else if (!(Time.unscaledTime < _nextReportCatchupRequestAt)) { _nextReportCatchupRequestAt = Time.unscaledTime + 2f; ReportSync.RequestLatestReport(flag, flag2); } } } private static float EffectiveLiveReportSyncIntervalSeconds() { return Mathf.Clamp(PluginConfig.Instance.LiveReportSyncIntervalSeconds.Value, 0.5f, 10f); } [IteratorStateMachine(typeof(<PollingLoop>d__52))] private IEnumerator PollingLoop() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <PollingLoop>d__52(0); } private static void HandleFinalReportReceived(ReportSnapshot snapshot) { ReportUi?.SetLatestFinalReport(snapshot); Log.LogInfo((object)("Received NuageReport final report sync. runId=" + snapshot.RunId + " level=" + Quote(snapshot.LevelName ?? "unknown") + " modVersion=" + snapshot.ModVersion)); } private static void HandleLiveReportReceived(ReportSnapshot snapshot) { ReportUi?.SetLatestLiveReport(snapshot); } private static ReportSnapshot? CreateLiveReportSnapshot() { return PatchSafety.Try("NuageReport Plugin.CreateLiveReportSnapshot", () => (!Recorder.IsActive) ? null : SnapshotFactory.CreateLiveReport(Recorder.Snapshot(), "1.0.0"), null); } internal static void StartRunIfNeeded(string? levelName) { if (CanRecord && !Recorder.IsActive) { if (!RunLifecyclePatches.IsRecordableLevel(levelName)) { PatchSafety.LogHookHit("NuageReportPlugin.StartRunIfNeeded", "ignored non-run level=" + (levelName ?? "unknown")); return; } string text = DateTimeOffset.UtcNow.ToString("yyyyMMdd-HHmmssfff"); _initialObjectBackfillDone = false; Recorder.StartRun(text, levelName); Log.LogInfo((object)("Run recording started. runId=" + text + " level=" + Quote(levelName ?? "unknown"))); } } internal static bool EnsureRunActiveFromHook(string hookName) { if (!CanRecord) { PatchSafety.LogHookHit(hookName, "recording gate is closed"); return false; } string text = RunLifecyclePatches.CurrentLevelName(); if (!RunLifecyclePatches.IsRecordableLevel(text)) { PatchSafety.LogHookHit(hookName, "ignored non-run level=" + (text ?? "unknown")); return false; } if (!Recorder.IsActive) { LogVerbose("No active run when " + hookName + " fired; starting a fallback run."); StartRunIfNeeded(text); BackfillInitialObjectsIfAvailable(hookName); } return Recorder.IsActive; } internal static void BackfillInitialObjectsIfAvailable(string source) { if (_initialObjectBackfillDone || !Recorder.IsActive) { return; } RoundDirector instance = RoundDirector.instance; if ((Object)(object)instance == (Object)null) { LogVerbose("Initial object backfill skipped from " + source + ": RoundDirector.instance is null."); return; } int num = 0; int num2 = 0; List<PhysGrabObject> list = GameAccess.Field<List<PhysGrabObject>>(instance, "physGrabObjects"); if (list != null) { foreach (PhysGrabObject item in list) { if (ValuableLifecyclePatches.RecordPhysGrabObject(item)) { num++; } } } List<CosmeticWorldObject> list2 = GameAccess.Field<List<CosmeticWorldObject>>(instance, "cosmeticWorldObjects"); if (list2 != null) { foreach (CosmeticWorldObject item2 in list2) { if (CosmeticObjectLifecyclePatches.RecordCosmeticObjectExisting(item2)) { num2++; } } } _initialObjectBackfillDone = true; LogVerbose($"Initial object backfill from {source}: valuables={num}, cosmeticObjects={num2}."); } internal static void EndRunIfActive(string? levelName) { if (!Recorder.IsActive) { return; } try { RunReport val = Recorder.EndRun(levelName); if (!ReportContainsRecordableLevel(val, levelName)) { _initialObjectBackfillDone = false; Log.LogInfo((object)("Discarded non-run report. runId=" + val.RunId + " level=" + Quote(ReportLevelName(val, levelName) ?? "unknown") + ".")); } else { string text = ExportJsonReportStatus(val); ReportSnapshot reportSnapshot = SnapshotFactory.CreateFinalReport(val, "1.0.0"); ReportUi?.SetLatestFinalReport(reportSnapshot); ReportSync?.PublishFinalReport(reportSnapshot); _initialObjectBackfillDone = false; GlobalReportSummary global = val.Summary.Global; Log.LogInfo((object)(text + " " + $"Objects {global.ObjectsExtracted}/{global.ObjectsAtStart}, " + $"value ${global.ObjectsExtractedValue}/${global.ObjectsAtStartValue}, " + $"lost ${global.ObjectsValueLost}.")); } } catch (Exception arg) { Log.LogError((object)$"Failed to end/export run report: {arg}"); } } private static string ExportJsonReportStatus(RunReport report) { if (!PluginConfig.Instance.ShouldWriteJsonOutput()) { return "Run report completed with JSON output disabled."; } try { string text = Exporter.Export(report); return "Run report exported: " + text + "."; } catch (Exception arg) { Log.LogError((object)$"Failed to export JSON run report: {arg}"); return "Run report completed, but JSON export failed."; } } private static bool ReportContainsRecordableLevel(RunReport report, string? fallbackLevelName) { if (RunLifecyclePatches.IsRecordableLevel(fallbackLevelName)) { return true; } foreach (RunEvent @event in report.Events) { if (RunLifecyclePatches.IsRecordableLevel(@event.LevelName)) { return true; } } return false; } private static string? ReportLevelName(RunReport report, string? fallbackLevelName) { if (!string.IsNullOrWhiteSpace(fallbackLevelName)) { return fallbackLevelName; } foreach (RunEvent @event in report.Events) { if (!string.IsNullOrWhiteSpace(@event.LevelName)) { return @event.LevelName; } } return null; } private static void LogRecordedEvent(RunEvent runEvent) { RunEvent runEvent2 = runEvent; if (PluginConfig.Instance.LogRecordedEvents != null && !PluginConfig.Instance.LogRecordedEvents.Value) { return; } PatchSafety.Run("Recorder.EventRecordedLog", delegate { //IL_0017: 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) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Recorded ").Append(runEvent2.Type).Append(" [") .Append(runEvent2.Confidence) .Append(']'); if (!string.IsNullOrWhiteSpace(runEvent2.LevelName)) { stringBuilder.Append(" level=").Append(Quote(runEvent2.LevelName)); } if (runEvent2.Valuable != null) { stringBuilder.Append(" valuable=").Append(Quote(runEvent2.Valuable.Name)).Append(" valuableId=") .Append(runEvent2.Valuable.RuntimeId); if (runEvent2.Valuable.ValueAtEvent.HasValue) { stringBuilder.Append(" value=$").Append(runEvent2.Valuable.ValueAtEvent.Value); } if (runEvent2.Valuable.WeightAtEvent.HasValue) { stringBuilder.Append(" weight=").Append(runEvent2.Valuable.WeightAtEvent.Value.ToString("0.###")); } } if (runEvent2.Player != null) { stringBuilder.Append(" player=").Append(Quote(runEvent2.Player.DisplayName)).Append(" playerId=") .Append(runEvent2.Player.Id); } if (runEvent2.DamageAmount.HasValue) { stringBuilder.Append(" damage=").Append(runEvent2.DamageAmount.Value); } if (runEvent2.DeltaValue.HasValue) { stringBuilder.Append(" deltaValue=").Append(runEvent2.DeltaValue.Value); } foreach (KeyValuePair<string, string> tag in runEvent2.Tags) { stringBuilder.Append(' ').Append(tag.Key).Append('=') .Append(Quote(tag.Value)); } if (!string.IsNullOrWhiteSpace(runEvent2.Message)) { stringBuilder.Append(" note=").Append(Quote(runEvent2.Message)); } Log.LogInfo((object)stringBuilder.ToString()); }); } private static string Quote(string value) { return "\"" + value.Replace("\"", "'") + "\""; } internal static void LogVerbose(string message) { if (PluginConfig.Instance.ShouldLogVerbose()) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } } } private static int CountInstalledPatches() { int num = 0; foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods()) { Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod); if (patchInfo != null) { num += CountOwnedPatches(patchInfo.Prefixes); num += CountOwnedPatches(patchInfo.Postfixes); num += CountOwnedPatches(patchInfo.Transpilers); num += CountOwnedPatches(patchInfo.Finalizers); } } return num; } private static int CountOwnedPatches(IEnumerable<Patch> patches) { int num = 0; foreach (Patch patch in patches) { if (patch.owner == "com.theohay.nuagereport") { num++; } } return num; } } } namespace NuageReport.Plugin.UI { internal sealed class ReportOverlayPage : MonoBehaviour { private sealed class Metric { public string Label { get; } public string Value { get; } public string Detail { get; } public Metric(string label, string value, string detail) { Label = label; Value = value; Detail = detail; } } private sealed class ButtonVisual { public Button Button { get; } public Image Background { get; } public TextMeshProUGUI Label { get; } public ButtonVisual(Button button, Image background, TextMeshProUGUI label) { Button = button; Background = background; Label = label; } } private const float FallbackFontSize = 18f; private const float HeaderHeight = 64f; private const float FooterHeight = 42f; private const float StackSpacing = 12f; private RectTransform? _contentRect; private RectTransform? _viewportRect; private RectTransform? _scrollTrackRect; private RectTransform? _scrollThumbRect; private Image? _scrollTrackImage; private Image? _scrollThumbImage; private TextMeshProUGUI? _titleText; private TextMeshProUGUI? _footerText; private TextMeshProUGUI? _fallbackText; private LayoutElement? _fallbackLayout; private ButtonVisual? _summaryTab; private ButtonVisual? _liveTab; private Action<ReportUiTab>? _tabSelected; private Action? _closed; private bool _isClosing; private bool _layoutDirty; private float _scrollOffset; private float _maxScrollOffset; private CursorLockMode _previousCursorLockState; private bool _previousCursorVisible; private ReportUiTab _selectedTab; private bool _summaryEnabled; private bool _liveEnabled; public static ReportOverlayPage Create(ReportUiTab selectedTab, bool summaryEnabled, bool liveEnabled, ReportSnapshot? snapshot, string fallbackText, string footer, Action<ReportUiTab> tabSelected, Action closed) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("NuageReport Report Overlay"); try { Object.DontDestroyOnLoad((Object)(object)val); Canvas val2 = val.AddComponent<Canvas>(); val2.renderMode = (RenderMode)0; val2.sortingOrder = 6000; CanvasScaler val3 = val.AddComponent<CanvasScaler>(); val3.uiScaleMode = (ScaleMode)1; val3.referenceResolution = new Vector2(1920f, 1080f); val3.matchWidthOrHeight = 0.5f; val.AddComponent<GraphicRaycaster>(); CanvasGroup val4 = val.AddComponent<CanvasGroup>(); val4.alpha = 1f; val4.interactable = true; val4.blocksRaycasts = true; ReportOverlayPage reportOverlayPage = val.AddComponent<ReportOverlayPage>(); reportOverlayPage._tabSelected = tabSelected; reportOverlayPage._closed = closed; reportOverlayPage.CaptureCursorState(); reportOverlayPage.Build(val.transform); reportOverlayPage.SetContent(selectedTab, summaryEnabled, liveEnabled, snapshot, fallbackText, footer); return reportOverlayPage; } catch { Object.Destroy((Object)(object)val); throw; } } public void SetContent(ReportUiTab selectedTab, bool summaryEnabled, bool liveEnabled, ReportSnapshot? snapshot, string fallbackText, string footer) { bool flag = _selectedTab != selectedTab; _selectedTab = selectedTab; _summaryEnabled = summaryEnabled; _liveEnabled = liveEnabled; _fallbackText = null; _fallbackLayout = null; if ((Object)(object)_titleText != (Object)null) { ((TMP_Text)_titleText).text = "NuageReport"; } if ((Object)(object)_footerText != (Object)null) { ((TMP_Text)_footerText).text = footer; } ClearContent(); if (snapshot != null && ReportUiData.TryParse(snapshot.JsonPayload, out ReportUiData data) && data != null) { BuildStructuredContent(snapshot, data); } else { BuildFallbackContent(string.IsNullOrWhiteSpace(fallbackText) ? "No report data." : fallbackText); } if (flag) { _scrollOffset = 0f; } UpdateTabVisuals(); _layoutDirty = true; } public void Close() { if (!_isClosing) { _isClosing = true; _closed?.Invoke(); Object.Destroy((Object)(object)((Component)this).gameObject); } } private void Build(Transform root) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_0107: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0260: 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_0295: Expected O, but got Unknown //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Expected O, but got Unknown //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Expected O, but got Unknown //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Expected O, but got Unknown //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0445: Unknown result type (might be due to invalid IL or missing references) //IL_045f: Unknown result type (might be due to invalid IL or missing references) //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Expected O, but got Unknown //IL_04ae: Unknown result type (might be due to invalid IL or missing references) //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_04e2: Unknown result type (might be due to invalid IL or missing references) //IL_04f2: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Unknown result type (might be due to invalid IL or missing references) //IL_0559: Unknown result type (might be due to invalid IL or missing references) //IL_0563: Expected O, but got Unknown //IL_0579: Unknown result type (might be due to invalid IL or missing references) //IL_057e: Unknown result type (might be due to invalid IL or missing references) //IL_058d: Unknown result type (might be due to invalid IL or missing references) //IL_059c: Unknown result type (might be due to invalid IL or missing references) //IL_05dc: Unknown result type (might be due to invalid IL or missing references) //IL_05fc: Unknown result type (might be due to invalid IL or missing references) //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_0606: Unknown result type (might be due to invalid IL or missing references) //IL_060b: Unknown result type (might be due to invalid IL or missing references) //IL_0634: Unknown result type (might be due to invalid IL or missing references) //IL_064e: Unknown result type (might be due to invalid IL or missing references) //IL_0668: Unknown result type (might be due to invalid IL or missing references) //IL_0682: Unknown result type (might be due to invalid IL or missing references) //IL_06b3: Unknown result type (might be due to invalid IL or missing references) //IL_06c8: Unknown result type (might be due to invalid IL or missing references) //IL_06d7: Unknown result type (might be due to invalid IL or missing references) //IL_06dc: Unknown result type (might be due to invalid IL or missing references) //IL_06eb: Unknown result type (might be due to invalid IL or missing references) //IL_0712: Unknown result type (might be due to invalid IL or missing references) //IL_074c: Unknown result type (might be due to invalid IL or missing references) //IL_0758: Unknown result type (might be due to invalid IL or missing references) //IL_076e: Unknown result type (might be due to invalid IL or missing references) //IL_0784: Unknown result type (might be due to invalid IL or missing references) //IL_07a8: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreatePanel(root, "Background", Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero); ((Graphic)val.GetComponent<Image>()).color = new Color(0.01f, 0.012f, 0.014f, 0.88f); GameObject val2 = CreatePanel(root, "Window", Vector2.zero, Vector2.one, new Vector2(64f, 54f), new Vector2(-64f, -54f)); Image component = val2.GetComponent<Image>(); ((Graphic)component).color = new Color(0.035f, 0.043f, 0.048f, 0.96f); Outline val3 = val2.AddComponent<Outline>(); ((Shadow)val3).effectColor = new Color(0.23f, 0.34f, 0.37f, 0.62f); ((Shadow)val3).effectDistance = new Vector2(1f, -1f); GameObject val4 = CreatePanel(val2.transform, "Top Bar", new Vector2(0f, 1f), Vector2.one, new Vector2(0f, -64f), Vector2.zero); ((Graphic)val4.GetComponent<Image>()).color = new Color(0.055f, 0.067f, 0.072f, 0.98f); _titleText = CreateText(val4.transform, "Title", 22f, (TextAlignmentOptions)4097); RectTransform rectTransform = ((TMP_Text)_titleText).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0f, 1f); rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.anchoredPosition = new Vector2(24f, 0f); rectTransform.sizeDelta = new Vector2(250f, 0f); ((TMP_Text)_titleText).fontStyle = (FontStyles)1; ((Graphic)_titleText).color = new Color(0.93f, 0.98f, 1f, 1f); ((TMP_Text)_titleText).enableWordWrapping = false; ((TMP_Text)_titleText).overflowMode = (TextOverflowModes)1; _summaryTab = CreateButton(val4.transform, "Summary", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(318f, 0f), new Vector2(126f, 38f), 16f); ((UnityEvent)_summaryTab.Button.onClick).AddListener((UnityAction)delegate { _tabSelected?.Invoke(ReportUiTab.Summary); }); _liveTab = CreateButton(val4.transform, "Live", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(450f, 0f), new Vector2(104f, 38f), 16f); ((UnityEvent)_liveTab.Button.onClick).AddListener((UnityAction)delegate { _tabSelected?.Invoke(ReportUiTab.Live); }); ButtonVisual buttonVisual = CreateButton(val4.transform, "X", new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-38f, 0f), new Vector2(44f, 38f), 18f); ((UnityEvent)buttonVisual.Button.onClick).AddListener(new UnityAction(Close)); ApplyButtonStyle(buttonVisual, enabled: true, selected: false, compact: true); GameObject val5 = CreatePanel(val2.transform, "Content", Vector2.zero, Vector2.one, new Vector2(24f, 60f), new Vector2(-24f, -82f)); ((Graphic)val5.GetComponent<Image>()).color = new Color(0.018f, 0.023f, 0.026f, 0.92f); GameObject val6 = new GameObject("Viewport"); val6.transform.SetParent(val5.transform, false); _viewportRect = val6.AddComponent<RectTransform>(); _viewportRect.anchorMin = Vector2.zero; _viewportRect.anchorMax = Vector2.one; _viewportRect.offsetMin = new Vector2(22f, 16f); _viewportRect.offsetMax = new Vector2(-38f, -16f); val6.AddComponent<RectMask2D>(); GameObject val7 = new GameObject("Content Stack"); val7.transform.SetParent(val6.transform, false); _contentRect = val7.AddComponent<RectTransform>(); _contentRect.anchorMin = new Vector2(0f, 1f); _contentRect.anchorMax = new Vector2(1f, 1f); _contentRect.pivot = new Vector2(0f, 1f); _contentRect.anchoredPosition = Vector2.zero; _contentRect.sizeDelta = new Vector2(0f, 800f); VerticalLayoutGroup val8 = val7.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)val8).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)val8).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val8).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)val8).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val8).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val8).spacing = 12f; ((LayoutGroup)val8).padding = new RectOffset(0, 0, 0, 0); GameObject val9 = CreatePanel(val5.transform, "Scroll Track", new Vector2(1f, 0f), Vector2.one, new Vector2(-17f, 18f), new Vector2(-12f, -18f)); _scrollTrackRect = val9.GetComponent<RectTransform>(); _scrollTrackImage = val9.GetComponent<Image>(); ((Graphic)_scrollTrackImage).color = new Color(0.16f, 0.21f, 0.23f, 0.55f); GameObject val10 = CreatePanel(val9.transform, "Scroll Thumb", new Vector2(0f, 1f), Vector2.one, Vector2.zero, Vector2.zero); _scrollThumbRect = val10.GetComponent<RectTransform>(); _scrollThumbRect.anchorMin = new Vector2(0f, 1f); _scrollThumbRect.anchorMax = new Vector2(1f, 1f); _scrollThumbRect.pivot = new Vector2(0.5f, 1f); _scrollThumbRect.sizeDelta = new Vector2(0f, 80f); _scrollThumbImage = val10.GetComponent<Image>(); ((Graphic)_scrollThumbImage).color = new Color(0.45f, 0.72f, 0.74f, 0.9f); GameObject val11 = CreatePanel(val2.transform, "Footer", Vector2.zero, new Vector2(1f, 0f), Vector2.zero, new Vector2(0f, 42f)); ((Graphic)val11.GetComponent<Image>()).color = new Color(0.055f, 0.067f, 0.072f, 0.98f); _footerText = CreateText(val11.transform, "Footer Text", 13f, (TextAlignmentOptions)514); RectTransform rectTransform2 = ((TMP_Text)_footerText).rectTransform; rectTransform2.anchorMin = Vector2.zero; rectTransform2.anchorMax = Vector2.one; rectTransform2.offsetMin = new Vector2(24f, 0f); rectTransform2.offsetMax = new Vector2(-24f, 0f); ((Graphic)_footerText).color = new Color(0.66f, 0.78f, 0.8f, 1f); ((TMP_Text)_footerText).enableWordWrapping = false; ((TMP_Text)_footerText).overflowMode = (TextOverflowModes)1; } private void BuildStructuredContent(ReportSnapshot snapshot, ReportUiData data) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_contentRect == (Object)null || data.global == null) { return; } ReportUiGlobal global = data.global; ReportUiPlayer[] array = data.players ?? Array.Empty<ReportUiPlayer>(); ReportUiCosmeticObjects cosmeticObjects = data.cosmeticObjects; string title = ((snapshot.ReportKind == ReportKind.Live) ? "Live Report" : "Post Game Summary"); string state = ((snapshot.ReportKind == ReportKind.Live) ? "In progress" : "Complete"); CreateHero(title, state, snapshot.LevelName); GameObject val = CreateHorizontalGroup("KPI Row", 94f, 10f); AddStatTile(val.transform, "Extracted value", Money(global.objectsExtractedValue), CountDetail(global.objectsExtracted, "object"), new Color(0.22f, 0.64f, 0.42f, 1f)); AddStatTile(val.transform, "Value lost", Money(global.objectsValueLost), CountDetail(global.objectsDestroyed, "destroyed"), new Color(0.74f, 0.28f, 0.25f, 1f)); AddStatTile(val.transform, "Detected", Percent(global.objectsDetectedPercent), global.objectsDetected + " / " + global.objectsAtStart, new Color(0.33f, 0.56f, 0.86f, 1f)); AddStatTile(val.transform, "Players hurt", Number(global.playerDamageTaken), "total damage", new Color(0.77f, 0.56f, 0.26f, 1f)); CreateSectionTitle("Valuables"); CreateMetricRows(new Metric("Map value", Money(global.objectsAtStartValue), CountDetail(global.objectsAtStart, "object")), new Metric("Extracted", Money(global.objectsExtractedValue), CountDetail(global.objectsExtracted, "object")), new Metric("Missed", Number(global.objectsMissed), "not detected"), new Metric("Damaged", Number(global.objectsDamaged), "valuable objects"), new Metric("Destroyed", Number(global.objectsDestroyed), Money(global.objectsValueLost) + " lost"), new Metric("Detection", Percent(global.objectsDetectedPercent), global.objectsDetected + " found")); CreateSectionTitle("Extras and Combat"); CreateMetricRows(new Metric("Monster loot", Money(global.orbsExtractedValue), CountDetail(global.orbsCollected, "orb")), new Metric("Loot lost", Money(global.orbsLostValue), CountDetail(global.orbsDestroyed, "orb")), new Metric("Cosmetics found", Number(global.cosmeticObjectsFound), CountDetail(global.cosmeticObjectsExisting, "known")), new Metric("Monsters killed", Number(global.monstersKilled), Number(global.monsterDamageTaken) + " damage"), new Metric("Player damage", Number(global.playerDamageTaken), "taken by crew")); CreateSectionTitle("Players"); if (array.Length == 0) { CreateEmptyState("No player-specific stats recorded yet."); } else { ReportUiPlayer[] array2 = array; foreach (ReportUiPlayer player in array2) { CreatePlayerRow(player); } } int num = cosmeticObjects?.existing?.Length ?? global.cosmeticObjectsExisting; int num2 = cosmeticObjects?.found?.Length ?? global.cosmeticObjectsFound; if (num > 0 || num2 > 0) { CreateSectionTitle("Cosmetic Objects"); CreateMetricRows(new Metric("Existing", Number(num), "seen by recorder"), new Metric("Found", Number(num2), "extracted or found")); } } private void BuildFallbackContent(string text) { if ((Object)(object)_contentRect == (Object)null) { return; } string text2 = text.Replace("\r\n", "\n"); CreateHero("Text Report", "Compatibility fallback", null); string[] array = text2.Split('\n'); if (array.Length <= 1) { CreateEmptyState(text2); return; } string title = "Report"; List<string> list = new List<string>(); for (int i = 0; i < array.Length; i++) { string text3 = array[i].TrimEnd(); if (IsLegacySectionHeader(array, i)) { FlushFallbackSection(title, list); list.Clear(); title = TitleCase(text3); i++; } else if (!string.IsNullOrWhiteSpace(text3)) { list.Add(text3); } } FlushFallbackSection(title, list); } private void FlushFallbackSection(string title, List<string> lines) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown if (lines.Count == 0) { return; } CreateSectionTitle(title); GameObject val = CreateContentPanel(title + " Fallback Rows", Math.Max(54f, 16f + (float)lines.Count * 34f), new Color(0.03f, 0.04f, 0.045f, 0.95f)); VerticalLayoutGroup val2 = val.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)val2).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val2).spacing = 0f; ((LayoutGroup)val2).padding = new RectOffset(14, 14, 8, 8); foreach (string line in lines) { AddFallbackLine(val.transform, line); } } private void AddFallbackLine(Transform parent, string line) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Fallback Row"); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; LayoutElement val3 = val.AddComponent<LayoutElement>(); val3.preferredHeight = 34f; val3.minHeight = 34f; int num = line.IndexOf(':'); if (num <= 0) { TextMeshProUGUI val4 = CreateText(val.transform, "Text", 15f, (TextAlignmentOptions)4097); ((TMP_Text)val4).rectTransform.offsetMin = Vector2.zero; ((TMP_Text)val4).rectTransform.offsetMax = Vector2.zero; ((TMP_Text)val4).text = line.Trim(); ((TMP_Text)val4).fontStyle = (FontStyles)((!line.StartsWith(" ", StringComparison.Ordinal)) ? 1 : 0); ((Graphic)val4).color = new Color(0.86f, 0.94f, 0.95f, 1f); ((TMP_Text)val4).enableWordWrapping = false; ((TMP_Text)val4).overflowMode = (TextOverflowModes)1; } else { string text = line.Substring(0, num).Trim(); string text2 = line.Substring(num + 1).Trim(); TextMeshProUGUI val5 = CreateText(val.transform, "Label", 13f, (TextAlignmentOptions)4097); SetAnchors(((TMP_Text)val5).rectTransform, Vector2.zero, new Vector2(0.48f, 1f), Vector2.zero, new Vector2(-8f, 0f)); ((TMP_Text)val5).text = text; ((Graphic)val5).color = new Color(0.62f, 0.72f, 0.74f, 1f); ((TMP_Text)val5).enableWordWrapping = false; ((TMP_Text)val5).overflowMode = (TextOverflowModes)1; TextMeshProUGUI val6 = CreateText(val.transform, "Value", 15f, (TextAlignmentOptions)4100); SetAnchors(((TMP_Text)val6).rectTransform, new Vector2(0.48f, 0f), Vector2.one, new Vector2(8f, 0f), Vector2.zero); ((TMP_Text)val6).text = text2; ((TMP_Text)val6).fontStyle = (FontStyles)1; ((Graphic)val6).color = new Color(0.92f, 0.97f, 0.98f, 1f); ((TMP_Text)val6).enableWordWrapping = false; ((TMP_Text)val6).overflowMode = (TextOverflowModes)1; } } private static bool IsLegacySectionHeader(string[] lines, int index) { if (index + 1 >= lines.Length) { return false; } string text = lines[index].Trim(); string text2 = lines[index + 1].Trim(); if (text.Length > 0 && text.ToUpperInvariant() == text && text2.Length >= 8) { return text2.Trim('-').Length == 0; } return false; } private static string TitleCase(string value) { string str = value.ToLowerInvariant(); return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(str); } private void CreateHero(string title, string state, string? levelName) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateContentPanel("Report Header", 64f, new Color(0.045f, 0.058f, 0.064f, 0.98f)); TextMeshProUGUI val2 = CreateText(val.transform, "Report Title", 22f, (TextAlignmentOptions)4097); RectTransform rectTransform = ((TMP_Text)val2).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0.55f, 1f); rectTransform.offsetMin = new Vector2(18f, 0f); rectTransform.offsetMax = new Vector2(-8f, 0f); ((TMP_Text)val2).text = title; ((TMP_Text)val2).fontStyle = (FontStyles)1; ((Graphic)val2).color = new Color(0.95f, 0.99f, 1f, 1f); ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)1; TextMeshProUGUI val3 = CreateText(val.transform, "Report Meta", 15f, (TextAlignmentOptions)4100); RectTransform rectTransform2 = ((TMP_Text)val3).rectTransform; rectTransform2.anchorMin = new Vector2(0.55f, 0f); rectTransform2.anchorMax = Vector2.one; rectTransform2.offsetMin = new Vector2(8f, 0f); rectTransform2.offsetMax = new Vector2(-18f, 0f); ((TMP_Text)val3).text = (string.IsNullOrWhiteSpace(levelName) ? state : (state + " | " + levelName)); ((Graphic)val3).color = new Color(0.62f, 0.78f, 0.8f, 1f); ((TMP_Text)val3).enableWordWrapping = false; ((TMP_Text)val3).overflowMode = (TextOverflowModes)1; } private void CreateSectionTitle(string title) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateContentPanel(title + " Title", 30f, new Color(0f, 0f, 0f, 0f)); Image component = val.GetComponent<Image>(); ((Behaviour)component).enabled = false; TextMeshProUGUI val2 = CreateText(val.transform, "Title", 16f, (TextAlignmentOptions)4097); ((TMP_Text)val2).rectTransform.offsetMin = new Vector2(2f, 0f); ((TMP_Text)val2).rectTransform.offsetMax = Vector2.zero; ((TMP_Text)val2).text = title.ToUpperInvariant(); ((TMP_Text)val2).fontStyle = (FontStyles)1; ((Graphic)val2).color = new Color(0.5f, 0.72f, 0.74f, 1f); ((TMP_Text)val2).characterSpacing = 0f; ((TMP_Text)val2).enableWordWrapping = false; } private GameObject CreateHorizontalGroup(string name, float height, float spacing) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateContentPanel(name, height, new Color(0f, 0f, 0f, 0f)); ((Behaviour)val.GetComponent<Image>()).enabled = false; HorizontalLayoutGroup val2 = val.AddComponent<HorizontalLayoutGroup>(); ((LayoutGroup)val2).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val2).spacing = spacing; return val; } private void AddStatTile(Transform parent, string label, string value, string detail, Color accent) { //IL_0020: 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_006c: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateChildPanel(parent, label + " Tile", new Color(0.03f, 0.04f, 0.045f, 0.98f)); LayoutElement val2 = val.AddComponent<LayoutElement>(); val2.flexibleWidth = 1f; val2.preferredHeight = 94f; GameObject val3 = CreatePanel(val.transform, "Accent", new Vector2(0f, 0f), new Vector2(0f, 1f), Vector2.zero, new Vector2(4f, 0f)); ((Graphic)val3.GetComponent<Image>()).color = accent; TextMeshProUGUI val4 = CreateText(val.transform, "Label", 13f, (TextAlignmentOptions)257); SetOffsets(((TMP_Text)val4).rectTransform, new Vector2(16f, 52f), new Vector2(-12f, -10f)); ((TMP_Text)val4).text = label; ((Graphic)val4).color = new Color(0.62f, 0.72f, 0.74f, 1f); ((TMP_Text)val4).enableWordWrapping = false; ((TMP_Text)val4).overflowMode = (TextOverflowModes)1; TextMeshProUGUI val5 = CreateText(val.transform, "Value", 28f, (TextAlignmentOptions)4097); SetOffsets(((TMP_Text)val5).rectTransform, new Vector2(16f, 18f), new Vector2(-12f, -24f)); ((TMP_Text)val5).text = value; ((TMP_Text)val5).fontStyle = (FontStyles)1; ((Graphic)val5).color = new Color(0.95f, 0.99f, 1f, 1f); ((TMP_Text)val5).enableWordWrapping = false; ((TMP_Text)val5).overflowMode = (TextOverflowModes)1; TextMeshProUGUI val6 = CreateText(val.transform, "Detail", 12f, (TextAlignmentOptions)1025); SetOffsets(((TMP_Text)val6).rectTransform, new Vector2(16f, 6f), new Vector2(-12f, -62f)); ((TMP_Text)val6).text = detail; ((Graphic)val6).color = new Color(0.55f, 0.64f, 0.66f, 1f); ((TMP_Text)val6).enableWordWrapping = false; ((TMP_Text)val6).overflowMode = (TextOverflowModes)1; } private void CreateMetricRows(params Metric[] metrics) { for (int i = 0; i < metrics.Length; i += 2) { GameObject val = CreateHorizontalGroup("Metric Row", 54f, 10f); AddMetricCell(val.transform, metrics[i]); if (i + 1 < metrics.Length) { AddMetricCell(val.transform, metrics[i + 1]); } else { AddMetricCell(val.transform, new Metric(string.Empty, string.Empty, string.Empty), visible: false); } } } private void AddMetricCell(Transform parent, Metric metric, bool visible = true) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateChildPanel(parent, "Metric", visible ? new Color(0.028f, 0.035f, 0.039f, 0.96f) : new Color(0f, 0f, 0f, 0f)); ((Behaviour)val.GetComponent<Image>()).enabled = visible; LayoutElement val2 = val.AddComponent<LayoutElement>(); val2.flexibleWidth = 1f; val2.preferredHeight = 54f; if (visible) { TextMeshProUGUI val3 = CreateText(val.transform, "Label", 13f, (TextAlignmentOptions)4097); SetOffsets(((TMP_Text)val3).rectTransform, new Vector2(14f, 0f), new Vector2(-260f, 0f)); ((TMP_Text)val3).text = metric.Label; ((Graphic)val3).color = new Color(0.64f, 0.74f, 0.76f, 1f); ((TMP_Text)val3).enableWordWrapping = false; ((TMP_Text)val3).overflowMode = (TextOverflowModes)1; TextMeshProUGUI val4 = CreateText(val.transform, "Value", 18f, (TextAlignmentOptions)4100); SetOffsets(((TMP_Text)val4).rectTransform, new Vector2(145f, 0f), new Vector2(-130f, 0f)); ((TMP_Text)val4).text = metric.Value; ((TMP_Text)val4).fontStyle = (FontStyles)1; ((Graphic)val4).color = new Color(0.93f, 0.97f, 0.98f, 1f); ((TMP_Text)val4).enableWordWrapping = false; ((TMP_Text)val4).overflowMode = (TextOverflowModes)1; TextMeshProUGUI val5 = CreateText(val.transform, "Detail", 12f, (TextAlignmentOptions)4100); SetOffsets(((TMP_Text)val5).rectTransform, new Vector2(270f, 0f), new Vector2(-14f, 0f)); ((TMP_Text)val5).text = metric.Detail; ((Graphic)val5).color = new Color(0.5f, 0.6f, 0.62f, 1f); ((TMP_Text)val5).enableWordWrapping = false; ((TMP_Text)val5).overflowMode = (TextOverflowModes)1; } } private void CreatePlayerRow(ReportUiPlayer player) { //IL_003b: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) string text = (string.IsNullOrWhiteSpace(player.displayName) ? player.playerId : player.displayName); GameObject val = CreateContentPanel("Player Row", 82f, new Color(0.03f, 0.039f, 0.043f, 0.98f)); TextMeshProUGUI val2 = CreateText(val.transform, "Name", 18f, (TextAlignmentOptions)4097); SetAnchors(((TMP_Text)val2).rectTransform, new Vector2(0f, 0f), new Vector2(0.28f, 1f), new Vector2(16f, 0f), new Vector2(-8f, 0f)); ((TMP_Text)val2).text = text; ((TMP_Text)val2).fontStyle = (FontStyles)1; ((Graphic)val2).color = new Color(0.95f, 0.99f, 1f, 1f); ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)1; AddPlayerStat(val.transform, new Vector2(0.3f, 0f), new Vector2(0.48f, 1f), "Cart/cashout", CountAndMoney(player.objectsPutInCartOrCashout, player.objectsPutInCartOrCashoutValue)); AddPlayerStat(val.transform, new Vector2(0.5f, 0f), new Vector2(0.66f, 1f), "Extracted weight", Weight(player.extractedObjectWeight)); AddPlayerStat(val.transform, new Vector2(0.68f, 0f), new Vector2(0.84f, 1f), "Detected", CountAndMoney(player.objectsDetected, player.objectsDetectedValue)); AddPlayerStat(val.transform, new Vector2(0.86f, 0f), new Vector2(1f, 1f), "Damage taken", Number(player.damageTaken)); } private void AddPlayerStat(Transform parent, Vector2 anchorMin, Vector2 anchorMax, string label, string value) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(label); val.transform.SetParent(parent, false); RectTransform rect = val.AddComponent<RectTransform>(); SetAnchors(rect, anchorMin, anchorMax, Vector2.zero, Vector2.zero); TextMeshProUGUI val2 = CreateText(val.transform, "Label", 11f, (TextAlignmentOptions)1025); SetAnchors(((TMP_Text)val2).rectTransform, new Vector2(0f, 0.5f), Vector2.one, new Vector2(0f, 0f), new Vector2(-8f, -8f)); ((TMP_Text)val2).text = label; ((Graphic)val2).color = new Color(0.54f, 0.64f, 0.66f, 1f); ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)1; TextMeshProUGUI val3 = CreateText(val.transform, "Value", 17f, (TextAlignmentOptions)257); SetAnchors(((TMP_Text)val3).rectTransform, Vector2.zero, new Vector2(1f, 0.55f), new Vector2(0f, 8f), new Vector2(-8f, 0f)); ((TMP_Text)val3).text = value; ((TMP_Text)val3).fontStyle = (FontStyles)1; ((Graphic)val3).color = new Color(0.87f, 0.94f, 0.95f, 1f); ((TMP_Text)val3).enableWordWrapping = false; ((TMP_Text)val3).overflowMode = (TextOverflowModes)1; } private void CreateEmptyState(string text) { //IL_001f: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateContentPanel("Empty State", 58f, new Color(0.028f, 0.035f, 0.039f, 0.96f)); TextMeshProUGUI val2 = CreateText(val.transform, "Text", 15f, (TextAlignmentOptions)4097); ((TMP_Text)val2).rectTransform.offsetMin = new Vector2(16f, 0f); ((TMP_Text)val2).rectTransform.offsetMax = new Vector2(-16f, 0f); ((TMP_Text)val2).text = text; ((Graphic)val2).color = new Color(0.62f, 0.72f, 0.74f, 1f); ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)1; } private void LateUpdate() { KeepModalInputState(); if (_layoutDirty) { RefreshContentHeight(); _layoutDirty = false; } } private void Update() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) KeepModalInputState(); float y = Input.mouseScrollDelta.y; if (Mathf.Abs(y) > 0.01f) { _scrollOffset = Mathf.Clamp(_scrollOffset - y * 58f, 0f, _maxScrollOffset); ApplyScrollOffset(); } } private void RefreshContentHeight() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00a5: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_contentRect == (Object)null) && !((Object)(object)_viewportRect == (Object)null)) { Canvas.ForceUpdateCanvases(); Rect rect = _viewportRect.rect; float num = Mathf.Max(300f, ((Rect)(ref rect)).height); if ((Object)(object)_fallbackText != (Object)null && (Object)(object)_fallbackLayout != (Object)null) { rect = _viewportRect.rect; float num2 = Mathf.Max(300f, ((Rect)(ref rect)).width - 36f); Vector2 preferredValues = ((TMP_Text)_fallbackText).GetPreferredValues(((TMP_Text)_fallbackText).text, num2, 0f); _fallbackLayout.preferredHeight = Mathf.Max(num, preferredValues.y + 36f); } LayoutRebuilder.ForceRebuildLayoutImmediate(_contentRect); float num3 = Mathf.Max(num, LayoutUtility.GetPreferredHeight(_contentRect)); _contentRect.sizeDelta = new Vector2(0f, num3); _maxScrollOffset = Mathf.Max(0f, num3 - num); _scrollOffset = Mathf.Clamp(_scrollOffset, 0f, _maxScrollOffset); ApplyScrollOffset(); } } private void ApplyScrollOffset() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_contentRect == (Object)null)) { _contentRect.anchoredPosition = new Vector2(0f, _scrollOffset); UpdateScrollThumb(); } } private void UpdateScrollThumb() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_viewportRect == (Object)null) && !((Object)(object)_scrollTrackRect == (Object)null) && !((Object)(object)_scrollThumbRect == (Object)null) && !((Object)(object)_scrollTrackImage == (Object)null) && !((Object)(object)_scrollThumbImage == (Object)null)) { bool flag = _maxScrollOffset > 1f; ((Behaviour)_scrollTrackImage).enabled = flag; ((Behaviour)_scrollThumbImage).enabled = flag; if (flag) { Rect rect = _scrollTrackRect.rect; float num = Mathf.Max(1f, ((Rect)(ref rect)).height); rect = _viewportRect.rect; float num2 = Mathf.Max(1f, ((Rect)(ref rect)).height); float num3 = num2 + _maxScrollOffset; float num4 = Mathf.Clamp(num * (num2 / num3), 42f, num); float num5 = Mathf.Max(0f, num - num4); float num6 = _scrollOffset / _maxScrollOffset; _scrollThumbRect.sizeDelta = new Vector2(0f, num4); _scrollThumbRect.anchoredPosition = new Vector2(0f, (0f - num6) * num5); } } } private void UpdateTabVisuals() { if (_summaryTab != null) { ApplyButtonStyle(_summaryTab, _summaryEnabled, _selectedTab == ReportUiTab.Summary, compact: false); } if (_liveTab != null) { ApplyButtonStyle(_liveTab, _liveEnabled, _selectedTab == ReportUiTab.Live, compact: false); } } private void ClearContent() { if (!((Object)(object)_contentRect == (Object)null)) { for (int num = ((Transform)_contentRect).childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)((Transform)_contentRect).GetChild(num)).gameObject); } } } private void OnDestroy() { RestoreCursorState(); if (!_isClosing) { _isClosing = true; _closed?.Invoke(); } } private void CaptureCursorState() { //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) _previousCursorLockState = Cursor.lockState; _previousCursorVisible = Cursor.visible; KeepModalInputState(); } private void KeepModalInputState() { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; InputManager instance = InputManager.instance; if (!((Object)(object)instance == (Object)null)) { GameAccess.SetFloatFieldAtLeast(instance, "disableAimingTimer", 0.25f); GameAccess.SetFloatFieldAtLeast(instance, "disableMovementTimer", 0.25f); } } private void RestoreCursorState() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Cursor.lockState = _previousCursorLockState; Cursor.visible = _previousCursorVisible; } private GameObject CreateContentPanel(string name, float height, Color color) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_contentRect == (Object)null) { throw new InvalidOperationException("Content root has not been built."); } GameObject val = CreateChildPanel((Transform)(object)_contentRect, name, color); RectTransform component = val.GetComponent<RectTransform>(); component.sizeDelta = new Vector2(0f, height); LayoutElement val2 = val.AddComponent<LayoutElement>(); val2.preferredHeight = height; val2.minHeight = height; val2.flexibleWidth = 1f; return val; } private static GameObject CreatePanel(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_001c: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = anchorMin; val2.anchorMax = anchorMax; val2.offsetMin = offsetMin; val2.offsetMax = offsetMax; val.AddComponent<Image>(); return val; } private static GameObject CreateChildPanel(Transform parent, string name, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //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_0032: 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_004f: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = color; return val; } private static TextMeshProUGUI CreateText(Transform parent, string name, float fontSize, TextAlignmentOptions alignment) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //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_0032: 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_0061: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; TextMeshProUGUI val3 = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val3).text = string.Empty; ((TMP_Text)val3).fontSize = fontSize; ((TMP_Text)val3).alignment = alignment; ((TMP_Text)val3).richText = false; return val3; } private static ButtonVisual CreateButton(Transform parent, string label, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 size, float fontSize) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0026: 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_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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_00e5: 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) GameObject val = new GameObject(label + " Button"); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = anchorMin; val2.anchorMax = anchorMax; val2.pivot = new Vector2(0.5f, 0.5f); val2.anchoredPosition = anchoredPosition; val2.sizeDelta = size; Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = new Color(0.09f, 0.12f, 0.13f, 0.96f); Button val4 = val.AddComponent<Button>(); ((Selectable)val4).targetGraphic = (Graphic)(object)val3; ((Selectable)val4).transition = (Transition)1; TextMeshProUGUI val5 = CreateText(val.transform, "Label", fontSize, (TextAlignmentOptions)514); ((TMP_Text)val5).text = label; ((Graphic)val5).color = Color.white; ((TMP_Text)val5).enableWordWrapping = false; ((TMP_Text)val5).overflowMode = (TextOverflowModes)1; RectTransform rectTransform = ((TMP_Text)val5).rectTransform; rectTransform.offsetMin = new Vector2(8f, 0f); rectTransform.offsetMax = new Vector2(-8f, 0f); return new ButtonVisual(val4, val3, val5); } private static void ApplyButtonStyle(ButtonVisual visual, bool enabled, bool selected, bool compact) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) ((Selectable)visual.Button).interactable = enabled; Color val = (selected ? new Color(0.13f, 0.45f, 0.46f, 0.98f) : new Color(0.075f, 0.095f, 0.105f, compact ? 0.7f : 0.96f)); Color val2 = (selected ? new Color(0.16f, 0.55f, 0.56f, 1f) : new Color(0.13f, 0.18f, 0.19f, 0.98f)); Color pressedColor = (selected ? new Color(0.09f, 0.32f, 0.34f, 1f) : new Color(0.045f, 0.06f, 0.065f, 1f)); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.06f, 0.065f, 0.07f, 0.55f); ColorBlock colors = ((Selectable)visual.Button).colors; ((ColorBlock)(ref colors)).normalColor = val; ((ColorBlock)(ref colors)).highlightedColor = val2; ((ColorBlock)(ref colors)).pressedColor = pressedColor; ((ColorBlock)(ref colors)).selectedColor = val2; ((ColorBlock)(ref colors)).disabledColor = val3; ((ColorBlock)(ref colors)).fadeDuration = 0.08f; ((ColorBlock)(ref colors)).colorMultiplier = 1f; ((Selectable)visual.Button).colors = colors; ((Graphic)visual.Background).color = (enabled ? val : val3); ((Graphic)visual.Label).color = ((!enabled) ? new Color(0.42f, 0.49f, 0.5f, 1f) : (selected ? new Color(0.98f, 1f, 1f, 1f) : new Color(0.74f, 0.84f, 0.86f, 1f))); ((TMP_Text)visual.Label).fontStyle = (FontStyles)(selected ? 1 : 0); } private static void SetOffsets(RectTransform rect, Vector2 offsetMin, Vector2 offsetMax) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = offsetMin; rect.offsetMax = offsetMax; } private static void SetAnchors(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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) rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; rect.offsetMin = offsetMin; rect.offsetMax = offsetMax; } private static string Money(int value) { return "$" + value.ToString("N0", CultureInfo.InvariantCulture); } private static string Number(int value) { return value.ToString("N0", CultureInfo.InvariantCulture); } private static string Percent(float value) { return value.ToString("0.##", CultureInfo.InvariantCulture) + "%"; } private static string Weight(float value) { return value.ToString("0.##", CultureInfo.InvariantCulture); } private static string CountAndMoney(int count, int value) { return Number(count) + " / " + Money(value); } private static string CountDetail(int count, string singular) { return Number(count) + " " + singular + ((count == 1) ? string.Empty : "s"); } } internal sealed class ReportUiController { private enum ReportKey { None, Menu, Escape } private sealed class PageContent { public ReportSnapshot? Snapshot { get; } public string FallbackText { get; } public string Footer { get; } public PageContent(ReportSnapshot? snapshot, string fallbackText, string footer) { Snapshot = snapshot; FallbackText = fallbackText; Footer = footer; } } private readonly ReportSyncService _syncService; private readonly Func<ReportSnapshot?> _liveReportProvider; private ReportSnapshot? _latestFinalReport; private ReportSnapshot? _latestLiveReport; private ReportOverlayPage? _overlay; private int _lastHandledFrame = -1; private ReportKey _lastHandledKey; private InputAction? _menuAction; private bool _menuActionWasHeld; private bool _waitingForShortcutRelease; private float _shortcutReleaseWaitStartedAt; private float _ignoreHotkeysUntil; private int _suppressGameMenuInputFrame = -1; private ReportUiTab _selectedTab; public ReportUiController(ReportSyncService syncService, Func<ReportSnapshot?> liveReportProvider) { _syncService = syncService ?? throw new ArgumentNullException("syncService"); _liveReportProvider = liveReportProvider ?? throw new ArgumentNullException("liveReportProvider"); } public void Initialize() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) RebuildInputActions(); NuageReportPlugin.Log.LogInfo((object)($"Report UI initialized. MenuShortcut={PluginConfig.Instance.EffectiveReportMenuShortcut()}, " + $"SelectedTab={_selectedTab}, LiveEnabled={PluginConfig.Instance.IsLiveReportEnabled()}, " + $"MenuInputAction={_menuAction != null}")); } public void Tick(string source = "Update") { //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 (!PluginConfig.Instance.IsModActive()) { Deactivate(); return; } KeepOpenMenuValid(); if (!HotkeysAreSuppressed()) { bool flag = ActionIsDown(_menuAction, ref _menuActionWasHeld); KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut(); if (((KeyboardShortcut)(ref val)).IsDown() || flag) { HandleMenuKey(flag ? (source + "/InputAction") : (source + "/KeyboardShortcut")); } } } public bool HandleKeyEvent(KeyCode keyCode, string source) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.Instance.IsModActive()) { Deactivate(); return false; } if ((int)keyCode == 0) { return false; } if ((int)keyCode == 27 && TryConsumeEscapeMenuInput(source)) { return true; } if (HotkeysAreSuppressed()) { return false; } if (IsMenuKey(keyCode)) { return HandleMenuKey(source); } return false; } public void Deactivate() { CloseOpenPage(); _menuActionWasHeld = false; _waitingForShortcutRelease = false; } public bool TryConsumeEscapeMenuInput(string source) { if (_suppressGameMenuInputFrame == Time.frameCount) { return true; } if ((Object)(object)_overlay == (Object)null) { return false; } if (!TryMarkHandled(ReportKey.Escape)) { SuppressGameMenuInputThisFrame(); return true; } NuageReportPlugin.Log.LogInfo((object)("Report overlay escape close detected via " + source + ".")); CloseOpenPage(); SuppressHotkeysUntilReleased(); SuppressGameMenuInputThisFrame(); return true; } public void SetLatestFinalReport(ReportSnapshot snapshot) { _latestFinalReport = snapshot ?? throw new ArgumentNullException("snapshot"); RefreshOpenTab(ReportUiTab.Summary); } public void SetLatestLiveReport(ReportSnapshot snapshot) { _latestLiveReport = snapshot ?? throw new ArgumentNullException("snapshot"); RefreshOpenTab(ReportUiTab.Live); } private bool HandleMenuKey(string source) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!TryMarkHandled(ReportKey.Menu)) { return false; } NuageReportPlugin.Log.LogInfo((object)($"Report menu key detected via {source}. enabledTabs=summary:{IsTabEnabled(ReportUiTab.Summary)} live:{IsTabEnabled(ReportUiTab.Live)} " + $"shortcut={PluginConfig.Instance.EffectiveReportMenuShortcut()} hasFinal={HasFinalReport()} hasLive={HasLiveReport()}")); if ((Object)(object)_overlay != (Object)null) { CloseOpenPage(); SuppressHotkeysUntilReleased(); return true; } try { OpenReportMenu(); } finally { SuppressHotkeysUntilReleased(); } return true; } private void OpenReportMenu() { ReportUiTab selectedTab = _selectedTab; ReportUiTab? reportUiTab = ChooseAvailableTab(selectedTab); if (!reportUiTab.HasValue) { NuageReportPlugin.Log.LogInfo((object)"Report menu did not open because both report tabs are disabled."); return; } _selectedTab = reportUiTab.Value; PageContent pageContent = ContentFor(_selectedTab); CloseOpenPage(); try { _overlay = ReportOverlayPage.Create(_selectedTab, IsTabEnabled(ReportUiTab.Summary), IsTabEnabled(ReportUiTab.Live), pageContent.Snapshot, pageContent.FallbackText, pageContent.Footer, SelectTabFromOverlay, delegate { _overlay = null; }); } catch (Exception arg) { _overlay = null; NuageReportPlugin.Log.LogError((object)$"Failed to open report overlay: {arg}"); } } private void SelectTabFromOverlay(ReportUiTab tab) { SelectTab(tab); } private void SelectTab(ReportUiTab tab) { if (!IsTabEnabled(tab)) { KeepOpenMenuValid(); return; } _selectedTab = tab; UpdateOpenMenuContent(); } private void KeepOpenMenuValid() { if (!((Object)(object)_overlay == (Object)null) && !IsTabEnabled(_selectedTab)) { ReportUiTab? reportUiTab = ChooseAvailableTab(OtherTab(_selectedTab)); if (!reportUiTab.HasValue) { CloseOpenPage(); } else { SelectTab(reportUiTab.Value); } } } private void RefreshOpenTab(ReportUiTab tab) { if ((Object)(object)_overlay != (Object)null && _selectedTab == tab && IsTabEnabled(tab)) { UpdateOpenMenuContent(); } } private void UpdateOpenMenuContent() { if (!((Object)(object)_overlay == (Object)null)) { PageContent pageContent = ContentFor(_selectedTab); _overlay.SetContent(_selectedTab, IsTabEnabled(ReportUiTab.Summary), IsTabEnabled(ReportUiTab.Live), pageContent.Snapshot, pageContent.FallbackText, pageContent.Footer); } } private PageContent ContentFor(ReportUiTab tab) { if (tab != ReportUiTab.Live) { return SummaryContent(); } return LiveContent(); } private PageContent SummaryContent() { ReportSnapshot reportSnapshot = _latestFinalReport ?? _syncService.LatestFinalReport; if (reportSnapshot == null) { _syncService.RequestLatestReport(includeFinalReport: true, includeLiveReport: false); return new PageContent(null, "No completed level report is available yet.", "Summary | No completed report"); } return new PageContent(reportSnapshot, reportSnapshot.DisplayText, FooterForSnapshot(reportSnapshot, "Summary")); } private PageContent LiveContent() { ReportSnapshot reportSnapshot = _liveReportProvider() ?? _latestLiveReport ?? _syncService.LatestLiveReport; if (reportSnapshot == null) { _syncService.RequestLatestReport(includeFinalReport: false, includeLiveReport: true); return new PageContent(null, "No live report is available.", "Live | Waiting for an active shared report"); } return new PageContent(reportSnapshot, reportSnapshot.DisplayText, FooterForSnapshot(reportSnapshot, "Live")); } private bool HasFinalReport() { if (_latestFinalReport == null) { return _syncService.LatestFinalReport != null; } return true; } private bool HasLiveReport() { if (_latestLiveReport == null) { return _syncService.LatestLiveReport != null; } return true; } private static ReportUiTab? ChooseAvailableTab(ReportUiTab preferredTab) { if (IsTabEnabled(preferredTab)) { return preferredTab; } ReportUiTab reportUiTab = OtherTab(preferredTab); if (!IsTabEnabled(reportUiTab)) { return null; } return reportUiTab; } private static bool IsTabEnabled(ReportUiTab tab) { if (tab != ReportUiTab.Live) { return true; } return PluginConfig.Instance.IsLiveReportEnabled(); } private static ReportUiTab OtherTab(ReportUiTab tab) { if (tab != ReportUiTab.Live) { return ReportUiTab.Live; } return ReportUiTab.Summary; } private void RebuildInputActions() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) InputAction? menuAction = _menuAction; if (menuAction != null) { menuAction.Disable(); } KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut(); _menuAction = CreateInputAction("NuageReport Menu", ((KeyboardShortcut)(ref val)).MainKey); _menuActionWasHeld = false; } private static InputAction? CreateInputAction(string name, KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown string text = BindingPathFor(keyCode); if (text == null) { return null; } InputAction val = new InputAction(name, (InputActionType)1, text, (string)null, (string)null, (string)null); val.Enable(); return val; } private static bool ActionIsDown(InputAction? action, ref bool wasHeld) { if (action == null) { return false; } bool flag = action.ReadValue<float>() > 0.5f; bool result = flag && !wasHeld; wasHeld = flag; return result; } private bool TryMarkHandled(ReportKey key) { if (_lastHandledFrame == Time.frameCount && _lastHandledKey == key) { return false; } _lastHandledFrame = Time.frameCount; _lastHandledKey = key; return true; } private static bool IsMenuKey(KeyCode keyCode) { //IL_0005: 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) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut(); return keyCode == ((KeyboardShortcut)(ref val)).MainKey; } private bool HotkeysAreSuppressed() { if (Time.unscaledTime < _ignoreHotkeysUntil) { return true; } if (!_waitingForShortcutRelease) { return false; } if (Time.unscaledTime - _shortcutReleaseWaitStartedAt > 2f) { _waitingForShortcutRelease = false; return false; } if (AnyReportShortcutPressed()) { return true; } _waitingForShortcutRelease = false; return false; } private void SuppressHotkeysUntilReleased() { _waitingForShortcutRelease = true; _shortcutReleaseWaitStartedAt = Time.unscaledTime; _ignoreHotkeysUntil = Time.unscaledTime + 0.15f; _menuActionWasHeld = IsActionPressed(_menuAction); } private void SuppressGameMenuInputThisFrame() { _suppressGameMenuInputFrame = Time.frameCount; } private bool AnyReportShortcutPressed() { //IL_0005: 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) KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut(); if (!((KeyboardShortcut)(ref val)).IsPressed()) { return IsActionPressed(_menuAction); } return true; } private static bool IsActionPressed(InputAction? action) { if (action != null) { return action.ReadValue<float>() > 0.5f; } return false; } private static string? BindingPathFor(KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Invalid comparison between Unknown and I4 //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Invalid comparison between Unknown and I4 //IL_000d: 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_005a: Expected I4, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected I4, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Invalid comparison between Unknown and I4 //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 if ((int)keyCode <= 91) { if ((int)keyCode == 32) { return "<Keyboard>/space"; } switch (keyCode - 45) { default: if ((int)keyCode != 91) { break; } return "<Keyboard>/leftBracket"; case 3: return "<Keyboard>/0"; case 4: return "<Keyboard>/1"; case 5: return "<Keyboard>/2"; case 6: return "<Keyboard>/3"; case 7: return "<Keyboard>/4"; case 8: return "<Keyboard>/5"; case 9: return "<Keyboard>/6"; case 10: return "<Keyboard>/7"; case 11: return "<Keyboard>/8"; case 12: return "<Keyboard>/9"; case 0: return "<Keyboard>/minus"; case 16: return "<Keyboard>/equals"; case 1: case 2: case 13: case 14: case 15: break; } } else if ((int)keyCode <= 96) { if ((int)keyCode == 93) { return "<Keyboard>/rightBracket"; } if ((int)keyCode == 96) { return "<Keyboard>/backquote"; } } else { if ((int)keyCode == 111) { return "<Keyboard>/o"; } switch (keyCode - 282) { case 0: return "<Keyboard>/f1"; case 1: return "<Keyboard>/f2"; case 2: return "<Keyboard>/f3"; case 3: return "<Keyboard>/f4"; case 4: return "<Keyboard>/f5"; case 5: return "<Keyboard>/f6"; case 6: return "<Keyboard>/f7"; case 7: return "<Keyboard>/f8"; case 8: return "<Keyboard>/f9"; case 9: return "<Keyboard>/f10"; case 10: return "<Keyboard>/f11"; case 11: return "<Keyboard>/f12"; } } return null; } private void CloseOpenPage() { if (!((Object)(object)_overlay == (Object)null)) { ReportOverlayPage overlay = _overlay; _overlay = null; overlay.Close(); } } private static string FooterForSnapshot(ReportSnapshot snapshot, string header) { string text = (string.IsNullOrWhiteSpace(snapshot.LevelName) ? "Unknown level" : snapshot.LevelName); string text2 = ((snapshot.ReportKind == ReportKind.Live) ? "Live" : "Complete"); return string.Format(CultureInfo.InvariantCulture, "{0} | {1} | {2} | Run {3} | Generated {4:u}", header, text2, text, snapshot.RunId, snapshot.GeneratedAtUtc); } } [Serializable] internal sealed class ReportUiData { public int schemaVersion; public string runId = string.Empty; public string startedAt = string.Empty; public string endedAt = string.Empty; public ReportUiGlobal? global; public ReportUiPlayer[]? players; public ReportUiCosmeticObjects? cosmeticObjects; public static bool TryParse(string json, out ReportUiData? data) { data = null; if (string.IsNullOrWhiteSpace(json)) { return false; } try { object obj = new ReportJsonReader(json).Read(); if (!(obj is Dictionary<string, object> map)) { return false; } data = FromMap(map); return data.global != null; } catch { data = null; return false; } } private static ReportUiData FromMap(Dictionary<string, object?> map) { return new ReportUiData { schemaVersion = IntValue(map, "schemaVersion"), runId = StringValue(map, "runId"), startedAt = StringValue(map, "startedAt"), endedAt = StringValue(map, "endedAt"), global = GlobalFromMap(ObjectValue(map, "global")), players = PlayersFromArray(ArrayValue(map, "players")), cosmeticObjects = CosmeticObjectsFromMap(ObjectValue(map, "cosmeticObjects")) }; } private static ReportUiGlobal? GlobalFromMap(Dictionary<string, object?>? map) { if (map == null) { return null; } return new ReportUiGlobal { objectsAtStart = IntValue(map, "objectsAtStart"), objectsAtStartValue = IntValue(map, "objectsAtStartValue"), objectsExtracted = IntValue(map, "objectsExtracted"), objectsExtractedValue = IntValue(map, "objectsExtractedValue"), objectsDetected = IntValue(map, "objectsDetected"), objectsDetectedPercent = FloatValue(map, "objectsDetectedPercent"), objectsMissed = IntValue(map, "objectsMissed"), objectsDamaged = IntValue(map, "objectsDamaged"), objectsDestroyed =