Decompiled source of CombatMeter v1.1.2

plugins/CombatMeter.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DiagnosticDamageProbe.Analytics;
using DiagnosticDamageProbe.Attribution;
using DiagnosticDamageProbe.Configuration;
using DiagnosticDamageProbe.Diagnostics;
using DiagnosticDamageProbe.Encounter;
using DiagnosticDamageProbe.History;
using DiagnosticDamageProbe.Snapshot;
using DiagnosticDamageProbe.Statistics;
using DiagnosticDamageProbe.Transport;
using DiagnosticDamageProbe.UI;
using HarmonyLib;
using Jotunn.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("CombatMeter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: AssemblyInformationalVersion("1.1.2")]
[assembly: AssemblyProduct("CombatMeter")]
[assembly: AssemblyTitle("CombatMeter")]
[assembly: AssemblyVersion("1.1.2.0")]
namespace DiagnosticDamageProbe
{
	internal static class AnalyticsRuntime
	{
		private sealed class DamageLink
		{
			internal HitData Hit;

			internal AnalyticsDamageEvidence Evidence;
		}

		private sealed class EffectState
		{
			internal readonly Guid Id = Guid.NewGuid();

			internal bool BreakReported;

			internal ActorId Attacker;

			internal ActorId Victim;

			internal ActorEvidence Actor;
		}

		private sealed class RejectedHit
		{
			internal readonly Guid Token = Guid.NewGuid();
		}

		internal sealed class AttackScope
		{
			internal readonly object Source;

			internal readonly ActorId Attacker;

			internal readonly Guid Token = Guid.NewGuid();

			internal readonly Guid Epoch;

			internal readonly AttackScope Parent;

			internal Character Character;

			internal AttackScope(object source, ActorId attacker)
			{
				Source = source;
				Attacker = attacker;
				Epoch = _epoch;
				Parent = Current;
			}
		}

		private static ConditionalWeakTable<Character, DamageLink> _damage = new ConditionalWeakTable<Character, DamageLink>();

		private static ConditionalWeakTable<SE_Shield, EffectState> _effects = new ConditionalWeakTable<SE_Shield, EffectState>();

		private static Guid _epoch = Guid.NewGuid();

		private static ConditionalWeakTable<HitData, RejectedHit> _rejectedHits = new ConditionalWeakTable<HitData, RejectedHit>();

		[ThreadStatic]
		internal static AttackScope Current;

		internal static ActorId Id(Character c)
		{
			if (!Object.op_Implicit((Object)(object)c))
			{
				return default(ActorId);
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			ZDO val = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO() : null);
			if (val != null)
			{
				return new ActorId(((ZDOID)(ref val.m_uid)).UserID, ((ZDOID)(ref val.m_uid)).ID);
			}
			return default(ActorId);
		}

		internal static ActorEvidence CaptureActor(Character actor, Character counterpart)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Invalid comparison between Unknown and I4
			if (!Object.op_Implicit((Object)(object)actor))
			{
				return null;
			}
			try
			{
				ZNetView component = ((Component)actor).GetComponent<ZNetView>();
				ZDO val = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO() : null);
				if (val == null)
				{
					return null;
				}
				object obj;
				if (!Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					obj = null;
				}
				else
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(val.GetPrefab());
					obj = ((prefab != null) ? ((Object)prefab).name : null);
				}
				string text = (string)obj;
				Character val2 = (Character)((counterpart is Player) ? ((object)counterpart) : ((object)Player.m_localPlayer));
				bool hostile = Object.op_Implicit((Object)(object)val2) && !actor.IsPlayer() && BaseAI.IsEnemy(actor, val2);
				bool summon = (int)actor.GetFaction() == 11;
				return new ActorEvidence(new ActorId(((ZDOID)(ref val.m_uid)).UserID, ((ZDOID)(ref val.m_uid)).ID), text, actor.IsPlayer(), actor.IsTamed(), hostile, summon, text == "staff_greenroots_tentaroot");
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
				return null;
			}
		}

		internal static void TrackDamage(Character victim, HitData hit, AnalyticsDamageEvidence evidence)
		{
			try
			{
				if (CombatAnalyticsProbe.LastHit.GetValue(victim) == hit)
				{
					_damage.Remove(victim);
					_damage.Add(victim, new DamageLink
					{
						Hit = hit,
						Evidence = evidence
					});
				}
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		internal static void ConfirmDeath(Character victim)
		{
			try
			{
				if (CombatAnalyticsProbe.Owner(victim) && !(victim.GetHealth() > 0f) && _damage.TryGetValue(victim, out var value) && value.Evidence.HpAfter == 0.0 && CombatAnalyticsProbe.LastHit.GetValue(victim) == value.Hit)
				{
					value.Evidence.ConfirmDeath();
				}
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		internal static AttackScope BeginAttack(Attack attack)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			try
			{
				Character val = (Character)CombatAnalyticsProbe.AttackCharacter.GetValue(attack);
				return Current = new AttackScope(attack, CombatAnalyticsProbe.Owner(val) ? Id(val) : default(ActorId))
				{
					Character = val
				};
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
				return null;
			}
		}

		internal static void EndAttack(AttackScope state)
		{
			if (state != null && state.Epoch == _epoch)
			{
				Current = state.Parent;
			}
		}

		internal static void RejectedDodge(Character victim, object source)
		{
			Player val = (Player)(object)((victim is Player) ? victim : null);
			if (val == null || victim.GetHealth() <= 0f || victim.IsDead() || victim.IsTeleporting() || victim.InCutscene())
			{
				return;
			}
			HitData val2 = (HitData)((source is HitData) ? source : null);
			if (val2 != null)
			{
				if (!CombatAnalyticsProbe.Owner(victim))
				{
					return;
				}
				ActorEvidence actorEvidence = CaptureActor(val2.GetAttacker(), victim);
				if (actorEvidence != null && actorEvidence.Eligible())
				{
					ActorId victim2 = Id(victim);
					if (victim2.IsValid)
					{
						Plugin.PublishDefense(val.GetPlayerID(), new DefenseObservation(Guid.NewGuid(), DefenseKind.Dodge, _rejectedHits.GetOrCreateValue(val2).Token, actorEvidence.Id, victim2, applied: false, parry: false, rejected: true, 0.0, 0.0, 0.0, 0.0, 0.0, actorEvidence, ActivityTime()));
					}
				}
			}
			else if (Current != null && Current.Source == source)
			{
				ActorId victim3 = Id(victim);
				if (victim3.IsValid && Current.Attacker.IsValid)
				{
					Plugin.PublishDefense(val.GetPlayerID(), new DefenseObservation(Guid.NewGuid(), DefenseKind.Dodge, Current.Token, Current.Attacker, victim3, applied: false, parry: false, rejected: true, 0.0, 0.0, 0.0, 0.0, 0.0, CaptureActor(Current.Character, victim), ActivityTime()));
				}
			}
		}

		private static double ActivityTime()
		{
			return (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
		}

		internal static void Block(Character victim, bool applied, bool parry, double removed, Guid eventId, ActorId attacker, Character source)
		{
			Player val = (Player)(object)((victim is Player) ? victim : null);
			if (val != null && applied && removed > 0.0)
			{
				long playerID = val.GetPlayerID();
				ActorId victim2 = Id(victim);
				bool parry2 = parry;
				ActorEvidence activityAttacker = CaptureActor(source, victim);
				double? activityTime = ActivityTime();
				Plugin.PublishDefense(playerID, new DefenseObservation(eventId, DefenseKind.Block, default(Guid), attacker, victim2, applied: true, parry2, rejected: false, removed, 0.0, 0.0, 0.0, 0.0, activityAttacker, activityTime));
			}
		}

		internal static void ShieldHit(SE_Shield shield, Character victim, Guid eventId, double incoming, double outgoing, ActorId attacker, Character source)
		{
			Player val = (Player)(object)((victim is Player) ? victim : null);
			if (val != null)
			{
				EffectState orCreateValue = _effects.GetOrCreateValue(shield);
				orCreateValue.Attacker = attacker;
				orCreateValue.Victim = Id(victim);
				orCreateValue.Actor = CaptureActor(source, victim);
				Plugin.PublishDefense(val.GetPlayerID(), new DefenseObservation(eventId, DefenseKind.BarrierHit, orCreateValue.Id, attacker, orCreateValue.Victim, applied: false, parry: false, rejected: false, 0.0, incoming, outgoing, 0.0, 0.0, orCreateValue.Actor, ActivityTime()));
			}
		}

		internal static void ShieldDone(SE_Shield shield, Character victim, double consumed, double capacity)
		{
			Player val = (Player)(object)((victim is Player) ? victim : null);
			if (val != null && consumed > capacity)
			{
				EffectState orCreateValue = _effects.GetOrCreateValue(shield);
				if (!orCreateValue.BreakReported)
				{
					orCreateValue.BreakReported = true;
					Plugin.PublishDefense(val.GetPlayerID(), new DefenseObservation(Guid.NewGuid(), DefenseKind.BarrierDone, orCreateValue.Id, orCreateValue.Attacker, orCreateValue.Victim, applied: false, parry: false, rejected: false, 0.0, 0.0, 0.0, consumed, capacity, orCreateValue.Actor));
				}
			}
		}

		internal static void Reset()
		{
			_damage = new ConditionalWeakTable<Character, DamageLink>();
			_effects = new ConditionalWeakTable<SE_Shield, EffectState>();
			_rejectedHits = new ConditionalWeakTable<HitData, RejectedHit>();
			Current = null;
			_epoch = Guid.NewGuid();
		}
	}
	[HarmonyPatch]
	internal static class AnalyticsAttackScopePatch
	{
		internal static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Attack), "DoMeleeAttack", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Attack), "DoAreaAttack", (Type[])null, (Type[])null);
		}

		[HarmonyPrefix]
		internal static void Prefix(Attack __instance, out AnalyticsRuntime.AttackScope __state)
		{
			__state = AnalyticsRuntime.BeginAttack(__instance);
		}

		[HarmonyFinalizer]
		internal static Exception Finalizer(Exception __exception, AnalyticsRuntime.AttackScope __state)
		{
			AnalyticsRuntime.EndAttack(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage", new Type[]
	{
		typeof(HitData),
		typeof(bool),
		typeof(bool),
		typeof(DamageModifier)
	})]
	internal static class ApplyDamagePatch
	{
		internal sealed class CallState
		{
			internal readonly ZNetView View;

			internal readonly ZDO Zdo;

			internal readonly long Session;

			internal readonly long Owner;

			internal readonly float HpBefore;

			internal readonly LogFields Fields;

			internal readonly DamageFacts Facts;

			internal HitData Hit;

			internal Guid AnalyticsInvocation;

			internal ActorEvidence VictimActor;

			internal ActorEvidence AttackerActor;

			internal CallState(ZNetView view, ZDO zdo, long session, float hpBefore, LogFields fields, DamageFacts facts)
			{
				View = view;
				Zdo = zdo;
				Session = session;
				Owner = zdo.GetOwner();
				HpBefore = hpBefore;
				Fields = fields;
				Facts = facts;
			}
		}

		[HarmonyPrefix]
		internal static void Prefix(Character __instance, HitData hit, out CallState __state)
		{
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_0322: 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_0503: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_058f: Invalid comparison between Unknown and I4
			//IL_0592: Unknown result type (might be due to invalid IL or missing references)
			//IL_0598: Invalid comparison between Unknown and I4
			//IL_05bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c5: Invalid comparison between Unknown and I4
			__state = null;
			try
			{
				if (!Object.op_Implicit((Object)(object)__instance))
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
				{
					return;
				}
				ZDO zDO = component.GetZDO();
				if (zDO != null && ZDOMan.instance != null)
				{
					float health = __instance.GetHealth();
					long sessionID = ZDOMan.GetSessionID();
					ZNet instance = ZNet.instance;
					bool flag = Object.op_Implicit((Object)(object)instance) && instance.IsServer();
					bool flag2 = Object.op_Implicit((Object)(object)instance) && instance.IsDedicated();
					bool flag3 = Object.op_Implicit((Object)(object)instance) && ZNet.IsSinglePlayer;
					string value = ((!Object.op_Implicit((Object)(object)instance)) ? "Unknown" : (flag2 ? "DedicatedServer" : ((!flag) ? "Client" : (flag3 ? "SinglePlayer" : "ListenHost"))));
					LogFields logFields = new LogFields().Text("TimestampUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)).Text("CallId", Guid.NewGuid().ToString("N")).Text("AnalyticsCallId", AnalyticsCorrelation.Current)
						.Text("ProcessRunId", Plugin.ProcessRunId)
						.Text("PeerSessionId", sessionID.ToString(CultureInfo.InvariantCulture))
						.Text("Role", value)
						.Flag("IsServer", flag)
						.Flag("IsDedicated", flag2)
						.Flag("IsSinglePlayer", flag3)
						.Text("VictimName", __instance.m_name)
						.Text("VictimObjectName", ((Object)__instance).name)
						.Text("VictimType", ((object)__instance).GetType().FullName)
						.Flag("VictimIsPlayer", __instance.IsPlayer())
						.Text("VictimZDOID", ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString())
						.Text("VictimOwnerBefore", zDO.GetOwner().ToString(CultureInfo.InvariantCulture))
						.Flag("IsOwnerBefore", component.IsOwner())
						.Number("HPBefore", health)
						.Text("HitType", ((object)Unsafe.As<HitType, HitType>(ref hit?.m_hitType)/*cast due to .constrained prefix*/).ToString())
						.Text("HitDataState", (hit == null) ? "Null" : "Present");
					if (hit != null)
					{
						logFields.Number("HitTotalDamageBefore", hit.GetTotalDamage());
						DamageTypes damage = hit.m_damage;
						logFields.Number("GenericBefore", damage.m_damage).Number("BluntBefore", damage.m_blunt).Number("SlashBefore", damage.m_slash)
							.Number("PierceBefore", damage.m_pierce)
							.Number("ChopBefore", damage.m_chop)
							.Number("PickaxeBefore", damage.m_pickaxe)
							.Number("FireBefore", damage.m_fire)
							.Number("FrostBefore", damage.m_frost)
							.Number("LightningBefore", damage.m_lightning)
							.Number("PoisonBefore", damage.m_poison)
							.Number("SpiritBefore", damage.m_spirit)
							.Number("NonPlayerBefore", damage.m_nonPlayer);
					}
					Character val = ((hit != null) ? hit.GetAttacker() : null);
					MagicAttributionProbe.ObserveDotTick(__instance, hit);
					MagicAttributionProbe.ObserveSummonHit(val);
					Player val2 = (Player)(object)((val is Player) ? val : null);
					string value2 = ((hit == null || ((ZDOID)(ref hit.m_attacker)).IsNone()) ? "None" : ((!Object.op_Implicit((Object)(object)val)) ? "Unresolved" : (Object.op_Implicit((Object)(object)val2) ? "Player" : "NPC")));
					logFields.Text("AttackerZDOID", ((object)Unsafe.As<ZDOID, ZDOID>(ref hit?.m_attacker)/*cast due to .constrained prefix*/).ToString()).Text("AttackerClass", value2).Text("GetAttackerResult", Object.op_Implicit((Object)(object)val) ? ((object)val).GetType().FullName : "null")
						.Text("AttackerName", Object.op_Implicit((Object)(object)val) ? val.m_name : null)
						.Text("AttackerObjectName", Object.op_Implicit((Object)(object)val) ? ((Object)val).name : null)
						.Text("AttackerPlayerID", Object.op_Implicit((Object)(object)val2) ? val2.GetPlayerID().ToString(CultureInfo.InvariantCulture) : null)
						.Text("AttackerPlayerName", Object.op_Implicit((Object)(object)val2) ? val2.GetPlayerName() : null);
					Player val3 = (Player)(object)((__instance is Player) ? __instance : null);
					long? victimPlayerId = (Object.op_Implicit((Object)(object)val3) ? Nonzero(val3.GetPlayerID()) : ((long?)null));
					long? attackerPlayerId = (Object.op_Implicit((Object)(object)val2) ? Nonzero(val2.GetPlayerID()) : ((long?)null));
					DamageFacts facts = new DamageFacts(((ZDOID)(ref zDO.m_uid)).UserID, ((ZDOID)(ref zDO.m_uid)).ID, __instance.IsPlayer(), victimPlayerId, (AttackerClass)Enum.Parse(typeof(AttackerClass), value2), attackerPlayerId, (byte)((hit != null) ? ((int)hit.m_hitType) : 0), Object.op_Implicit((Object)(object)val3) ? val3.GetPlayerName() : __instance.m_name, Object.op_Implicit((Object)(object)val2) ? val2.GetPlayerName() : (Object.op_Implicit((Object)(object)val) ? val.m_name : null), (hit == null || ((ZDOID)(ref hit.m_attacker)).IsNone()) ? 0 : ((ZDOID)(ref hit.m_attacker)).UserID, (hit != null && !((ZDOID)(ref hit.m_attacker)).IsNone()) ? ((ZDOID)(ref hit.m_attacker)).ID : 0u, (byte)((hit != null) ? (((int)hit.m_hitType == 7) ? 1 : (((int)hit.m_hitType == 5 && hit.m_damage.m_spirit > 0f && hit.m_damage.m_fire <= 0f) ? 3 : (((int)hit.m_hitType == 5) ? 2 : 0))) : 0));
					__state = new CallState(component, zDO, sessionID, health, logFields, facts);
					__state.Hit = hit;
					__state.AnalyticsInvocation = Guid.NewGuid();
					__state.VictimActor = AnalyticsRuntime.CaptureActor(__instance, val);
					__state.AttackerActor = AnalyticsRuntime.CaptureActor(val, __instance);
				}
			}
			catch (Exception ex)
			{
				__state = null;
				Plugin.Warn("DamageProbeCaptureFailed phase=Prefix exception=" + ex.GetType().Name);
			}
		}

		[HarmonyPostfix]
		internal static void Postfix(Character __instance, CallState __state)
		{
			//IL_00e0: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			if (__state == null)
			{
				return;
			}
			try
			{
				if (!Object.op_Implicit((Object)(object)__instance) || !Object.op_Implicit((Object)(object)__state.View) || !__state.View.IsValid() || __state.Zdo != __state.View.GetZDO() || ZDOMan.instance == null || ZDOMan.GetSessionID() != __state.Session || !__state.View.IsOwner() || __state.Zdo.GetOwner() != __state.Owner)
				{
					Plugin.Warn("DamageProbeObservationLost reason=VictimInvalidOrOwnershipChanged");
					return;
				}
				float health = __instance.GetHealth();
				if (!IsFinite(__state.HpBefore) || !IsFinite(health))
				{
					Plugin.Warn("DamageProbeObservationLost reason=NonFiniteHP");
					return;
				}
				float num = Math.Max(0f, __state.HpBefore - health);
				if (!(num > 0f) || !IsFinite(num))
				{
					return;
				}
				AnalyticsDamageEvidence analyticsDamageEvidence = null;
				if (__state.Hit != null)
				{
					DamageTypes damage = __state.Hit.m_damage;
					analyticsDamageEvidence = new AnalyticsDamageEvidence(__state.AnalyticsInvocation, __state.VictimActor, __state.AttackerActor, new DamageComponents(damage.m_blunt, damage.m_slash, damage.m_pierce, damage.m_fire, damage.m_frost, damage.m_lightning, damage.m_poison, damage.m_spirit), __state.Hit.GetTotalDamage(), health);
					AnalyticsRuntime.TrackDamage(__instance, __state.Hit, analyticsDamageEvidence);
				}
				Plugin.Publish(__state.Session, __state.Facts, num, analyticsDamageEvidence);
				__state.Fields.Text("CompletedUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)).Text("VictimOwner", __state.Zdo.GetOwner().ToString(CultureInfo.InvariantCulture)).Flag("IsOwner", __state.View.IsOwner())
					.Number("HPAfter", health)
					.Number("EffectiveHpLoss", num);
				if (Plugin.LoggingEnabled)
				{
					ManualLogSource probeLog = Plugin.ProbeLog;
					if (probeLog != null)
					{
						probeLog.LogDebug((object)("DamageProbeEvent " + __state.Fields));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("DamageProbeCaptureFailed phase=Postfix exception=" + ex.GetType().Name);
			}
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static long? Nonzero(long value)
		{
			if (value != 0L)
			{
				return value;
			}
			return null;
		}
	}
	internal static class CombatAnalyticsProbe
	{
		internal sealed class ShieldState
		{
			internal bool BreakLogged;
		}

		internal sealed class HitIdentity
		{
			internal string CallId;

			internal HitData Hit;

			internal string DeathId;
		}

		internal static readonly FieldInfo ShieldCharacter = AccessTools.Field(typeof(StatusEffect), "m_character");

		internal static readonly FieldInfo ShieldDamage = AccessTools.Field(typeof(SE_Shield), "m_damage");

		internal static readonly FieldInfo ShieldCapacity = AccessTools.Field(typeof(SE_Shield), "m_totalAbsorbDamage");

		internal static readonly FieldInfo LastHit = AccessTools.Field(typeof(Character), "m_lastHit");

		internal static readonly FieldInfo BlockTimer = AccessTools.Field(typeof(Humanoid), "m_blockTimer");

		internal static readonly MethodInfo Blocker = AccessTools.Method(typeof(Humanoid), "GetCurrentBlocker", (Type[])null, (Type[])null);

		internal static readonly FieldInfo AttackCharacter = AccessTools.Field(typeof(Attack), "m_character");

		internal static readonly FieldInfo ProjectileOwner = AccessTools.Field(typeof(Projectile), "m_owner");

		internal static readonly FieldInfo AoeOwner = AccessTools.Field(typeof(Aoe), "m_owner");

		internal static readonly ConditionalWeakTable<SE_Shield, ShieldState> Shields = new ConditionalWeakTable<SE_Shield, ShieldState>();

		internal static readonly ConditionalWeakTable<Character, HitIdentity> Hits = new ConditionalWeakTable<Character, HitIdentity>();

		internal static bool Enabled => Plugin.LoggingEnabled;

		internal static bool Owner(Character c)
		{
			if (!Object.op_Implicit((Object)(object)c))
			{
				return false;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid())
			{
				return component.IsOwner();
			}
			return false;
		}

		internal static LogFields Fields(string hook, Character victim, HitData hit = null)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			LogFields logFields = new LogFields().Text("TimestampUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)).Text("ProcessRunId", Plugin.ProcessRunId).Text("PeerSessionId", ZDOMan.GetSessionID().ToString(CultureInfo.InvariantCulture))
				.Text("Hook", hook)
				.Text("VictimZDOID", Object.op_Implicit((Object)(object)victim) ? ((object)victim.GetZDOID()/*cast due to .constrained prefix*/).ToString() : null)
				.Text("VictimToken", Object.op_Implicit((Object)(object)victim) ? victim.m_name : null)
				.Text("VictimObject", Object.op_Implicit((Object)(object)victim) ? ((Object)victim).name : null)
				.Flag("Owner", Owner(victim));
			if (Object.op_Implicit((Object)(object)victim))
			{
				ZNetView component = ((Component)victim).GetComponent<ZNetView>();
				ZDO val = ((component != null) ? component.GetZDO() : null);
				logFields.Text("VictimPrefabHash", (val != null) ? val.GetPrefab().ToString(CultureInfo.InvariantCulture) : null);
				if (val != null && Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(val.GetPrefab());
					logFields.Text("VictimPrefab", (prefab != null) ? ((Object)prefab).name : null);
				}
				logFields.Flag("VictimIsPlayer", victim.IsPlayer()).Flag("VictimIsTamed", victim.IsTamed());
				Player val2 = (Player)(object)((victim is Player) ? victim : null);
				if (val2 != null)
				{
					logFields.Text("PlayerID", val2.GetPlayerID().ToString(CultureInfo.InvariantCulture)).Text("PlayerName", val2.GetPlayerName());
				}
			}
			if (hit != null)
			{
				logFields.Text("AttackerZDOID", ((object)Unsafe.As<ZDOID, ZDOID>(ref hit.m_attacker)/*cast due to .constrained prefix*/).ToString()).Text("HitType", ((object)Unsafe.As<HitType, HitType>(ref hit.m_hitType)/*cast due to .constrained prefix*/).ToString());
				Character attacker = hit.GetAttacker();
				if (Object.op_Implicit((Object)(object)attacker))
				{
					if (Object.op_Implicit((Object)(object)victim))
					{
						logFields.Flag("VictimEnemyOfAttacker", BaseAI.IsEnemy(attacker, victim));
					}
					LogFields logFields2 = logFields.Text("AttackerToken", attacker.m_name).Text("AttackerObject", ((Object)attacker).name);
					ZNetView component2 = ((Component)attacker).GetComponent<ZNetView>();
					object value;
					if (component2 == null)
					{
						value = null;
					}
					else
					{
						ZDO zDO = component2.GetZDO();
						value = ((zDO != null) ? zDO.GetPrefab().ToString(CultureInfo.InvariantCulture) : null);
					}
					logFields2.Text("AttackerPrefabHash", (string)value).Flag("AttackerIsPlayer", attacker.IsPlayer()).Flag("AttackerIsTamed", attacker.IsTamed());
					Player val3 = (Player)(object)((attacker is Player) ? attacker : null);
					if (val3 != null)
					{
						logFields.Text("AttackerPlayerID", val3.GetPlayerID().ToString(CultureInfo.InvariantCulture));
					}
					ZNetView component3 = ((Component)attacker).GetComponent<ZNetView>();
					ZDO val4 = ((component3 != null) ? component3.GetZDO() : null);
					if (val4 != null && Object.op_Implicit((Object)(object)ZNetScene.instance))
					{
						GameObject prefab2 = ZNetScene.instance.GetPrefab(val4.GetPrefab());
						logFields.Text("AttackerPrefab", (prefab2 != null) ? ((Object)prefab2).name : null);
					}
				}
			}
			return logFields;
		}

		internal static void Emit(string family, LogFields fields)
		{
			try
			{
				ManualLogSource probeLog = Plugin.ProbeLog;
				if (probeLog != null)
				{
					probeLog.LogDebug((object)("CombatAnalytics" + family + "Probe " + fields));
				}
			}
			catch
			{
			}
		}

		internal static void Components(LogFields f, string suffix, DamageTypes d)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			f.Number("Generic" + suffix, d.m_damage).Number("Blunt" + suffix, d.m_blunt).Number("Slash" + suffix, d.m_slash)
				.Number("Pierce" + suffix, d.m_pierce)
				.Number("Chop" + suffix, d.m_chop)
				.Number("Pickaxe" + suffix, d.m_pickaxe)
				.Number("Fire" + suffix, d.m_fire)
				.Number("Frost" + suffix, d.m_frost)
				.Number("Lightning" + suffix, d.m_lightning)
				.Number("Poison" + suffix, d.m_poison)
				.Number("Spirit" + suffix, d.m_spirit)
				.Number("NonPlayer" + suffix, d.m_nonPlayer);
		}

		internal static void Failed(Exception ex)
		{
			Plugin.Warn("CombatAnalyticsProbeFailed exception=" + ex.GetType().Name);
		}
	}
	[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
	internal static class AnalyticsBlockPatch
	{
		internal sealed class State
		{
			internal State Parent;

			internal HitData Hit;

			internal LogFields Fields;

			internal bool TimedCandidate;

			internal bool Applied;

			internal float Prevented;

			internal Character Victim;

			internal readonly Guid Event = Guid.NewGuid();
		}

		[ThreadStatic]
		internal static State Current;

		[HarmonyPrefix]
		internal static void Prefix(Humanoid __instance, HitData hit, out State __state)
		{
			__state = null;
			if (!CombatAnalyticsProbe.Owner((Character)(object)__instance))
			{
				return;
			}
			try
			{
				object? obj = CombatAnalyticsProbe.Blocker.Invoke(__instance, null);
				ItemData val = (ItemData)((obj is ItemData) ? obj : null);
				float num = (float)CombatAnalyticsProbe.BlockTimer.GetValue(__instance);
				__state = new State
				{
					Parent = Current,
					Hit = hit,
					Victim = (Character)(object)__instance,
					TimedCandidate = (val != null && val.m_shared.m_timedBlockBonus > 1f && num != -1f && num < 0.25f),
					Fields = (CombatAnalyticsProbe.Enabled ? CombatAnalyticsProbe.Fields("Humanoid.BlockAttack", (Character)(object)__instance, hit).Number("Incoming", hit.GetTotalDamage()).Number("BlockTimer", num) : null)
				};
				Current = __state;
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		[HarmonyPostfix]
		internal static void Postfix(HitData hit, bool __result, State __state)
		{
			if (__state == null)
			{
				return;
			}
			try
			{
				AnalyticsRuntime.Block(__state.Victim, __state.Applied, __state.TimedCandidate, __state.Prevented, __state.Event, new ActorId(((ZDOID)(ref hit.m_attacker)).UserID, ((ZDOID)(ref hit.m_attacker)).ID), hit.GetAttacker());
				if (__state.Fields != null)
				{
					CombatAnalyticsProbe.Emit("Block", __state.Fields.Flag("MethodResult", __result).Flag("MitigationApplied", __state.Applied).Number("BlockDamageRemoved", __state.Prevented)
						.Flag("TimedBlockCandidate", __state.TimedCandidate)
						.Flag("SuccessfulTimedBlock", __state.Applied && __state.TimedCandidate)
						.Number("Outgoing", hit.GetTotalDamage()));
				}
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		[HarmonyFinalizer]
		internal static Exception Finalizer(Exception __exception, State __state)
		{
			if (__state != null)
			{
				Current = __state.Parent;
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(HitData), "BlockDamage")]
	internal static class AnalyticsBlockDamagePatch
	{
		[HarmonyPrefix]
		internal static void Prefix(HitData __instance, out float __state)
		{
			__state = ((AnalyticsBlockPatch.Current?.Hit == __instance) ? __instance.GetTotalBlockableDamage() : (-1f));
		}

		[HarmonyPostfix]
		internal static void Postfix(HitData __instance, float __state)
		{
			AnalyticsBlockPatch.State current = AnalyticsBlockPatch.Current;
			if (!(__state < 0f) && current != null && current.Hit == __instance)
			{
				current.Applied = true;
				current.Prevented += Math.Max(0f, __state - __instance.GetTotalBlockableDamage());
			}
		}
	}
	[HarmonyPatch]
	internal static class AnalyticsDodgePatch
	{
		internal static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Attack), "DoMeleeAttack", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Attack), "<DoAreaAttack>g__checkHits|27_0", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Projectile), "IsValidTarget", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Aoe), "ShouldHit", (Type[])null, (Type[])null);
		}

		internal static bool Observe(Character victim, string hook, object source)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			bool flag = victim.IsDodgeInvincible();
			if (flag)
			{
				try
				{
					AnalyticsRuntime.RejectedDodge(victim, source);
				}
				catch (Exception ex)
				{
					CombatAnalyticsProbe.Failed(ex);
				}
			}
			if (flag && CombatAnalyticsProbe.Enabled)
			{
				try
				{
					LogFields logFields = CombatAnalyticsProbe.Fields(hook, victim, (HitData)((source is HitData) ? source : null)).Flag("RejectedByIFrames", value: true).Text("Semantics", "rejected candidate; not deduplicated attack count")
						.Text("SourceInstance", RuntimeHelpers.GetHashCode(source).ToString(CultureInfo.InvariantCulture))
						.Text("SourceType", source.GetType().FullName);
					Character val = ((!(source is Attack)) ? ((!(source is Projectile)) ? ((!(source is Aoe)) ? ((Character)null) : ((Character)CombatAnalyticsProbe.AoeOwner.GetValue(source))) : ((Character)CombatAnalyticsProbe.ProjectileOwner.GetValue(source))) : ((Character)CombatAnalyticsProbe.AttackCharacter.GetValue(source)));
					if (Object.op_Implicit((Object)(object)val))
					{
						logFields.Text("AttackerZDOID", ((object)val.GetZDOID()/*cast due to .constrained prefix*/).ToString()).Text("AttackerToken", val.m_name);
					}
					CombatAnalyticsProbe.Emit("Dodge", logFields);
				}
				catch (Exception ex2)
				{
					CombatAnalyticsProbe.Failed(ex2);
				}
			}
			return flag;
		}

		[HarmonyTranspiler]
		internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>();
			MethodInfo methodInfo = AccessTools.Method(typeof(Character), "IsDodgeInvincible", (Type[])null, (Type[])null);
			int num = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, methodInfo))
				{
					num++;
					CodeInstruction item = new CodeInstruction(instruction)
					{
						opcode = OpCodes.Ldstr,
						operand = __originalMethod.DeclaringType.Name + "." + __originalMethod.Name
					};
					list.Add(item);
					list.Add(new CodeInstruction((__originalMethod.DeclaringType == typeof(Character)) ? OpCodes.Ldarg_2 : OpCodes.Ldarg_0, (object)null));
					list.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(AnalyticsDodgePatch), "Observe", (Type[])null, (Type[])null)));
				}
				else
				{
					list.Add(instruction);
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Analytics dodge hook requires exactly one verified predicate: " + __originalMethod);
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(SE_Shield), "OnDamaged")]
	internal static class AnalyticsBarrierDamagePatch
	{
		internal sealed class State
		{
			internal Character Victim;

			internal float Incoming;

			internal readonly Guid Event = Guid.NewGuid();

			internal LogFields Fields;
		}

		[HarmonyPrefix]
		internal static void Prefix(SE_Shield __instance, HitData hit, out State __state)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			__state = null;
			try
			{
				Character val = (Character)CombatAnalyticsProbe.ShieldCharacter.GetValue(__instance);
				if (CombatAnalyticsProbe.Owner(val))
				{
					__state = new State
					{
						Victim = val,
						Incoming = hit.GetTotalDamage()
					};
					if (CombatAnalyticsProbe.Enabled)
					{
						__state.Fields = CombatAnalyticsProbe.Fields("SE_Shield.OnDamaged", val, hit).Text("ShieldName", ((Object)__instance).name).Text("ShieldToken", ((StatusEffect)__instance).m_name)
							.Text("ShieldInstance", RuntimeHelpers.GetHashCode(__instance).ToString(CultureInfo.InvariantCulture))
							.Number("IncomingAtShield", hit.GetTotalDamage())
							.Number("ConsumedBefore", (float)CombatAnalyticsProbe.ShieldDamage.GetValue(__instance))
							.Number("Capacity", (float)CombatAnalyticsProbe.ShieldCapacity.GetValue(__instance));
					}
				}
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		[HarmonyPostfix]
		internal static void Postfix(SE_Shield __instance, HitData hit, State __state)
		{
			if (__state == null)
			{
				return;
			}
			try
			{
				AnalyticsRuntime.ShieldHit(__instance, __state.Victim, __state.Event, __state.Incoming, hit.GetTotalDamage(), new ActorId(((ZDOID)(ref hit.m_attacker)).UserID, ((ZDOID)(ref hit.m_attacker)).ID), hit.GetAttacker());
				if (__state.Fields != null)
				{
					CombatAnalyticsProbe.Emit("Barrier", __state.Fields.Number("ConsumedAfter", (float)CombatAnalyticsProbe.ShieldDamage.GetValue(__instance)).Number("OutgoingFromShield", hit.GetTotalDamage()));
				}
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}
	}
	[HarmonyPatch(typeof(SE_Shield), "IsDone")]
	internal static class AnalyticsBarrierBreakPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(SE_Shield __instance, bool __result)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			if (!__result)
			{
				return;
			}
			try
			{
				Character val = (Character)CombatAnalyticsProbe.ShieldCharacter.GetValue(__instance);
				if (!CombatAnalyticsProbe.Owner(val))
				{
					return;
				}
				float num = (float)CombatAnalyticsProbe.ShieldDamage.GetValue(__instance);
				float num2 = (float)CombatAnalyticsProbe.ShieldCapacity.GetValue(__instance);
				AnalyticsRuntime.ShieldDone(__instance, val, num, num2);
				if (CombatAnalyticsProbe.Enabled)
				{
					CombatAnalyticsProbe.ShieldState orCreateValue = CombatAnalyticsProbe.Shields.GetOrCreateValue(__instance);
					if (!orCreateValue.BreakLogged)
					{
						orCreateValue.BreakLogged = true;
						CombatAnalyticsProbe.Emit("Barrier", CombatAnalyticsProbe.Fields("SE_Shield.IsDone", val).Text("ShieldName", ((Object)__instance).name).Text("ShieldToken", ((StatusEffect)__instance).m_name)
							.Text("ShieldInstance", RuntimeHelpers.GetHashCode(__instance).ToString(CultureInfo.InvariantCulture))
							.Number("Consumed", num)
							.Number("Capacity", num2)
							.Flag("CapacityBreak", num > num2));
					}
				}
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}
	}
	[HarmonyPatch(typeof(SE_Shield), "SetLevel")]
	internal static class AnalyticsBarrierLevelPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(SE_Shield __instance, int itemLevel, float skillLevel)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if (!CombatAnalyticsProbe.Enabled)
			{
				return;
			}
			try
			{
				Character val = (Character)CombatAnalyticsProbe.ShieldCharacter.GetValue(__instance);
				if (CombatAnalyticsProbe.Owner(val))
				{
					CombatAnalyticsProbe.Emit("Barrier", CombatAnalyticsProbe.Fields("SE_Shield.SetLevel", val).Text("ShieldName", ((Object)__instance).name).Text("ShieldToken", ((StatusEffect)__instance).m_name)
						.Text("ShieldInstance", RuntimeHelpers.GetHashCode(__instance).ToString(CultureInfo.InvariantCulture))
						.Number("ItemLevel", itemLevel)
						.Number("SkillLevel", skillLevel)
						.Number("Consumed", (float)CombatAnalyticsProbe.ShieldDamage.GetValue(__instance))
						.Number("Capacity", (float)CombatAnalyticsProbe.ShieldCapacity.GetValue(__instance)));
				}
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}
	}
	[HarmonyPatch(typeof(Character), "OnDeath")]
	internal static class AnalyticsDeathPatch
	{
		[HarmonyPrefix]
		internal static void Prefix(Character __instance)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			if (!CombatAnalyticsProbe.Owner(__instance))
			{
				return;
			}
			AnalyticsRuntime.ConfirmDeath(__instance);
			if (!CombatAnalyticsProbe.Enabled)
			{
				return;
			}
			try
			{
				CombatAnalyticsProbe.Emit("Death", CombatAnalyticsProbe.Fields("Character.OnDeath", __instance, (HitData)CombatAnalyticsProbe.LastHit.GetValue(__instance)).Number("HP", __instance.GetHealth()).Text("FinalDamageCallId", FinalCall(__instance))
					.Text("DeathObservationId", DeathId(__instance)));
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		private static string FinalCall(Character c)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			HitData val = (HitData)CombatAnalyticsProbe.LastHit.GetValue(c);
			if (val == null || !CombatAnalyticsProbe.Hits.TryGetValue(c, out var value) || value.Hit != val)
			{
				return null;
			}
			return value.CallId;
		}

		private static string DeathId(Character c)
		{
			CombatAnalyticsProbe.HitIdentity orCreateValue = CombatAnalyticsProbe.Hits.GetOrCreateValue(c);
			return orCreateValue.DeathId ?? (orCreateValue.DeathId = Guid.NewGuid().ToString("N"));
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage", new Type[]
	{
		typeof(HitData),
		typeof(bool),
		typeof(bool),
		typeof(DamageModifier)
	})]
	internal static class AnalyticsDamagePatch
	{
		internal sealed class State
		{
			internal LogFields Fields;

			internal float Hp;

			internal AnalyticsCorrelation Scope;

			internal string Id;
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		internal static void Prefix(Character __instance, HitData hit, out State __state)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			if (!CombatAnalyticsProbe.Enabled || !CombatAnalyticsProbe.Owner(__instance) || hit == null)
			{
				return;
			}
			try
			{
				string text = Guid.NewGuid().ToString("N");
				__state = new State
				{
					Id = text,
					Hp = __instance.GetHealth(),
					Fields = CombatAnalyticsProbe.Fields("Character.ApplyDamage", __instance, hit).Text("CallId", text).Number("InputTotal", hit.GetTotalDamage())
				};
				CombatAnalyticsProbe.Components(__state.Fields, "Before", hit.m_damage);
				__state.Scope = new AnalyticsCorrelation(text);
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		[HarmonyPostfix]
		internal static void Postfix(Character __instance, HitData hit, State __state)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (__state == null || !Object.op_Implicit((Object)(object)__instance))
			{
				return;
			}
			try
			{
				CombatAnalyticsProbe.Components(__state.Fields, "After", hit.m_damage);
				if (__instance.GetHealth() < __state.Hp && CombatAnalyticsProbe.LastHit.GetValue(__instance) == hit)
				{
					CombatAnalyticsProbe.HitIdentity orCreateValue = CombatAnalyticsProbe.Hits.GetOrCreateValue(__instance);
					orCreateValue.CallId = __state.Id;
					orCreateValue.Hit = hit;
				}
				CombatAnalyticsProbe.Emit("DamageType", __state.Fields.Number("HPBefore", __state.Hp).Number("HPAfter", __instance.GetHealth()).Number("EffectiveHpLossObserved", Math.Max(0f, __state.Hp - __instance.GetHealth()))
					.Number("PostProcessedTotal", hit.GetTotalDamage())
					.Flag("StillOwner", CombatAnalyticsProbe.Owner(__instance)));
			}
			catch (Exception ex)
			{
				CombatAnalyticsProbe.Failed(ex);
			}
		}

		[HarmonyFinalizer]
		internal static Exception Finalizer(Exception __exception, State __state)
		{
			__state?.Scope?.Dispose();
			return __exception;
		}
	}
	internal sealed class CompatibilityRuntime
	{
		internal const string Rpc = "CombatMeter.Compatibility.v1";

		private static CompatibilityRuntime _instance;

		private readonly Plugin _owner;

		private readonly HashSet<ZNetPeer> _rejections = new HashSet<ZNetPeer>();

		private ZNet _net;

		private readonly CompatibilityRegistry<ZNetPeer> _states = new CompatibilityRegistry<ZNetPeer>();

		private readonly Dictionary<ZNetPeer, (ZRpc Rpc, Coroutine Timeout)> _bindings = new Dictionary<ZNetPeer, (ZRpc, Coroutine)>();

		private CompatibilityRuntime(Plugin owner)
		{
			_owner = owner;
		}

		internal static void Initialize(Plugin owner)
		{
			Shutdown();
			_instance = new CompatibilityRuntime(owner);
			Bind(ZNet.instance);
		}

		internal static void Shutdown()
		{
			_instance?.Clear();
			_instance = null;
		}

		internal static void Bind(ZNet net)
		{
			if (_instance == null || !Object.op_Implicit((Object)(object)net) || _instance._net == net)
			{
				return;
			}
			_instance.Clear();
			_instance._net = net;
			foreach (ZNetPeer item in new List<ZNetPeer>(net.GetPeers()))
			{
				Connected(net, item);
			}
		}

		internal static bool IsReady(ZNetPeer peer)
		{
			CompatibilityRuntime instance = _instance;
			if (instance != null && instance._net == ZNet.instance && peer != null)
			{
				CompatibilityHandshake compatibilityHandshake = instance._states.Get(peer);
				if (compatibilityHandshake != null && compatibilityHandshake.Ready && instance._bindings.TryGetValue(peer, out (ZRpc, Coroutine) value))
				{
					return value.Item1 == peer.m_rpc;
				}
			}
			return false;
		}

		internal static Guid EvidenceScope(ZNetPeer peer, bool remote)
		{
			if (!IsReady(peer))
			{
				return Guid.Empty;
			}
			CompatibilityHandshake compatibilityHandshake = _instance._states.Get(peer);
			if (!remote)
			{
				return compatibilityHandshake.Scope;
			}
			return compatibilityHandshake.RemoteScope;
		}

		internal static void Connected(ZNet net, ZNetPeer peer)
		{
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			Bind(net);
			CompatibilityRuntime i = _instance;
			if (i == null || !Object.op_Implicit((Object)(object)net) || peer == null || net != i._net || i._states.Get(peer) != null)
			{
				return;
			}
			if (!i._states.Start(peer, "1.1.2", 2, out var state))
			{
				Plugin.Warn("CombatMeterCompatibilityRejected peer=" + peer.m_uid + " reason=HandshakeCapacity");
				net.Disconnect(peer);
				return;
			}
			ZRpc rpc = peer.m_rpc;
			i._bindings[peer] = (rpc, null);
			try
			{
				rpc.Register<ZPackage>("CombatMeter.Compatibility.v1", (Action<ZRpc, ZPackage>)delegate(ZRpc sender, ZPackage pkg)
				{
					try
					{
						i.Receive(peer, state, sender, pkg);
					}
					catch
					{
						if (i.Current(peer, state, sender))
						{
							state.Reject("HandshakeUnavailable");
							i.Reject(peer, state);
						}
					}
				});
				i._bindings[peer] = (rpc, ((MonoBehaviour)i._owner).StartCoroutine(i.Timeout(peer, state)));
				rpc.Invoke("CombatMeter.Compatibility.v1", new object[1] { (object)new ZPackage(state.Offer()) });
			}
			catch
			{
				state.Reject("HandshakeUnavailable");
				i.Reject(peer, state);
			}
		}

		private bool Current(ZNetPeer peer, CompatibilityHandshake state, ZRpc rpc)
		{
			if (_net == ZNet.instance && _states.Get(peer) == state && _bindings.TryGetValue(peer, out (ZRpc, Coroutine) value) && value.Item1 == rpc)
			{
				return peer.m_rpc == rpc;
			}
			return false;
		}

		private void Receive(ZNetPeer peer, CompatibilityHandshake state, ZRpc rpc, ZPackage package)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			if (!Current(peer, state, rpc))
			{
				return;
			}
			bool ready = state.Ready;
			byte[] array = state.Receive((package != null && package.Size() <= 96) ? package.GetArray() : null);
			if (state.Rejection != null)
			{
				Reject(peer, state);
				return;
			}
			if (array != null)
			{
				rpc.Invoke("CombatMeter.Compatibility.v1", new object[1] { (object)new ZPackage(array) });
			}
			if (!ready && state.Ready)
			{
				CancelTimer(peer);
				Plugin.TransportLog("CombatMeterCompatibilityAccepted peer=" + peer.m_uid + " localVersion=" + state.LocalVersion + " remoteVersion=" + state.RemoteVersion + " localCompat=" + state.LocalCompatibility + " remoteCompat=" + state.RemoteCompatibility);
			}
		}

		private IEnumerator Timeout(ZNetPeer peer, CompatibilityHandshake state)
		{
			yield return (object)new WaitForSecondsRealtime(10f);
			if (Current(peer, state, peer.m_rpc) && !state.Ready)
			{
				state.Timeout();
				Reject(peer, state);
			}
		}

		private void Reject(ZNetPeer peer, CompatibilityHandshake state)
		{
			if (_states.Get(peer) != state || !_rejections.Add(peer))
			{
				return;
			}
			CancelTimer(peer);
			Plugin.Warn("CombatMeterCompatibilityRejected peer=" + peer.m_uid + " reason=" + state.Rejection + " localVersion=" + state.LocalVersion + " remoteVersion=" + (state.RemoteVersion ?? "unknown") + " localCompat=" + state.LocalCompatibility + " remoteCompat=" + state.RemoteCompatibility + ". Install the same CombatMeter build on host and clients.");
			try
			{
				if (_net.IsServer())
				{
					peer.m_rpc.Invoke("Error", new object[1] { 3 });
				}
				else
				{
					ZNet.SetExternalError((ConnectionStatus)3);
					peer.m_rpc.Invoke("Disconnect", Array.Empty<object>());
				}
			}
			finally
			{
				_net.Disconnect(peer);
			}
		}

		private void CancelTimer(ZNetPeer peer)
		{
			if (_bindings.TryGetValue(peer, out (ZRpc, Coroutine) value) && value.Item2 != null)
			{
				((MonoBehaviour)_owner).StopCoroutine(value.Item2);
				_bindings[peer] = (value.Item1, null);
			}
		}

		internal static void Disconnected(ZNet net, ZNetPeer peer)
		{
			CompatibilityRuntime instance = _instance;
			if (instance != null && net == instance._net && peer != null)
			{
				instance.CancelTimer(peer);
				instance._rejections.Remove(peer);
				instance._states.Remove(peer);
				instance._bindings.Remove(peer);
			}
		}

		internal static void Stop(ZNet net)
		{
			if (_instance != null && _instance._net == net)
			{
				_instance.Clear();
			}
		}

		private void Clear()
		{
			foreach (var value in _bindings.Values)
			{
				if (value.Timeout != null)
				{
					((MonoBehaviour)_owner).StopCoroutine(value.Timeout);
				}
			}
			_bindings.Clear();
			_rejections.Clear();
			_states.Clear();
			_net = null;
		}
	}
	internal sealed class DamageCommitTransport
	{
		internal readonly struct DeathObservation
		{
			internal readonly string Result;

			internal readonly EncounterState StateBefore;

			internal readonly EncounterState StateAfter;

			internal DeathObservation(string result, EncounterState before, EncounterState after)
			{
				Result = result;
				StateBefore = before;
				StateAfter = after;
			}
		}

		internal const string CommitRpc = "CombatMeter.DamageCommit.v1";

		internal const string AckRpc = "CombatMeter.DamageCommitAck.v1";

		internal const string AttributionRpc = "CombatMeter.MagicAttribution.v1";

		internal const string AttributionAckRpc = "CombatMeter.MagicAttributionAck.v1";

		internal const string SnapshotRpc = "CombatMeter.CombatSnapshot.v2";

		internal const string DetailsRpc = "CombatMeter.CombatDetails.v1";

		internal const string HistoryRequestRpc = "CombatMeter.CombatHistory.v1.Request";

		internal const string HistoryResponseRpc = "CombatMeter.CombatHistory.v1.Response";

		internal const string StatisticsRequestRpc = "CombatMeter.CombatHistory.Statistics.v1.Request";

		internal const string StatisticsResponseRpc = "CombatMeter.CombatHistory.Statistics.v1.Response";

		internal const string StatisticsSnapshotRequestRpc = "CombatMeter.CombatStatistics.v1.Request";

		internal const string StatisticsSnapshotResponseRpc = "CombatMeter.CombatStatistics.v1.Response";

		internal const string HistoryRevisionRpc = "CombatMeter.CombatStatistics.v1.Changed";

		private StatisticsRevision _statisticsObserved;

		private StatisticsRevision _statisticsDeferred;

		private StatisticsRevision _statisticsApplied;

		private readonly HashSet<long> _statisticsPendingSnapshots = new HashSet<long>();

		private long _statisticsPublishedHistory = -1L;

		private long _statisticsPublishedSession = -1L;

		private HistoryReadService _historyRead;

		private readonly HistoryReadCache _historyCache = new HistoryReadCache(Plugin.HistoryLog);

		private readonly Dictionary<long, (ZNetPeer Peer, Guid Connection, long Request, double Window, int Count)> _historyReaders = new Dictionary<long, (ZNetPeer, Guid, long, double, int)>();

		private readonly Dictionary<long, (double Expires, Action<HistoryReadResult, string> Callback)> _historyProbeRequests = new Dictionary<long, (double, Action<HistoryReadResult, string>)>();

		internal const double SnapshotIntervalSeconds = 0.5;

		private readonly ConditionalWeakTable<ZRoutedRpc, object> _registrations = new ConditionalWeakTable<ZRoutedRpc, object>();

		private ZNet _net;

		private ZNetPeer _historyServerConnection;

		private ZRoutedRpc _rpc;

		private WeakReference<ZNet> _blockedNet;

		private CommitSession _session;

		private EncounterManager _encounter;

		private CombatClusterManager _clusters;

		private CombatMeterPerformance _performance;

		private SessionPlayerIdentity _identities;

		private Func<long, string> _displayName;

		private EncounterAnalyticsSnapshot _lastAnalyticsSummary;

		private readonly Func<string, bool> _knownSummon;

		private readonly Func<long> _performanceTimestamp;

		private MagicAttributionResolver _attribution;

		private EventSequence _attributionSequence;

		private double _clockAnchorMonotonic;

		private long _clockAnchorUtcTicks;

		private readonly CombatSnapshotStore _snapshotStore = new CombatSnapshotStore();

		private readonly CombatDetailsStore _detailsStore = new CombatDetailsStore();

		private readonly CombatDetailsRetention _detailsRetention = new CombatDetailsRetention();

		private Guid _detailsExpectedEpoch;

		private EncounterHistoryStore _history;

		private readonly Func<EncounterHistoryStore> _historyFactory;

		private Guid _snapshotEpoch;

		private long _snapshotSequence;

		private double _nextSnapshotAt;

		private string _lastLocalSnapshotDiagnosticKey;

		private string _lastPublishedSnapshotDiagnosticKey;

		private string _lastAcceptedSnapshotDiagnosticKey;

		private double _nextSnapshotReceivedDiagnosticAt;

		private readonly AttributionAcceptor _attributionAcceptor = new AttributionAcceptor();

		private readonly AttributionOutbox _attributionOutbox = new AttributionOutbox();

		private readonly List<(DamageCommit Commit, double AcceptedAt, double Expires)> _pendingSummon = new List<(DamageCommit, double, double)>();

		private readonly List<(DamageCommit Commit, double AcceptedAt, double Expires)> _pendingDot = new List<(DamageCommit, double, double)>();

		private readonly Dictionary<EventId, List<(long Player, DefenseObservation Observation)>> _pendingDefense = new Dictionary<EventId, List<(long, DefenseObservation)>>();

		private int _pendingDefenseCount;

		private bool _disposed;

		private readonly Func<double> _now;

		private readonly Func<double> _softTimeout;

		private readonly Func<double> _recoveryTimeout;

		private readonly Func<double> _dpsIdleTimeout;

		private EncounterSettings _encounterSettings;

		internal const string AnalyticsRpc = "CombatMeter.AnalyticsEvidence.v1";

		internal const string AnalyticsAckRpc = "CombatMeter.AnalyticsEvidence.v1.Ack";

		private readonly Queue<(AnalyticsWireEvent Event, double Expires)> _analyticsOutbox = new Queue<(AnalyticsWireEvent, double)>();

		private CommitAcceptor _analyticsReplay = new CommitAcceptor();

		private long _analyticsSequence;

		private double _analyticsNextSend;

		private readonly Dictionary<(long, Guid, Guid), (AnalyticsDamageEvidence Evidence, EventId DamageId, double Expires)> _analyticsDeaths = new Dictionary<(long, Guid, Guid), (AnalyticsDamageEvidence, EventId, double)>();

		private readonly Queue<((long, Guid, Guid) Key, double Expires)> _analyticsDeathExpiry = new Queue<((long, Guid, Guid), double)>();

		internal HistoryReadService HistoryRead => _historyRead;

		internal HistoryReadCache HistoryCache => _historyCache;

		internal EncounterManager Encounter => _encounter;

		internal CombatClusterManager Clusters => _clusters;

		internal EncounterAnalyticsSnapshot LastFinishedAnalytics => _clusters?.LastFinished?.Encounter.LastFinishedAnalytics;

		internal CombatMeterPerformance Performance => _performance;

		internal MagicAttributionResolver Attribution => _attribution;

		internal int AttributionPendingCount => _attributionOutbox.Count;

		internal CombatSnapshotStore SnapshotStore => _snapshotStore;

		internal CombatDetailsStore DetailsStore => _detailsStore;

		internal EncounterHistoryStore HistoryStore => _history;

		internal bool HasActiveSession => _session != null;

		internal event Action<StatisticsRevision> HistoryChanged;

		internal DamageCommitTransport(Func<double> now = null, Func<double> softTimeout = null, Func<double> recoveryTimeout = null, Func<double> dpsIdleTimeout = null, Func<long> performanceTimestamp = null, Func<EncounterHistoryStore> historyFactory = null)
		{
			_now = now ?? ((Func<double>)(() => (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency));
			_softTimeout = softTimeout;
			_recoveryTimeout = recoveryTimeout;
			_dpsIdleTimeout = dpsIdleTimeout;
			_performanceTimestamp = performanceTimestamp;
			_historyFactory = historyFactory;
			_detailsRetention.CompletedSnapshot += CaptureHistory;
			_knownSummon = (string id) => _attribution != null && _attribution.Summons.TryResolve(id, out var _);
		}

		internal void Bind(ZNet net)
		{
			if (_disposed || !Object.op_Implicit((Object)(object)net) || net.IsDedicated() || IsBlocked(net))
			{
				return;
			}
			ZRoutedRpc rpc = ZRoutedRpc.instance;
			if (rpc == null || ZDOMan.instance == null)
			{
				return;
			}
			if (net != _net || rpc != _rpc)
			{
				Close("NetworkInstanceChanged");
				_net = net;
				_rpc = rpc;
				_blockedNet = null;
			}
			if (!_registrations.TryGetValue(rpc, out var _))
			{
				_registrations.Add(rpc, new object());
				try
				{
					RegisterAnalytics(rpc);
					rpc.Register<ZPackage>("CombatMeter.DamageCommit.v1", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						Receive(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.DamageCommitAck.v1", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveAck(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.MagicAttribution.v1", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveAttribution(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.MagicAttributionAck.v1", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveAttributionAck(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatSnapshot.v2", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveSnapshot(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatDetails.v1", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveDetails(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatHistory.v1.Request", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveHistoryRequest(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatHistory.v1.Response", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveHistoryResponse(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatHistory.Statistics.v1.Request", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveHistoryRequest(rpc, sender, pkg, statistics: true);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatHistory.Statistics.v1.Response", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveHistoryResponse(rpc, sender, pkg, statistics: true);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatStatistics.v1.Changed", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveHistoryRevision(rpc, sender, pkg);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatStatistics.v1.Request", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveHistoryRequest(rpc, sender, pkg, statistics: false, snapshot: true);
					});
					rpc.Register<ZPackage>("CombatMeter.CombatStatistics.v1.Response", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
					{
						ReceiveHistoryResponse(rpc, sender, pkg, statistics: false, snapshot: true);
					});
				}
				catch
				{
					_blockedNet = new WeakReference<ZNet>(net);
					Close("RpcRegistrationFailed");
					throw;
				}
			}
			EnsureSession();
		}

		private bool EnsureSession()
		{
			if (_disposed || !Object.op_Implicit((Object)(object)_net) || IsBlocked(_net) || _net != ZNet.instance || _rpc != ZRoutedRpc.instance || ZDOMan.instance == null)
			{
				return false;
			}
			long sessionID = ZDOMan.GetSessionID();
			if (sessionID == 0L)
			{
				return false;
			}
			bool flag = _net.IsServer();
			if (!flag && !CompatibilityRuntime.IsReady(_net.GetServerPeer()))
			{
				return false;
			}
			if (!flag && _session != null && _historyServerConnection != _net.GetServerPeer())
			{
				Close("ServerConnectionChanged");
			}
			if (_session != null && (_session.Peer != sessionID || _session.IsHost != flag))
			{
				Close("PeerChanged");
			}
			if (_session == null)
			{
				_encounterSettings = (flag ? new EncounterSettings(ReadSoftTimeout(), ReadRecoveryTimeout(), ReadDpsIdleTimeout()) : null);
				_encounter = (flag ? new EncounterManager(new CombatStatisticsAggregator(), _encounterSettings, Plugin.EncounterLog, analyticsEnabled: false) : null);
				_clusters = (flag ? new CombatClusterManager(_encounterSettings) : null);
				if (flag)
				{
					_clusters.Finished += RetainDetails;
				}
				_identities = (flag ? new SessionPlayerIdentity(Plugin.TransportLog, ObserveDisplayName) : null);
				_displayName = (flag ? new Func<long, string>(_identities.ResolveDisplayName) : null);
				if (flag)
				{
					_identities.Sync(_net.GetPeers(), sessionID, HostPlayerIdentity.ResolveLocal(sessionID));
				}
				if (flag)
				{
					LogEncounterSettings("CombatEncounterSettings");
				}
				_attribution = (flag ? new MagicAttributionResolver() : null);
				_attributionSequence = new EventSequence(sessionID, Guid.NewGuid());
				_clockAnchorMonotonic = _now();
				_clockAnchorUtcTicks = DateTime.UtcNow.Ticks;
				_snapshotStore.Clear();
				_detailsStore.Clear();
				_detailsRetention.Clear();
				_detailsExpectedEpoch = Guid.Empty;
				_snapshotEpoch = Guid.NewGuid();
				if (flag)
				{
					try
					{
						_history = (_historyFactory ?? new Func<EncounterHistoryStore>(HistoryRuntime.Open))();
					}
					catch (Exception ex)
					{
						Plugin.HistoryLog("CombatHistoryLoadFailed reason=" + ex.GetType().Name + " historyDisabled=true");
					}
				}
				_snapshotSequence = 0L;
				_nextSnapshotAt = _clockAnchorMonotonic;
				_lastLocalSnapshotDiagnosticKey = null;
				_lastPublishedSnapshotDiagnosticKey = null;
				_lastAcceptedSnapshotDiagnosticKey = null;
				_nextSnapshotReceivedDiagnosticAt = 0.0;
				_session = new CommitSession(sessionID, Guid.NewGuid(), flag, Send, Log, flag ? new Action<DamageCommit>(AcceptForAttribution) : null);
				_historyServerConnection = (flag ? null : _net.GetServerPeer());
				if (flag && _history != null)
				{
					_historyRead = new HistoryReadService(_history, _snapshotEpoch, new DateTimeOffset(_clockAnchorUtcTicks, TimeSpan.Zero), Plugin.HistoryLog);
					_history.Accepted += NotifyHistoryChanged;
				}
				_historyCache.Bind(flag ? sessionID : _net.GetServerPeer().m_uid);
				if (!flag)
				{
					Plugin.EncounterLog("CombatSettingsAuthority role=Client source=ListenHostSnapshot localCombatConfigIgnored=true");
				}
				LogFields logFields = new LogFields().Text("PeerSessionId", sessionID.ToString(CultureInfo.InvariantCulture));
				Guid epoch = _session.Sequence.Epoch;
				Plugin.TransportLog("DamageCommitSessionReady " + logFields.Text("SourceEpoch", epoch.ToString("N")).Flag("IsHost", flag));
			}
			RefreshPerformanceSettings();
			return true;
		}

		internal bool ObserveSummonProvenance(long observedPeer, long summonCreator, uint summonObject, long playerId)
		{
			if (!EnsureSession())
			{
				Plugin.MagicLog("SummonProvenanceRegistrationFailed reason=TransportUnavailable");
				return false;
			}
			if (observedPeer != _session.Peer)
			{
				Plugin.MagicLog("SummonProvenanceRegistrationFailed reason=NotExpectedPeer");
				return false;
			}
			if (summonCreator == 0L || summonObject == 0)
			{
				Plugin.MagicLog("SummonProvenanceRegistrationFailed reason=SummonZdoUnavailable");
				return false;
			}
			if (playerId == 0L)
			{
				Plugin.MagicLog("SummonProvenanceRegistrationFailed reason=PlayerIdZero");
				return false;
			}
			PublishAttribution(new AttributionMessage(_attributionSequence.Next(), AttributionMessageKind.Summon, summonCreator, summonObject, playerId, DotKind.Poison, 0f, 0f, AttackerClass.None, null, 0L));
			return true;
		}

		internal void ObserveDotPool(long observedPeer, long victimCreator, uint victimObject, DotKind kind, float before, float after, AttackerClass sourceClass, long? sourcePlayerId, long sourceCreator, uint sourceObject, bool updateAccepted)
		{
			if (EnsureSession() && observedPeer == _session.Peer && victimCreator != 0L && victimObject != 0 && updateAccepted)
			{
				PublishAttribution(new AttributionMessage(_attributionSequence.Next(), AttributionMessageKind.DotPool, victimCreator, victimObject, null, kind, before, after, sourceClass, sourcePlayerId, sourceCreator, sourceObject, updateAccepted));
			}
		}

		private void PublishAttribution(AttributionMessage message)
		{
			if (_session.IsHost)
			{
				ProcessAttribution(message, _session.Peer);
			}
			else if (!_attributionOutbox.Enqueue(message))
			{
				Plugin.Warn("SummonProvenanceRejected reason=PendingCapacity");
			}
			else if (message.Kind == AttributionMessageKind.Summon)
			{
				Plugin.MagicLog("SummonProvenanceMessageQueued summon=" + message.SubjectZdoId + " event=" + message.Id);
			}
		}

		internal DeathObservation ObservePlayerDeath(long playerId, string incarnation = null)
		{
			if (playerId == 0L)
			{
				return new DeathObservation("MissingPlayerID", EncounterState.NoEncounter, EncounterState.NoEncounter);
			}
			if (_disposed || !Object.op_Implicit((Object)(object)_net) || !_net.IsServer() || _net.IsDedicated())
			{
				return new DeathObservation("NotHost", EncounterState.NoEncounter, EncounterState.NoEncounter);
			}
			if (!EnsureSession() || _session == null || !_session.IsHost || _encounter == null)
			{
				return new DeathObservation("NoActiveSession", EncounterState.NoEncounter, EncounterState.NoEncounter);
			}
			EncounterState state = _encounter.State;
			double now = _now();
			PlayerDeathResult playerDeathResult = _encounter.OnPlayerDied(playerId, now, incarnation);
			_clusters.OnPlayerDied(playerId, now, incarnation);
			return new DeathObservation(playerDeathResult.ToString(), state, _encounter.State);
		}

		internal void Observe(long observedPeer, DamageFacts facts, float loss, AnalyticsDamageEvidence analytics = null)
		{
			if (!EnsureSession() || observedPeer != _session.Peer)
			{
				Plugin.Warn("DamageCommitObservationRejected reason=NoActiveSession");
				return;
			}
			CombatMeterPerformance performance = GetPerformance();
			if (performance != null)
			{
				performance.DamageObserved++;
			}
			if (!_session.IsHost && analytics != null)
			{
				ZNetPeer connection = _historyServerConnection;
				Guid epoch = _detailsExpectedEpoch;
				analytics.WhenDeath(delegate
				{
					QueueRemoteDeath(analytics.Invocation, connection, epoch);
				});
			}
			_session.Observe(facts, loss, DateTime.UtcNow.Ticks, analytics);
			Pump(publishSnapshot: false);
		}

		internal void ObserveDefense(long observedPeer, long playerId, DefenseObservation observation)
		{
			if (!EnsureSession() || observedPeer != _session.Peer || playerId == 0L)
			{
				return;
			}
			if (!_session.IsHost)
			{
				QueueRemoteDefense(playerId, observation);
				return;
			}
			_clusters.Update(_now());
			if (_clusters.TryGetClusterForPlayer(playerId, out var cluster))
			{
				ApplyDefense(cluster, playerId, observation, _now());
			}
			else
			{
				if (observation == null || !observation.Attacker.IsValid || !observation.Victim.IsValid || _pendingDefenseCount >= 256)
				{
					return;
				}
				foreach (var item in _pendingSummon)
				{
					DamageFacts facts = item.Commit.Facts;
					if (facts.VictimPlayerId == playerId && facts.VictimCreator == observation.Victim.Creator && facts.VictimObject == observation.Victim.Object && facts.AttackerCreator == observation.Attacker.Creator && facts.AttackerObject == observation.Attacker.Object)
					{
						if (!_pendingDefense.TryGetValue(item.Commit.Id, out List<(long, DefenseObservation)> value))
						{
							value = (_pendingDefense[item.Commit.Id] = new List<(long, DefenseObservation)>());
						}
						value.Add((playerId, observation));
						_pendingDefenseCount++;
						break;
					}
				}
			}
		}

		private void ApplyDefense(CombatCluster cluster, long playerId, DefenseObservation observation, double now)
		{
			if (cluster.Encounter.Analytics.AcceptDefense(playerId, observation))
			{
				ActorEvidence activityAttacker = observation.ActivityAttacker;
				if (observation.ActivityTime.HasValue && observation.Victim.IsValid && activityAttacker != null && activityAttacker.Id.Equals(observation.Attacker) && activityAttacker.Eligible(_knownSummon) && cluster.Encounter.Analytics.ContainsEnemy(activityAttacker.Id) && _clusters.TryGetMembership(CombatNode.Combatant(activityAttacker.Id.Creator, activityAttacker.Id.Object), out var cluster2) && cluster == cluster2 && cluster.Encounter.TryRefreshExistingCombatActivity(playerId, now, observation.ActivityTime.Value) && Plugin.LoggingEnabled)
				{
					Plugin.TransportTrace("CombatAnalyticsDefenseActivity " + new LogFields().Text("EncounterId", cluster.Id.ToString(CultureInfo.InvariantCulture)).Text("PlayerID", playerId.ToString(CultureInfo.InvariantCulture)).Text("Kind", (observation.Kind != DefenseKind.Block) ? ((observation.Kind == DefenseKind.Dodge) ? "Dodge" : "Barrier") : (observation.Parry ? "Parry" : "Block"))
						.Number("ActivityTime", observation.ActivityTime.Value)
						.Flag("ActivityRefreshed", value: true));
				}
			}
		}

		internal void Update()
		{
			if (_disposed)
			{
				return;
			}
			foreach (long item in new List<long>(_historyProbeRequests.Keys))
			{
				if (_now() >= _historyProbeRequests[item].Expires)
				{
					CompleteHistoryProbe(item, null, "Timeout");
				}
			}
			if (!Object.op_Implicit((Object)(object)_net) || _net != ZNet.instance || _rpc != ZRoutedRpc.instance)
			{
				Close("NetworkUnavailable");
				if (Object.op_Implicit((Object)(object)ZNet.instance))
				{
					Bind(ZNet.instance);
				}
				return;
			}
			if (_session != null && !_session.IsHost && _net.GetServerPeer() == null)
			{
				Close("Disconnected");
			}
			if (EnsureSession())
			{
				Pump(publishSnapshot: true);
			}
		}

		private void Pump(bool publishSnapshot)
		{
			double num = _now();
			SyncEncounterSettings();
			_encounter?.Update(num);
			_clusters?.Update(num);
			_session?.Pump(num);
			PumpAnalytics(num);
			if (_session != null && !_session.IsHost)
			{
				AttributionMessage expired;
				AttributionMessage attributionMessage = _attributionOutbox.Due(num, out expired);
				if (expired != null && expired.Kind == AttributionMessageKind.Summon)
				{
					Plugin.MagicLog("SummonProvenanceRetryExpired summon=" + expired.SubjectZdoId + " event=" + expired.Id);
				}
				if (attributionMessage != null)
				{
					SendAttribution(attributionMessage);
				}
			}
			if (_session == null || !_session.IsHost)
			{
				return;
			}
			for (int num2 = _pendingSummon.Count - 1; num2 >= 0; num2--)
			{
				if (num >= _pendingSummon[num2].Expires)
				{
					(DamageCommit, double, double) tuple = _pendingSummon[num2];
					_pendingSummon.RemoveAt(num2);
					ProcessAttributed(tuple.Item1, tuple.Item2);
				}
			}
			for (int num3 = _pendingDot.Count - 1; num3 >= 0; num3--)
			{
				if (num >= _pendingDot[num3].Expires)
				{
					(DamageCommit, double, double) tuple2 = _pendingDot[num3];
					_pendingDot.RemoveAt(num3);
					ProcessAttributed(tuple2.Item1, tuple2.Item2);
				}
			}
			if (publishSnapshot && num >= _nextSnapshotAt)
			{
				PublishSnapshot(num);
			}
			if (publishSnapshot)
			{
				ReportPerformance();
			}
			if (publishSnapshot && Plugin.LoggingEnabled)
			{
				EncounterAnalyticsSnapshot lastFinishedAnalytics = LastFinishedAnalytics;
				if (lastFinishedAnalytics != null && lastFinishedAnalytics != _lastAnalyticsSummary)
				{
					_lastAnalyticsSummary = lastFinishedAnalytics;
					Plugin.TransportLog("CombatAnalyticsCoreSummary " + AnalyticsDiagnostics.Format(lastFinishedAnalytics).Text("HostEpoch", _snapshotEpoch.ToString("N")));
				}
			}
		}

		private double ReadSoftTimeout()
		{
			return _softTimeout?.Invoke() ?? 20.0;
		}

		private double ReadRecoveryTimeout()
		{
			return _recoveryTimeout?.Invoke() ?? 180.0;
		}

		private double ReadDpsIdleTimeout()
		{
			return _dpsIdleTimeout?.Invoke() ?? 6.0;
		}

		private void SyncEncounterSettings()
		{
			if (_encounterSettings != null && _session != null && _session.IsHost && _encounterSettings.Update(ReadSoftTimeout(), ReadRecoveryTimeout(), ReadDpsIdleTimeout()))
			{
				LogEncounterSettings("CombatEncounterSettingsChanged");
			}
		}

		private void LogEncounterSettings(string marker)
		{
			Plugin.EncounterLog(marker + " combatTimeout=" + _encounterSettings.SoftTimeoutSeconds.ToString(CultureInfo.InvariantCulture) + " recoveryTimeout=" + _encounterSettings.RecoveryTimeoutSeconds.ToString(CultureInfo.InvariantCulture) + " dpsIdleTimeout=" + _encounterSettings.DpsIdleTimeoutSeconds.ToString(CultureInfo.InvariantCulture) + " authority=" + (ZNet.IsSinglePlayer ? "SinglePlayer" : "ListenHost"));
		}

		private void PublishSnapshot(double now)
		{
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Expected O, but got Unknown
			CombatMeterPerformance performance = GetPerformance();
			long num = ((performance != null) ? PerformanceTimestamp() : 0);
			try
			{
				_nextSnapshotAt = now + 0.5;
				if (_history != null)
				{
					try
					{
						_detailsRetention.RefreshCompletedRevisions(_snapshotEpoch);
					}
					catch (Exception ex)
					{
						Plugin.HistoryLog("CombatHistoryEncounterRejected reason=RevisionRefresh:" + ex.GetType().Name);
					}
				}
				_identities.Sync(_net.GetPeers(), _session.Peer, HostPlayerIdentity.ResolveLocal(_session.Peer));
				if (IsCurrentPerformance(performance))
				{
					performance.ReadyPeers = 0;
				}
				long sequence = checked(++_snapshotSequence);
				long? num2 = ResolveSnapshotPlayer(_session.Peer, local: true);
				CombatSnapshot snapshot = BuildRecipientSnapshot(sequence, num2, now);
				SnapshotApplyResult snapshotApplyResult = _snapshotStore.Apply(snapshot, _session.Peer, _session.Peer);
				if (snapshotApplyResult != SnapshotApplyResult.Accepted)
				{
					throw new InvalidOperationException("LocalSnapshot" + snapshotApplyResult);
				}
				LogSnapshotChange("CombatSnapshotAppliedLocal", snapshot);
				PublishDetails(sequence, _session.Peer, num2, now, local: true);
				if (ZNet.IsSinglePlayer)
				{
					return;
				}
				HashSet<long> hashSet = new HashSet<long>();
				foreach (ZNetPeer peer in _net.GetPeers())
				{
					if (peer != null && peer.IsReady() && CompatibilityRuntime.IsReady(peer) && peer.m_uid != 0L && peer.m_uid != _session.Peer && hashSet.Add(peer.m_uid))
					{
						if (IsCurrentPerformance(performance))
						{
							performance.ReadyPeers++;
						}
						long? num3 = ResolveSnapshotPlayer(peer.m_uid, local: false);
						CombatSnapshot snapshot2 = BuildRecipientSnapshot(sequence, num3, now);
						long num4 = (IsCurrentPerformance(performance) ? PerformanceTimestamp() : 0);
						ZPackage val = new ZPackage(CombatSnapshotCodec.Encode(snapshot2));
						if (IsCurrentPerformance(performance))
						{
							performance.Serialization.Record(PerformanceTimestamp() - num4);
						}
						_rpc.InvokeRoutedRPC(peer.m_uid, "CombatMeter.CombatSnapshot.v2", new object[1] { val });
						if (IsCurrentPerformance(performance))
						{
							performance.RecordSend(val.Size());
						}
						LogSnapshotChange("CombatSnapshotPublished", snapshot2);
						PublishDetails(sequence, peer.m_uid, num3, now, local: false);
					}
				}
			}
			finally
			{
				if (IsCurrentPerformance(performance))
				{
					performance.SnapshotCycle.Record(PerformanceTimestamp() - num);
				}
			}
		}

		private DateTimeOffset HistoryTime(double monotonic)
		{
			return new DateTimeOffset(checked(_clockAnchorUtcTicks + (long)Math.Round((monotonic - _clockAnchorMonotonic) * 10000000.0)), TimeSpan.Zero);
		}

		private void CaptureHistory(EncounterManager source, CombatDetailsEncounter previous, CombatDetailsEncounter completed)
		{
			if (_history == null)
			{
				return;
			}
			CommitSession session = _session;
			if (session == null || !session.IsHost)
			{
				return;
			}
			try
			{
				if (previous != null)
				{
					_history.ApplyTerminalRevision(previous, completed);
					return;
				}
				DateTimeOffset finalized = HistoryTime(Math.Max(_now(), source.EndTime));
				_history.AddCompletedEncounter(new HistoryEncounter(completed, HistoryTime(source.StartTime), HistoryTime(source.EndTime), finalized, HistoryRuntime.DayKey(finalized)));
			}
			catch (Exception ex)
			{
				Plugin.HistoryLog("CombatHistoryEncounterRejected reason=" + ex.GetType().Name);
			}
		}

		private void RetainDetails(CombatCluster cluster)
		{
			try
			{
				_detailsRetention.Completed(_snapshotEpoch, cluster);
			}
			catch (Exception ex) when (ex is InvalidDataException || ex is InvalidOperationException || ex is OverflowException || ex is ArgumentException)
			{
				_detailsRetention.Unavailable(cluster);
				Plugin.SnapshotLog("CombatDetailsSnapshotRejected reason=CompletedBuild " + ex.GetType().Name);
			}
		}

		private void PublishDetails(long sequence, long target, long? player, double now, bool local)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			try
			{
				CombatDetailsSnapshot combatDetailsSnapshot = _detailsRetention.ForPlayer(_session.Peer, _snapshotEpoch, sequence, _clusters, player, now);
				byte[] array = CombatDetailsCodec.Encode(combatDetailsSnapshot);
				string hash = DetailsHash(array);
				DetailsLog("CombatDetailsSnapshotBuilt", combatDetailsSnapshot, target, array.Length, hash);
				if (local)
				{
					CombatDetailsEncounter lastCompletedEncounter = _detailsStore.LastCompletedEncounter;
					SnapshotApplyResult snapshotApplyResult = _detailsStore.Apply(combatDetailsSnapshot, _session.Peer, _session.Peer);
					if (snapshotApplyResult != SnapshotApplyResult.Accepted)
					{
						throw new InvalidOperationException("DetailsLocal" + snapshotApplyResult);
					}
					DetailsLog("CombatDetailsSnapshotAppliedLocal", combatDetailsSnapshot, target, array.Length, hash);
					if (lastCompletedEncounter != _detailsStore.LastCompletedEncounter && _detailsStore.LastCompletedEncounter != null)
					{
						DetailsLog("CombatDetailsSnapshotStoredCompleted", combatDetailsSnapshot, target, array.Length, hash);
					}
				}
				else
				{
					_rpc.InvokeRoutedRPC(target, "CombatMeter.CombatDetails.v1", new object[1] { (object)new ZPackage(array) });
					DetailsLog("CombatDetailsSnapshotSent", combatDetailsSnapshot, target, array.Length, hash);
				}
				if (!player.HasValue)
				{
					DetailsLog("CombatDetailsSnapshotCleared", combatDetailsSnapshot, target, array.Length);
				}
			}
			catch (Exception ex) when (ex is InvalidDataException || ex is InvalidOperationException || ex is OverflowException || ex is ArgumentException)
			{
				Plugin.SnapshotLog("CombatDetailsSnapshotRejected reason=BuildBounds target=" + target + " type=" + ex.GetType().Name);
				CombatDetailsSnapshot snapshot = new CombatDetailsSnapshot(_session.Peer, _snapshotEpoch, sequence, 0L, null, null);
				if (local)
				{
					_detailsStore.Apply(snapshot, _session.Peer, _session.Peer);
					return;
				}
				_rpc.InvokeRoutedRPC(target, "CombatMeter.CombatDetails.v1", new object[1] { (object)new ZPackage(CombatDetailsCodec.Encode(snapshot)) });
			}
		}

		internal long RequestHistory(HistoryQuery query, int offset = 0, int limit = 20, string key = "", Action<HistoryReadResult, string> completed = null)
		{
			return RequestHistoryCore(query, offset, limit, key, completed, statistics: false);
		}

		internal long RequestStatistics(HistoryQuery query, int offset = 0, int limit = 20, string key = "", Action<HistoryReadResult, string> completed = null)
		{
			return RequestHistoryCore(query, offset, limit, key, completed, statistics: true);
		}

		internal long RequestStatisticsSnapshot(Action<StatisticsSnapshot, string> completed)
		{
			return RequestHistoryCore(HistoryQuery.Session, 0, 20, "", delegate(HistoryReadResult a, string failure)
			{
				completed((a == null) ? null : new StatisticsSnapshot(a), failure);
			}, statistics: false, snapshot: true);
		}

		private long RequestHistoryCore(HistoryQuery query, int offset, int limit, string key, Action<HistoryReadResult, string> completed, bool statistics, bool snapshot = false)
		{
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Expected O, but got Unknown
			if (!HistoryTransportReady(_rpc))
			{
				throw new InvalidOperationException("HistoryTransportUnavailable");
			}
			if (_session.IsHost && _historyRead == null)
			{
				throw new InvalidOperationException("HistoryUnavailable");
			}
			if (!_session.IsHost && (_net.GetServerPeer() == null || !_net.GetServerPeer().IsReady()))
			{
				throw new InvalidOperationException("HostNotReady");
			}
			long num = (_session.IsHost ? _session.Peer : _net.GetServerPeer().m_uid);
			_historyCache.Bind(num);
			if (!_session.IsHost && _snapshotStore.TryGetLatest(out var snapshot2))
			{
				_historyCache.ObserveEpoch(snapshot2.SnapshotEpoch);
			}
			_historyCache.Bind(num);
			if (completed != null && _historyProbeRequests.Count >= 8)
			{
				throw new InvalidOperationException("ProbeCapacity");
			}
			HistoryReadRequest historyReadRequest = _historyCache.Begin(query, _now(), offset, limit, key, (byte)(snapshot ? 2 : (statistics ? 1 : 0)));
			if (completed != null)
			{
				_historyProbeRequests.Add(historyReadRequest.RequestId, (_now() + 30.0, completed));
			}
			if (snapshot)
			{
				_statisticsPendingSnapshots.Add(historyReadRequest.RequestId);
			}
			if (snapshot)
			{
				Plugin.HistoryLog("CombatStatisticsSnapshotRequested request=" + historyReadRequest.RequestId + " connection=" + historyReadRequest.ConnectionId.ToString("N"));
			}
			try
			{
				if (_session.IsHost)
				{
					byte[] bytes = (snapshot ? StatisticsSnapshotCodec.Encode(_historyRead.ReadStatisticsSnapshot(historyReadRequest)) : (statistics ? StatisticsReadCodec.Encode(_historyRead.Read(historyReadRequest)) : HistoryReadCodec.EncodeBoundedResponse(_historyRead.Read(historyReadRequest))));
					if (_historyCache.Accept(num, bytes, _now(), out var accepted, statistics, snapshot))
					{
						if (snapshot)
						{
							LogStatisticsSnapshot(accepted, bytes);
						}
						CompleteHistoryProbe(historyReadRequest.RequestId, accepted, null);
					}
					else
					{
						CompleteHistoryProbe(historyReadRequest.RequestId, null, "ResponseRejected");
					}
				}
				else
				{
					ZNetPeer serverPeer = _net.GetServerPeer();
					if (serverPeer == null || !serverPeer.IsReady() || num == 0L)
					{
						throw new InvalidOperationException("HostNotReady");
					}
					_rpc.InvokeRoutedRPC(num, snapshot ? "CombatMeter.CombatStatistics.v1.Request" : (statistics ? "CombatMeter.CombatHistory.Statistics.v1.Request" : "CombatMeter.CombatHistory.v1.Request"), new object[1] { (object)new ZPackage(HistoryReadCodec.EncodeRequest(historyReadRequest)) });
				}
				return historyReadRequest.RequestId;
			}
			catch
			{
				CancelHistoryRequest(historyReadRequest.RequestId);
				throw;
			}
		}

		private bool CompatibleSender(long sender)
		{
			if (!Object.op_Implicit((Object)(object)_net) || sender == 0L)
			{
				return false;
			}
			if (!_net.IsServer())
			{
				ZNetPeer serverPeer = _net.GetServerPeer();
				if (serverPeer != null && serverPeer.m_uid == sender)
				{
					return CompatibilityRuntime.IsReady(serverPeer);
				}
				return false;
			}
			foreach (ZNetPeer peer in _net.GetPeers())
			{
				if (peer != null && peer.m_uid == sender && CompatibilityRuntime.IsReady(peer))
				{
					return true;
				}
			}
			return false;
		}

		private bool HistoryTransportReady(ZRoutedRpc rpc)
		{
			if (!_disposed && _session != null && Object.op_Implicit((Object)(object)_net) && !IsBlocked(_net) && _net == ZNet.instance && rpc == _rpc && rpc == ZRoutedRpc.instance && ZDOMan.instance != null && ZDOMan.GetSessionID() == _session.Peer && _net.IsServer() == _session.IsHost)
			{
				if (!_session.IsHost)
				{
					if (CompatibilityRuntime.IsReady(_net.GetServerPeer()))
					{
						return _historyServerConnection == _net.GetServerPeer();
					}
					return false;
				}
				return true;
			}
			return false;
		}

		private void ReceiveHistoryRequest(ZRoutedRpc rpc, long sender, ZPackage package, bool statistics = false, bool snapshot = false)
		{
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0331: Expected O, but got Unknown
			if (!HistoryTransportReady(rpc) || !_session.IsHost || _historyRead == null)
			{
				return;
			}
			try
			{
				ZNetPeer val = null;
				foreach (ZNetPeer peer in _net.GetPeers())
				{
					if (peer != null && peer.m_uid == sender && peer.IsReady() && CompatibilityRuntime.IsReady(peer))
					{
						val = peer;
						break;
					}
				}
				if (sender == 0L || sender == _session.Peer || val == null || !_identities.ResolveConnection(sender, val).HasValue)
				{
					Plugin.HistoryLog("CombatHistoryReadResponseRejected reason=UnauthorizedPeer");
					return;
				}
				if (package == null || package.Size() > 256)
				{
					throw new InvalidDataException("RequestBytes");
				}
				HistoryReadRequest historyReadRequest = HistoryReadCodec.DecodeRequest(package.GetArray());
				double num = _now();
				if (snapshot && historyReadRequest.Query != HistoryQuery.Session)
				{
					throw new InvalidDataException("StatisticsSnapshotRequestShape");
				}
				foreach (long item in new List<long>(_historyReaders.Keys))
				{
					if (!_net.GetPeers().Contains(_historyReaders[item].Peer))
					{
						_historyReaders.Remove(item);
					}
				}
				_historyReaders.TryGetValue(sender, out (ZNetPeer, Guid, long, double, int) value);
				if (value.Item1 != val)
				{
					value = (val, historyReadRequest.ConnectionId, 0L, num, 0);
				}
				if (num - value.Item4 >= 1.0)
				{
					value = (value.Item1, value.Item2, value.Item3, num, 0);
				}
				if (value.Item5 >= 8 || (_historyReaders.Count >= 256 && !_historyReaders.ContainsKey(sender)))
				{
					Plugin.HistoryLog("CombatHistoryReadResponseRejected reason=RequestCapacity");
					return;
				}
				if (value.Item2 == historyReadRequest.ConnectionId && historyReadRequest.RequestId <= value.Item3)
				{
					Plugin.HistoryLog("CombatHistoryReadResponseRejected reason=StaleRequest");
					return;
				}
				_historyReaders[sender] = (val, historyReadRequest.ConnectionId, historyReadRequest.RequestId, value.Item4, value.Item5 + 1);
				Plugin.HistoryLog("CombatHistoryReadRequestReceived query=" + historyReadRequest.Query.ToString() + " request=" + historyReadRequest.RequestId + " peer=" + sender);
				HistoryReadResult historyReadResult = (snapshot ? _historyRead.ReadStatisticsSnapshot(historyReadRequest).Data : _historyRead.Read(historyReadRequest));
				byte[] array = (snapshot ? StatisticsSnapshotCodec.Encode(new StatisticsSnapshot(historyReadResult)) : (statistics ? StatisticsReadCodec.Encode(historyReadResult) : HistoryReadCodec.EncodeBoundedResponse(historyReadResult)));
				rpc.InvokeRoutedRPC(sender, snapshot ? "CombatMeter.CombatStatistics.v1.Response" : (statistics ? "CombatMeter.CombatHistory.Statistics.v1.Response" : "CombatMeter.CombatHistory.v1.Response"), new object[1] { (object)new ZPackage(array) });
				if (Plugin.HistoryDiagnosticsEnabled)
				{
					Plugin.HistoryLog("CombatHistoryReadResponseSent query=" + historyReadRequest.Query.ToString() + " request=" + historyReadRequest.RequestId + " peer=" + sender + " bytes=" + array.Length + " hash=" + DetailsHash(array));
				}
			}
			catch (Exception ex)
			{
				Plugin.HistoryLog("CombatHistoryReadResponseRejected reason=MalformedOrHandlerFailure type=" + ex.GetType().Name);
			}
		}

		private void ReceiveHistoryResponse(ZRoutedRpc rpc, long sender, ZPackage package, bool statistics = false, bool snapshot = false)
		{
			if (!HistoryTransportReady(rpc) || _session.IsHost)
			{
				return;
			}
			ZNetPeer serverPeer = _net.GetServerPeer();
			if (serverPeer == null || serverPeer.m_uid == 0L || sender != serverPeer.m_uid)
			{
				return;
			}
			try
			{
				_historyCache.Bind(serverPeer.m_uid);
				if (_snapshotStore.TryGetLatest(out var snapshot2))
				{
					_historyCache.ObserveEpoch(snapshot2.SnapshotEpoch);
				}
				_historyCache.Bind(serverPeer.m_uid);
				if (package == null || package.Size() > 1048576)
				{
					throw new InvalidDataException("ResponseBytes");
				}
				byte[] array = package.GetArray();
				if (_historyCache.Accept(sender, array, _now(), out var accepted, statistics, snapshot))
				{
					if (snapshot)
					{
						LogStatisticsSnapshot(accepted, array);
					}
					CompleteHistoryProbe(accepted.Request.RequestId, accepted, null);
					if (snapshot && _statisticsDeferred != null)
					{
						StatisticsRevision statisticsDeferred = _statisticsDeferred;
						_statisticsDeferred = null;
						AcceptStatisticsRevision(statisticsDeferred);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.HistoryLog("CombatHistoryReadResponseRejected reason=MalformedOrHandlerFailure type=" + ex.GetType().Name);
			}
		}

		private void CompleteHistoryProbe(long request, HistoryReadResult result, string reason)
		{
			if (!_historyProbeRequests.TryGetValue(request, out (double, Action<HistoryReadResult, string>) value))
			{
				return;
			}
			_historyProbeRequests.Remove(request);
			_statisticsPendingSnapshots.Remove(request);
			try
			{
				value.Item2(result, reason);
			}
			catch
			{
			}
		}

		internal void CancelHistoryRequest(long id)
		{
			_historyProbeRequests.Remove(id);
			_statisticsPendingSnapshots.Remove(id);
			_historyCache.Cancel(id);
		}

		private void LogStatisticsSnapshot(HistoryReadResult a, byte[] bytes)
		{
			if (a.Status == HistoryReadStatus.Ok)
			{
				_statisticsApplied = new StatisticsRevision(a.WorldIdentity, a.HostEpoch, a.SessionId, a.Request.ConnectionId, a.HistoryRevision, a.SessionRevision);
			}
			if (Plugin.HistoryDiagnosticsEnabled)
			{
				Plugin.HistoryLog("CombatStatisticsSnapshotAccepted request=" + a.Request.RequestId + " history=" + a.HistoryRevision + " session=" + a.SessionRevision + " bytes=" + bytes.Length + " status=" + a.Status.ToString() + " hash=" + StatisticsSnapshotCodec.SemanticHash(new StatisticsSnapshot(a)));
			}
		}

		private void NotifyHistoryChanged(HistoryEncounter record, CombatDetailsEncounter before, CombatDetailsEncounter after)
		{
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			if (_historyRead == null)
			{
				return;
			}
			CommitSession session = _session;
			if (session == null || !session.IsHost)
			{
				return;
			}
			long revision = _history.Revision;
			long revision2 = _historyRead.CurrentSession.Revision;
			if (revision == _statisticsPublishedHistory && revision2 == _statisticsPublishedSession)
			{
				return;
			}
			_statisticsPublishedHistory = revision;
			_statisticsPublishedSession = revision2;
			foreach (ZNetPeer peer in _net.GetPeers())
			{
				if (peer != null && peer.IsReady() && CompatibilityRuntime.IsReady(peer) && peer.m_uid != 0L && _identities.ResolveConnection(peer.m_uid, peer).HasValue && _historyReaders.TryGetValue(peer.m_uid, out (ZNetPeer, Guid, long, double, int) value) && value.Item1 == peer)
				{
					try
					{
						byte[] array = StatisticsSnapshotCodec.Revision(new StatisticsRevision(_history.WorldIdentity, _snapshotEpoch, _historyRead.CurrentSession.SessionId, value.Item2, revision, revision2));
						_rpc.InvokeRoutedRPC(peer.m_uid, "CombatMeter.CombatStatistics.v1.Changed", new object[1] { (object)new ZPackage(array) });
						Plugin.HistoryLog("CombatStatisticsRevisionNotified history=" + revision + " session=" + revision2 + " peer=" + peer.m_uid + " bytes=" + array.Length);
					}
					catch
					{
					}
				}
			}
			RaiseHistoryChanged(new StatisticsRevision(_history.WorldIdentity, _snapshotEpoch, _historyRead.CurrentSession.SessionId, _historyCache.ConnectionId, revision, revision2));
		}

		private void RaiseHistoryChanged(StatisticsRevision revision)
		{
			if (this.HistoryChanged == null)
			{
				return;
			}
			Delegate[] invocationList = this.HistoryChanged.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<StatisticsRevision> action = (Action<StatisticsRevision>)invocationList[i];
				try
				{
					action(revision);
				}
				catch
				{
				}
			}
		}

		private void ReceiveHistoryRevision(ZRoutedRpc rpc, long sender, ZPackage package)
		{
			if (!HistoryTransportReady(rpc) || _session.IsHost || sender != _net.GetServerPeer()?.m_uid)
			{
				return;
			}
			try
			{
				if (package == null || package.Size() > 512)
				{
					return;
				}
				StatisticsRevision statisticsRevision = StatisticsSnapshotCodec.Revision(package.GetArray());
				if (statisticsRevision.Connection != _historyCache.ConnectionId || (_historyCache.WorldIdentity != null && (statisticsRevision.World != _historyCache.WorldIdentity || statisticsRevision.Epoch != _historyCache.HostEpoch || statisticsRevision.Session != _historyCache.SessionId)))
				{
					return;
				}
				StatisticsRevision statisticsRevision2 = ((_statisticsApplied?.Connection == statisticsRevision.Connection) ? _statisticsApplied : null);
				if (statisticsRevision2 != null && (statisticsRevision.History < statisticsRevision2.History || statisticsRevision.Summary < statisticsRevision2.Summary || (statisticsRevision.History == statisticsRevision2.History && statisticsRevision.Summary == statisticsRevision2.Summary)))
				{
					return;
				}
				if (_statisticsPendingSnapshots.Count > 0 || (_historyCache.WorldIdentity == null && _historyProbeRequests.Count > 0))
				{
					if (_statisticsDeferred == null || (statisticsRevision.History >= _statisticsDeferred.History && statisticsRevision.Summary >= _statisticsDeferred.Summary && (statisticsRevision.History > _statisticsDeferred.History || statisticsRevision.Summary > _statisticsDeferred.Summary)))
					{
						_statisticsDeferred = statisticsRevision;
						Plugin.HistoryLog("CombatStatisticsRefreshCoalesced history=" + statisticsRevision.History + " session=" + statisticsRevision.Summary);
					}
				}
				else
				{
					AcceptStatisticsRevision(statisticsRevision);
				}
			}
			catch (Exception ex)
			{
				Plugin.HistoryLog("CombatHistoryReadResponseRejected reason=RevisionMalformed type=" + ex.GetType().Name);
			}
		}

		private void AcceptStatisticsRevision(StatisticsRevision revision)
		{
			if (!(revision.Connection != _historyCache.ConnectionId) && !(revision.World != _historyCache.WorldIdentity) && !(revision.Epoch != _historyCache.HostEpoch) && !(revision.Session != _historyCache.SessionId))
			{
				StatisticsRevision obj = ((_statisticsApplied?.Connection == revision.Connection) ? _statisticsApplied : null);
				long num = obj?.History ?? 0;
				long num2 = obj?.Summary ?? 0;
				if (_statisticsObserved?.Connection == revision.Connection)
				{
					num = Math.Max(num, _statisticsObserved.History);
					num2 = Math.Max(num2, _statisticsObserved.Summary);
				}
				if (revision.History >= num && revision.Summary >= num2 && (revision.History != num || revision.Summary != num2))
				{
					_statisticsObserved = revision;
					RaiseHistoryChanged(revision);
				}
			}
		}

		private void ReceiveDetails(ZRoutedRpc rpc, long sender, ZPackage package)
		{
			if (!CompatibleSender(sender) || _disposed || rpc != _rpc || !EnsureSession() || _session.IsHost)
			{
				return;
			}
			ZNetPeer serverPeer = _net.GetServerPeer();
			if (serverPeer == null || sender != serverPeer.m_uid)
			{
				Plugin.SnapshotLog("CombatDetailsSnapshotRejected reason=UnexpectedSender");
				return;
			}
			try
			{
				if (package == null || package.Size() > 524288)
				{
					throw new InvalidDataException("PacketSize");
				}
				CombatDetailsSnapshot combatDetailsSnapshot = CombatDetailsCodec.Decode(package.GetArray());
				if (_snapshotStore.TryGetLatest(out var snapshot))
				{
					_detailsExpectedEpoch = snapshot.SnapshotEpoch;
				}
				if (_detailsExpectedEpoch != Guid.Empty && combatDetailsSnapshot.HostEpoch != _detailsExpectedEpoch)
				{
					_detailsStore.Invalidate();
					Plugin.SnapshotLog("CombatDetailsSnapshotCleared reason=HostEpochMismatch");
					Plugin.SnapshotLog("CombatDetailsSnapshotRejected reason=HostEpochMismatch");
					return;
				}
				string hash = DetailsHash(package.GetArray());
				DetailsLog("CombatDetailsSnapshotReceived", combatDetailsSnapshot, _session.Peer, package.Size(), hash);
				CombatDetailsEncounter lastCompletedEncounter = _detailsStore.LastCompletedEncounter;
				SnapshotApplyResult snapshotApplyResult = _detailsStore.Apply(combatDetailsSnapshot, sender, serverPeer.m_uid);
				switch (snapshotApplyResult)
				{
				case SnapshotApplyResult.Accepted:
					_detailsExpectedEpoch = combatDetailsSnapshot.HostEpoch;
					DetailsLog("CombatDetailsSnapshotAppliedRemote", combatDetailsSnapshot, _session.Peer, package.Size(), hash);
					if (lastCompletedEncounter != _detailsStore.LastCompletedEncounter && _detailsStore.LastCompletedEncounter != null)
					{
						DetailsLog("CombatDetailsSnapshotStoredCompleted", combatDetailsSnapshot, _session.Peer, package.Size(), hash);
					}
					if (combatDetailsSnapshot.TargetPlayerId == 0L)
					{
						DetailsLog("CombatDetailsSnapshotCleared", combatDetailsSnapshot, _session.Peer, package.Size());
						_historyCache.Clear("IdentityRevoked");
					}
					return;
				case SnapshotApplyResult.SessionMismatch:
					_detailsStore.Invalidate();
					break;
				}
				Plugin.SnapshotLog("CombatDetailsSnapshotRejected reason=" + snapshotApplyResult);
			}
			catch (Exception ex) when (ex is InvalidDataException || ex is EndOfStreamException || ex is ArgumentException || ex is OverflowException)
			{
				if (ex is InvalidDataException && ex.Message == "DetailsSchemaVersion"