Decompiled source of ClientPredictedProjectiles v3.0.3

ClientPredictedProjectiles.dll

Decompiled 19 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RoR2;
using RoR2.Audio;
using RoR2.Networking;
using RoR2.Orbs;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ClientPredictedProjectiles")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("3.0.3.0")]
[assembly: AssemblyInformationalVersion("3.0.3")]
[assembly: AssemblyProduct("ClientPredictedProjectiles")]
[assembly: AssemblyTitle("ClientPredictedProjectiles")]
[assembly: AssemblyVersion("3.0.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ClientPredictedProjectiles
{
	internal static class Access
	{
		public static readonly FieldRef<ProjectileImpactExplosion, float> ImpactExplosionStopwatch = AccessTools.FieldRefAccess<ProjectileImpactExplosion, float>("stopwatch");

		public static readonly FieldRef<ProjectileSimple, float> SimpleStopwatch = AccessTools.FieldRefAccess<ProjectileSimple, float>("stopwatch");

		public static readonly FieldRef<ProjectileSimple, float> SimpleOscillationStopwatch = AccessTools.FieldRefAccess<ProjectileSimple, float>("oscillationStopwatch");

		public static readonly Action<ProjectileGhostController, Transform> GhostCopyTransform = AccessTools.MethodDelegate<Action<ProjectileGhostController, Transform>>(AccessTools.Method(typeof(ProjectileGhostController), "CopyTransform", new Type[1] { typeof(Transform) }, (Type[])null), (object)null, true);
	}
	internal static class PredictionManagerAccess
	{
		public static readonly Type PredictionManagerType = AccessTools.Inner(typeof(ProjectileManager), "PredictionManager");

		private static readonly FieldInfo predictionManagerField = AccessTools.Field(typeof(ProjectileManager), "predictionManager");

		private static readonly FieldInfo predictionsField = AccessTools.Field(PredictionManagerType, "predictions");

		private static readonly MethodInfo releaseMethod = AccessTools.Method(PredictionManagerType, "ReleasePredictionId", (Type[])null, (Type[])null);

		public static Dictionary<ushort, ProjectileController> GetPredictions(object predictionManager)
		{
			if (predictionManager != null)
			{
				return predictionsField.GetValue(predictionManager) as Dictionary<ushort, ProjectileController>;
			}
			return null;
		}

		public static void Release(ushort predictionId)
		{
			ProjectileManager instance = ProjectileManager.instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				object value = predictionManagerField.GetValue(instance);
				Dictionary<ushort, ProjectileController> predictions = GetPredictions(value);
				if (predictions != null && predictions.ContainsKey(predictionId))
				{
					releaseMethod.Invoke(value, new object[1] { predictionId });
				}
			}
		}
	}
	[DefaultExecutionOrder(-10000)]
	internal sealed class ClientOwnedProjectile : MonoBehaviour
	{
		private struct RecentAttack
		{
			public AttackKind kind;

			public int key;

			public float age;

			public object attack;

			public bool filled;

			public Vector3 center;

			public float range;
		}

		private const int MaxReportsPerProjectile = 512;

		private const float RecentAttackMemory = 1f;

		private static readonly Dictionary<long, ClientOwnedProjectile> byPrediction = new Dictionary<long, ClientOwnedProjectile>();

		private static readonly Dictionary<int, ClientOwnedProjectile> byObject = new Dictionary<int, ClientOwnedProjectile>();

		private ushort predictionId;

		private long key;

		private readonly List<int> objectIds = new List<int>();

		private ProjectileProfile profile;

		private ProjectileController controller;

		private Rigidbody rb;

		private float spawnTime;

		private int reportCount;

		private bool inSync = true;

		private bool ownerConfirmed;

		private uint shot;

		private bool decisionMade;

		private static readonly Dictionary<long, ClientOwnedProjectile> finished = new Dictionary<long, ClientOwnedProjectile>();

		private float destroyedAt;

		private readonly List<ImpactReportMessage> pendingImpacts = new List<ImpactReportMessage>();

		private readonly List<AttackReportMessage> pendingAttacks = new List<AttackReportMessage>();

		private readonly List<RecentAttack> recentAttacks = new List<RecentAttack>();

		public NetworkConnection Owner { get; private set; }

		public float Age => Time.fixedTime - spawnTime;

		private static float Tick => Time.fixedDeltaTime;

		private bool OwnerSimulates
		{
			get
			{
				if (Owner != null && Owner.isConnected)
				{
					return NetProtocol.ClientSimulates(Owner, controller.catalogIndex);
				}
				return false;
			}
		}

		public bool ClientDecides
		{
			get
			{
				if (inSync && ownerConfirmed && PluginConfig.ClientImpacts.Value)
				{
					return OwnerSimulates;
				}
				return false;
			}
		}

		public static ClientOwnedProjectile Attach(ProjectileController controller, ProjectileProfile profile, NetworkConnection owner, ushort predictionId)
		{
			ClientOwnedProjectile clientOwnedProjectile = ((Component)controller).gameObject.AddComponent<ClientOwnedProjectile>();
			clientOwnedProjectile.controller = controller;
			clientOwnedProjectile.profile = profile;
			clientOwnedProjectile.Owner = owner;
			clientOwnedProjectile.predictionId = predictionId;
			clientOwnedProjectile.rb = ((Component)controller).GetComponent<Rigidbody>();
			clientOwnedProjectile.spawnTime = Time.fixedTime;
			clientOwnedProjectile.key = MakeKey(owner, predictionId);
			byPrediction[clientOwnedProjectile.key] = clientOwnedProjectile;
			Transform[] componentsInChildren = ((Component)controller).GetComponentsInChildren<Transform>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				int instanceID = ((Object)((Component)componentsInChildren[i]).gameObject).GetInstanceID();
				byObject[instanceID] = clientOwnedProjectile;
				clientOwnedProjectile.objectIds.Add(instanceID);
			}
			finished.Remove(clientOwnedProjectile.key);
			NetProtocol.DeliverEarlyReports(clientOwnedProjectile, owner, predictionId);
			clientOwnedProjectile.decisionMade = true;
			if (!clientOwnedProjectile.ownerConfirmed && PluginConfig.VerboseLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"[{profile.name}#{predictionId}] owner did not confirm simulating this shot; host decides its hits");
			}
			return clientOwnedProjectile;
		}

		public static bool TryGet(NetworkConnection conn, ushort predictionId, out ClientOwnedProjectile tracker)
		{
			if (byPrediction.TryGetValue(MakeKey(conn, predictionId), out tracker))
			{
				return Object.op_Implicit((Object)(object)tracker);
			}
			return false;
		}

		public static bool TryFindForObject(GameObject obj, out ClientOwnedProjectile tracker)
		{
			tracker = null;
			if (Object.op_Implicit((Object)(object)obj) && byObject.TryGetValue(((Object)obj).GetInstanceID(), out tracker))
			{
				return Object.op_Implicit((Object)(object)tracker);
			}
			return false;
		}

		public static long MakeKey(NetworkConnection conn, ushort predictionId)
		{
			return ((long)conn.connectionId << 16) | predictionId;
		}

		private void OnDestroy()
		{
			if (byPrediction.TryGetValue(key, out var value) && (Object)(object)value == (Object)(object)this)
			{
				byPrediction.Remove(key);
			}
			foreach (int objectId in objectIds)
			{
				if (byObject.TryGetValue(objectId, out var value2) && (Object)(object)value2 == (Object)(object)this)
				{
					byObject.Remove(objectId);
				}
			}
			if (ClientDecides && recentAttacks.Exists((RecentAttack a) => !a.filled))
			{
				destroyedAt = Time.fixedTime;
				finished[key] = this;
			}
			if (PluginConfig.VerboseLogging.Value && inSync && (pendingImpacts.Count > 0 || pendingAttacks.Count > 0))
			{
				Plugin.Log.LogDebug((object)$"[{profile.name}#{predictionId}] ended with {pendingImpacts.Count} impact / {pendingAttacks.Count} hit reports unused");
			}
		}

		public void ConfirmOwnerSimulating(uint ownerShot)
		{
			if (!decisionMade && ownerShot != 0)
			{
				ownerConfirmed = true;
				shot = ownerShot;
			}
		}

		public static bool TryApplyToFinished(NetworkConnection conn, AttackReportMessage report)
		{
			long num = MakeKey(conn, report.predictionId);
			if (!finished.TryGetValue(num, out var value) || value.shot != report.shot)
			{
				return false;
			}
			if (Time.fixedTime - value.destroyedAt > 1f)
			{
				finished.Remove(num);
				return false;
			}
			value.ApplyToRecentAttack(report, value.Age);
			return true;
		}

		public static void PruneFinished()
		{
			if (finished.Count == 0)
			{
				return;
			}
			List<long> list = new List<long>();
			foreach (KeyValuePair<long, ClientOwnedProjectile> item in finished)
			{
				if (Time.fixedTime - item.Value.destroyedAt > 1f)
				{
					list.Add(item.Key);
				}
			}
			foreach (long item2 in list)
			{
				finished.Remove(item2);
			}
		}

		public bool OnHostImpact(in ProjectileImpactInfo info)
		{
			return !ClientDecides;
		}

		private void Desync(string reason)
		{
			if (inSync)
			{
				inSync = false;
				pendingImpacts.Clear();
				pendingAttacks.Clear();
				recentAttacks.Clear();
				if (PluginConfig.VerboseLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"[{profile.name}#{predictionId}] host takes over: {reason}");
				}
				NetProtocol.SendImpactRejected(Owner, predictionId);
			}
		}

		private void FixedUpdate()
		{
			if (!ClientDecides)
			{
				if (inSync && !OwnerSimulates)
				{
					Desync("owner no longer simulates it");
				}
				return;
			}
			float age = Age;
			while (pendingImpacts.Count > 0 && pendingImpacts[0].age <= age + Tick * 0.5f)
			{
				ImpactReportMessage report = pendingImpacts[0];
				pendingImpacts.RemoveAt(0);
				if (!TryApplyImpact(report) || !Object.op_Implicit((Object)(object)this) || !inSync)
				{
					return;
				}
			}
			recentAttacks.RemoveAll((RecentAttack a) => age - a.age > 1f);
			pendingAttacks.RemoveAll((AttackReportMessage a) => age - a.age > 1f);
		}

		public void EnqueueImpact(ImpactReportMessage report)
		{
			if (!ClientDecides || report.shot != shot)
			{
				return;
			}
			if (++reportCount > 512)
			{
				Desync("too many reports for one projectile");
				return;
			}
			int num = pendingImpacts.FindIndex((ImpactReportMessage r) => r.age > report.age);
			pendingImpacts.Insert((num < 0) ? pendingImpacts.Count : num, report);
		}

		private bool TryApplyImpact(ImpactReportMessage report)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			float connectionFixedSmoothedRtt = RttManager.GetConnectionFixedSmoothedRtt(Owner);
			float num = 0f;
			Collider val2;
			Vector3 val3;
			Vector3 val4;
			if (report.hitHurtBox)
			{
				HurtBox val = ((HurtBoxReference)(ref report.hurtBox)).ResolveHurtBox();
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.collider))
				{
					return true;
				}
				val2 = val.collider;
				Transform transform = ((Component)val).transform;
				val3 = transform.TransformPoint(report.center);
				val4 = transform.TransformPoint(report.point);
				num = GetSpeed(val.healthComponent);
			}
			else
			{
				val3 = report.center;
				val4 = report.point;
				val2 = FindWorldCollider(val4, report.normal);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					Desync("nothing to hit at a reported impact point");
					return false;
				}
			}
			float num2;
			if (!Object.op_Implicit((Object)(object)rb))
			{
				num2 = 0f;
			}
			else
			{
				Vector3 velocity = rb.velocity;
				num2 = ((Vector3)(ref velocity)).magnitude;
			}
			float num3 = Mathf.Max(num2, ((Vector3)(ref report.velocity)).magnitude);
			float num4 = PluginConfig.ImpactTolerance.Value + num3 * 0.25f + num * (connectionFixedSmoothedRtt + 0.3f);
			float num5 = Vector3.Distance(val3, ((Component)this).transform.position);
			if (num5 > num4)
			{
				Desync($"reported impact {num5:0.0}m from host copy (tolerance {num4:0.0}m)");
				return false;
			}
			Teleport(val3, report.rotation, report.velocity);
			if (PluginConfig.VerboseLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"[{profile.name}#{predictionId}] owner impact on {((Object)val2).name} at age {report.age:0.000} ({num5:0.0}m correction)");
			}
			ProjectileImpactInfo val5 = new ProjectileImpactInfo
			{
				collider = val2,
				estimatedPointOfImpact = val4,
				estimatedImpactNormal = report.normal
			};
			EchoSuppression.EnterOwnedScope(this, (Component)(object)controller);
			try
			{
				controller.PerformImpact(val5);
			}
			finally
			{
				EchoSuppression.ExitOwnedScope();
			}
			return true;
		}

		public void OnAttackReport(AttackReportMessage report)
		{
			if (!ClientDecides || report.shot != shot)
			{
				return;
			}
			if (++reportCount > 512)
			{
				Desync("too many reports for one projectile");
				return;
			}
			float age = Age;
			if (report.age < age - Tick * 2f)
			{
				ApplyToRecentAttack(report, age);
				return;
			}
			int num = pendingAttacks.FindIndex((AttackReportMessage r) => r.age > report.age);
			pendingAttacks.Insert((num < 0) ? pendingAttacks.Count : num, report);
		}

		private void ApplyToRecentAttack(AttackReportMessage report, float age)
		{
			for (int i = 0; i < recentAttacks.Count; i++)
			{
				RecentAttack recentAttack = recentAttacks[i];
				if (!recentAttack.filled && recentAttack.kind == report.kind && recentAttack.key == report.key && Mathf.Abs(recentAttack.age - report.age) <= Tick * 3f)
				{
					recentAttack.filled = true;
					recentAttacks[i] = recentAttack;
					ApplyLate(recentAttack, report);
					return;
				}
			}
			if (PluginConfig.VerboseLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"[{profile.name}#{predictionId}] hit report for age {report.age:0.000} arrived at {age:0.000} with no matching host attack; consider a larger report buffer");
			}
		}

		private AttackReportMessage TakeAttackReport(AttackKind kind, int attackKey, object attack, Vector3 center, float range)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			float age = Age;
			AttackReportMessage attackReportMessage = null;
			for (int i = 0; i < pendingAttacks.Count; i++)
			{
				AttackReportMessage attackReportMessage2 = pendingAttacks[i];
				if (attackReportMessage2.age > age + Tick * 2f)
				{
					break;
				}
				if (attackReportMessage2.kind == kind && attackReportMessage2.key == attackKey)
				{
					attackReportMessage = attackReportMessage2;
					pendingAttacks.RemoveAt(i);
					break;
				}
			}
			recentAttacks.Add(new RecentAttack
			{
				kind = kind,
				key = attackKey,
				age = age,
				attack = attack,
				filled = (attackReportMessage != null),
				center = center,
				range = range
			});
			return attackReportMessage;
		}

		public HitPoint[] TakeBlastHits(BlastAttack blast, int attackKey)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			return ToBlastHits(TakeAttackReport(AttackKind.Blast, attackKey, blast, blast.position, blast.radius), blast.position, blast.radius);
		}

		public List<OverlapInfo> TakeOverlapHits(OverlapAttack attack, int attackKey)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			GetOverlapBounds(attack, out var center, out var range);
			return ToOverlapHits(TakeAttackReport(AttackKind.Overlap, attackKey, attack, center, range), center, range);
		}

		private void ApplyLate(RecentAttack recent, AttackReportMessage report)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			object attack = recent.attack;
			BlastAttack val = (BlastAttack)((attack is BlastAttack) ? attack : null);
			if (val != null)
			{
				HitPoint[] array = ToBlastHits(report, recent.center, recent.range);
				if (array.Length != 0)
				{
					Detection.ApplyLateBlast(Owner, val, array);
				}
				return;
			}
			object attack2 = recent.attack;
			OverlapAttack val2 = (OverlapAttack)((attack2 is OverlapAttack) ? attack2 : null);
			if (val2 != null)
			{
				List<OverlapInfo> list = ToOverlapHits(report, recent.center, recent.range);
				if (list.Count > 0)
				{
					Detection.ApplyLateOverlap(Owner, val2, list);
				}
			}
		}

		private HitPoint[] ToBlastHits(AttackReportMessage report, Vector3 center, float radius)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if (report == null || report.hits.Count == 0)
			{
				return Array.Empty<HitPoint>();
			}
			List<HitPoint> list = new List<HitPoint>(report.hits.Count);
			foreach (HitRecord hit in report.hits)
			{
				Vector3 worldPosition;
				HurtBox val = ResolveHit(hit, center, radius, out worldPosition);
				if (Object.op_Implicit((Object)(object)val))
				{
					list.Add(new HitPoint
					{
						hurtBox = val,
						hitPosition = worldPosition,
						hitNormal = hit.direction,
						hitOrigin = hit.hitOrigin,
						distanceSqr = hit.distanceSqr
					});
				}
			}
			return list.ToArray();
		}

		private List<OverlapInfo> ToOverlapHits(AttackReportMessage report, Vector3 center, float range)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			List<OverlapInfo> list = new List<OverlapInfo>();
			if (report == null || report.hits.Count == 0)
			{
				return list;
			}
			foreach (HitRecord hit in report.hits)
			{
				Vector3 worldPosition;
				HurtBox val = ResolveHit(hit, center, range, out worldPosition);
				if (Object.op_Implicit((Object)(object)val))
				{
					list.Add(new OverlapInfo
					{
						hurtBox = val,
						hitPosition = worldPosition,
						pushDirection = hit.direction,
						hitOrigin = hit.hitOrigin
					});
				}
			}
			return list;
		}

		private HurtBox ResolveHit(HitRecord record, Vector3 attackCenter, float attackRange, out Vector3 worldPosition)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			worldPosition = default(Vector3);
			HurtBox val = ((HurtBoxReference)(ref record.hurtBox)).ResolveHurtBox();
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.healthComponent) || !val.healthComponent.alive)
			{
				return null;
			}
			worldPosition = ((Component)val).transform.TransformPoint(record.localPosition);
			float connectionFixedSmoothedRtt = RttManager.GetConnectionFixedSmoothedRtt(Owner);
			float num = attackRange + PluginConfig.ImpactTolerance.Value + GetSpeed(val.healthComponent) * (connectionFixedSmoothedRtt + 0.3f);
			Vector3 val2 = ((Component)val).transform.position - attackCenter;
			if (((Vector3)(ref val2)).sqrMagnitude > num * num)
			{
				if (PluginConfig.VerboseLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"[{profile.name}#{predictionId}] ignored reported hit on {((Object)val.healthComponent).name}: too far from the attack on the host");
				}
				return null;
			}
			return val;
		}

		private void GetOverlapBounds(OverlapAttack attack, out Vector3 center, out float range)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			center = ((Component)this).transform.position;
			range = 5f;
			if (!Object.op_Implicit((Object)(object)attack.hitBoxGroup) || attack.hitBoxGroup.hitBoxes == null)
			{
				return;
			}
			HitBox[] hitBoxes = attack.hitBoxGroup.hitBoxes;
			foreach (HitBox val in hitBoxes)
			{
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)((Component)val).transform))
				{
					Vector3 lossyScale = ((Component)val).transform.lossyScale;
					center = ((Component)val).transform.position;
					range = Mathf.Max(new float[3]
					{
						Mathf.Abs(lossyScale.x),
						Mathf.Abs(lossyScale.y),
						Mathf.Abs(lossyScale.z)
					});
					break;
				}
			}
		}

		private void Teleport(Vector3 position, Quaternion rotation, Vector3? velocity)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			((Component)this).transform.SetPositionAndRotation(position, rotation);
			if (Object.op_Implicit((Object)(object)rb))
			{
				rb.position = position;
				rb.rotation = rotation;
				if (velocity.HasValue && !rb.isKinematic)
				{
					rb.velocity = velocity.Value;
				}
			}
		}

		private static float GetSpeed(HealthComponent health)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)health) || !Object.op_Implicit((Object)(object)health.body))
			{
				return 0f;
			}
			CharacterBody body = health.body;
			if (Object.op_Implicit((Object)(object)body.characterMotor))
			{
				return ((Vector3)(ref body.characterMotor.velocity)).magnitude;
			}
			if (Object.op_Implicit((Object)(object)body.rigidbody))
			{
				Vector3 velocity = body.rigidbody.velocity;
				return ((Vector3)(ref velocity)).magnitude;
			}
			return 0f;
		}

		private Collider FindWorldCollider(Vector3 point, Vector3 normal)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			int num = PhysicsUtil.CollisionMask(((Component)this).gameObject.layer);
			LayerIndex entityPrecise = LayerIndex.entityPrecise;
			LayerMask mask = ((LayerIndex)(ref entityPrecise)).mask;
			int num2 = num & ~((LayerMask)(ref mask)).value;
			if (((Vector3)(ref normal)).sqrMagnitude > 0.01f)
			{
				((Vector3)(ref normal)).Normalize();
				RaycastHit val = default(RaycastHit);
				if (Physics.Raycast(point + normal * 0.5f, -normal, ref val, 1.5f, num2, (QueryTriggerInteraction)2) && !((Component)((RaycastHit)(ref val)).collider).transform.IsChildOf(((Component)this).transform))
				{
					return ((RaycastHit)(ref val)).collider;
				}
			}
			Collider[] array = Physics.OverlapSphere(point, 0.75f, num2, (QueryTriggerInteraction)2);
			Collider result = null;
			float num3 = float.MaxValue;
			Collider[] array2 = array;
			HurtBox val3 = default(HurtBox);
			foreach (Collider val2 in array2)
			{
				if (Object.op_Implicit((Object)(object)val2) && !((Component)val2).transform.IsChildOf(((Component)this).transform) && !((Component)val2).TryGetComponent<HurtBox>(ref val3))
				{
					Vector3 val4 = val2.ClosestPoint(point) - point;
					float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
					if (sqrMagnitude < num3)
					{
						num3 = sqrMagnitude;
						result = val2;
					}
				}
			}
			return result;
		}
	}
	internal enum AttackKind : byte
	{
		Blast = 1,
		Overlap
	}
	internal struct HitRecord
	{
		public HurtBoxReference hurtBox;

		public Vector3 localPosition;

		public Vector3 direction;

		public Vector3 hitOrigin;

		public float distanceSqr;
	}
	internal sealed class AttackReportMessage : MessageBase
	{
		public ushort predictionId;

		public uint shot;

		public float age;

		public AttackKind kind;

		public int key;

		public readonly List<HitRecord> hits = new List<HitRecord>();

		public override void Serialize(NetworkWriter writer)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			writer.WritePackedUInt32((uint)predictionId);
			writer.WritePackedUInt32(shot);
			writer.Write(age);
			writer.Write((byte)kind);
			writer.Write(key);
			writer.WritePackedUInt32((uint)hits.Count);
			foreach (HitRecord hit in hits)
			{
				HurtBoxReference hurtBox = hit.hurtBox;
				((HurtBoxReference)(ref hurtBox)).Write(writer);
				writer.Write(hit.localPosition);
				writer.Write(hit.direction);
				writer.Write(hit.hitOrigin);
				writer.Write(hit.distanceSqr);
			}
		}

		public override void Deserialize(NetworkReader reader)
		{
			//IL_0075: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			predictionId = (ushort)reader.ReadPackedUInt32();
			shot = reader.ReadPackedUInt32();
			age = reader.ReadSingle();
			kind = (AttackKind)reader.ReadByte();
			key = reader.ReadInt32();
			int num = (int)Math.Min(reader.ReadPackedUInt32(), 256u);
			hits.Clear();
			for (int i = 0; i < num; i++)
			{
				HitRecord item = default(HitRecord);
				((HurtBoxReference)(ref item.hurtBox)).Read(reader);
				item.localPosition = reader.ReadVector3();
				item.direction = reader.ReadVector3();
				item.hitOrigin = reader.ReadVector3();
				item.distanceSqr = reader.ReadSingle();
				hits.Add(item);
			}
		}
	}
	internal static class Detection
	{
		[HarmonyPatch(typeof(BlastAttack), "Fire")]
		private static class BlastFirePatch
		{
			private static bool Prefix(BlastAttack __instance, ref Result __result, out HitPoint[] __state)
			{
				//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
				__state = blastInjection;
				if (NetworkServer.active)
				{
					blastInjection = null;
					EchoSuppression.OwnedFrame frame;
					if (forcedBlastInjection != null)
					{
						blastInjection = forcedBlastInjection;
						forcedBlastInjection = null;
					}
					else if (EchoSuppression.TryGetCurrentAttack(out frame))
					{
						blastInjection = frame.tracker.TakeBlastHits(__instance, KeyFor(AttackKind.Blast, frame.component));
					}
					return true;
				}
				if (Sandbox.depth == 0)
				{
					return true;
				}
				HitPoint[] array;
				try
				{
					array = collectHits(__instance) ?? Array.Empty<HitPoint>();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogDebug((object)("[detection] blast hit collection failed: " + ex.Message));
					array = Array.Empty<HitPoint>();
				}
				ReportBlast(__instance, array);
				PlayBlastHitEffects(__instance, array);
				__result = new Result
				{
					hitCount = array.Length,
					hitPoints = array
				};
				return false;
			}

			private static void Finalizer(HitPoint[] __state)
			{
				blastInjection = __state;
			}
		}

		[HarmonyPatch(typeof(BlastAttack), "CollectHits")]
		private static class CollectHitsPatch
		{
			private static bool Prefix(ref HitPoint[] __result)
			{
				if (blastInjection != null && NetworkServer.active)
				{
					__result = blastInjection;
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(OverlapAttack), "Fire")]
		private static class OverlapFirePatch
		{
			private static bool Prefix(OverlapAttack __instance, List<HurtBox> hitResults, ref bool __result)
			{
				if (!NetworkServer.active || !EchoSuppression.TryGetCurrentAttack(out var frame))
				{
					return true;
				}
				List<OverlapInfo> hits = frame.tracker.TakeOverlapHits(__instance, KeyFor(AttackKind.Overlap, frame.component));
				__result = ApplyOverlapHits(__instance, hits, hitResults);
				return false;
			}
		}

		[HarmonyPatch(typeof(OverlapAttack), "ProcessHits")]
		private static class ProcessHitsCapture
		{
			private static void Postfix(List<OverlapInfo> hitList)
			{
				//IL_0069: Unknown result type (might be due to invalid IL or missing references)
				//IL_006e: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_009e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				if (hitList == null || hitList.Count == 0 || !TryGetClientFrame(out var frame))
				{
					return;
				}
				AttackReportMessage attackReportMessage = new AttackReportMessage
				{
					predictionId = frame.controller.predictionId,
					age = frame.Age,
					shot = frame.shot,
					kind = AttackKind.Overlap,
					key = KeyFor(AttackKind.Overlap, frame.component)
				};
				foreach (OverlapInfo hit in hitList)
				{
					if (Object.op_Implicit((Object)(object)hit.hurtBox))
					{
						attackReportMessage.hits.Add(new HitRecord
						{
							hurtBox = HurtBoxReference.FromHurtBox(hit.hurtBox),
							localPosition = ((Component)hit.hurtBox).transform.InverseTransformPoint(hit.hitPosition),
							direction = hit.pushDirection,
							hitOrigin = hit.hitOrigin
						});
					}
				}
				if (attackReportMessage.hits.Count > 0)
				{
					NetProtocol.SendAttackReport(attackReportMessage);
				}
			}
		}

		private static readonly Func<BlastAttack, HitPoint[]> collectHits = AccessTools.MethodDelegate<Func<BlastAttack, HitPoint[]>>(AccessTools.Method(typeof(BlastAttack), "CollectHits", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<OverlapAttack, List<OverlapInfo>> processHits = AccessTools.MethodDelegate<Action<OverlapAttack, List<OverlapInfo>>>(AccessTools.Method(typeof(OverlapAttack), "ProcessHits", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<OverlapAttack, GameObject> setLastHitObject = AccessTools.MethodDelegate<Action<OverlapAttack, GameObject>>(AccessTools.PropertySetter(typeof(OverlapAttack), "lastHitObject"), (object)null, true);

		private static readonly Action<OverlapAttack, HealthComponent> addIgnoredHitList = TryGetAddIgnored();

		private static HitPoint[] blastInjection;

		private static readonly Dictionary<int, int> componentKeyCache = new Dictionary<int, int>();

		private static HitPoint[] forcedBlastInjection;

		private static Action<OverlapAttack, HealthComponent> TryGetAddIgnored()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(OverlapAttack), "addIgnoredHitList", new Type[1] { typeof(HealthComponent) }, (Type[])null);
			if (!(methodInfo == null))
			{
				return AccessTools.MethodDelegate<Action<OverlapAttack, HealthComponent>>(methodInfo, (object)null, true);
			}
			return null;
		}

		public static int KeyFor(AttackKind kind, Component component)
		{
			if (!Object.op_Implicit((Object)(object)component))
			{
				return (int)kind;
			}
			int instanceID = ((Object)component).GetInstanceID();
			if (!componentKeyCache.TryGetValue(instanceID, out var value))
			{
				if (componentKeyCache.Count > 4096)
				{
					componentKeyCache.Clear();
				}
				value = Fnv1a(((object)component).GetType().FullName + "/" + ((Object)component.gameObject).name);
				componentKeyCache[instanceID] = value;
			}
			return value * 31 + (int)kind;
		}

		private static int Fnv1a(string s)
		{
			uint num = 2166136261u;
			foreach (char c in s)
			{
				num = (num ^ c) * 16777619;
			}
			return (int)num;
		}

		private static bool TryGetClientFrame(out Sandbox.Frame frame)
		{
			frame = Sandbox.Current;
			if (Sandbox.depth > 0 && !NetworkServer.active && NetProtocol.HostAllowsClientImpacts && Object.op_Implicit((Object)(object)frame.controller))
			{
				return frame.controller.predictionId != 0;
			}
			return false;
		}

		private static void ReportBlast(BlastAttack blast, HitPoint[] hits)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: 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_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetClientFrame(out var frame))
			{
				return;
			}
			AttackReportMessage attackReportMessage = new AttackReportMessage
			{
				predictionId = frame.controller.predictionId,
				age = frame.Age,
				shot = frame.shot,
				kind = AttackKind.Blast,
				key = KeyFor(AttackKind.Blast, frame.component)
			};
			foreach (HitPoint val in hits)
			{
				if (Object.op_Implicit((Object)(object)val.hurtBox) && Object.op_Implicit((Object)(object)val.hurtBox.healthComponent))
				{
					attackReportMessage.hits.Add(new HitRecord
					{
						hurtBox = HurtBoxReference.FromHurtBox(val.hurtBox),
						localPosition = ((Component)val.hurtBox).transform.InverseTransformPoint(val.hitPosition),
						direction = val.hitNormal,
						hitOrigin = val.hitOrigin,
						distanceSqr = val.distanceSqr
					});
				}
			}
			if (attackReportMessage.hits.Count > 0)
			{
				NetProtocol.SendAttackReport(attackReportMessage);
			}
		}

		private static void PlayBlastHitEffects(BlastAttack blast, HitPoint[] hits)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			if ((int)blast.impactEffect == -1)
			{
				return;
			}
			foreach (HitPoint val in hits)
			{
				if (Object.op_Implicit((Object)(object)val.hurtBox) && Object.op_Implicit((Object)(object)val.hurtBox.healthComponent))
				{
					Vector3 val2 = val.hitPosition - blast.position;
					EffectManager.SpawnEffect(blast.impactEffect, new EffectData
					{
						origin = val.hitPosition,
						rotation = Util.QuaternionSafeLookRotation(-val2)
					}, false);
				}
			}
		}

		public static void ApplyLateBlast(NetworkConnection owner, BlastAttack blast, HitPoint[] hits)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			forcedBlastInjection = hits;
			EchoSuppression.EnterLateScope(owner);
			try
			{
				blast.Fire();
			}
			finally
			{
				EchoSuppression.ExitOwnedScope();
				forcedBlastInjection = null;
			}
		}

		public static bool ApplyOverlapHits(OverlapAttack attack, List<OverlapInfo> hits, List<HurtBox> hitResults)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (hits == null || hits.Count == 0)
			{
				return false;
			}
			foreach (OverlapInfo hit in hits)
			{
				HealthComponent healthComponent = hit.hurtBox.healthComponent;
				setLastHitObject(attack, ((Component)healthComponent).gameObject);
				addIgnoredHitList?.Invoke(attack, healthComponent);
				hitResults?.Add(hit.hurtBox);
			}
			int count = hits.Count;
			processHits(attack, hits);
			return count > 0;
		}

		public static void ApplyLateOverlap(NetworkConnection owner, OverlapAttack attack, List<OverlapInfo> hits)
		{
			EchoSuppression.EnterLateScope(owner);
			try
			{
				ApplyOverlapHits(attack, hits, null);
			}
			finally
			{
				EchoSuppression.ExitOwnedScope();
			}
		}
	}
	internal static class EchoSuppression
	{
		public struct OwnedFrame
		{
			public ClientOwnedProjectile tracker;

			public Component component;

			public NetworkConnection lateOwner;
		}

		[HarmonyPatch(typeof(EffectManager), "TransmitEffect")]
		private static class TransmitEffectPatch
		{
			private static void Prefix(ref NetworkConnection netOrigin)
			{
				if (netOrigin == null && NetworkServer.active)
				{
					netOrigin = CurrentExcludedConnection;
				}
			}
		}

		private static readonly Stack<OwnedFrame> ownedScopes = new Stack<OwnedFrame>();

		public static int damageDepth;

		public static NetworkConnection CurrentExcludedConnection
		{
			get
			{
				if (damageDepth != 0 || ownedScopes.Count == 0)
				{
					return null;
				}
				OwnedFrame ownedFrame = ownedScopes.Peek();
				if (ownedFrame.lateOwner != null)
				{
					return ownedFrame.lateOwner;
				}
				if (!Object.op_Implicit((Object)(object)ownedFrame.tracker) || !ownedFrame.tracker.ClientDecides)
				{
					return null;
				}
				return ownedFrame.tracker.Owner;
			}
		}

		public static bool TryGetCurrent(out OwnedFrame frame)
		{
			if (ownedScopes.Count > 0)
			{
				frame = ownedScopes.Peek();
				if (frame.lateOwner == null && Object.op_Implicit((Object)(object)frame.tracker))
				{
					return frame.tracker.ClientDecides;
				}
				return false;
			}
			frame = default(OwnedFrame);
			return false;
		}

		public static bool TryGetCurrentAttack(out OwnedFrame frame)
		{
			frame = default(OwnedFrame);
			if (damageDepth == 0)
			{
				return TryGetCurrent(out frame);
			}
			return false;
		}

		public static bool TryEnterOwnedScope(Component component)
		{
			if (!ClientOwnedProjectile.TryFindForObject(component.gameObject, out var tracker) || !tracker.ClientDecides)
			{
				return false;
			}
			ownedScopes.Push(new OwnedFrame
			{
				tracker = tracker,
				component = component
			});
			return true;
		}

		public static void EnterOwnedScope(ClientOwnedProjectile tracker, Component component)
		{
			ownedScopes.Push(new OwnedFrame
			{
				tracker = tracker,
				component = component
			});
		}

		public static void EnterLateScope(NetworkConnection owner)
		{
			ownedScopes.Push(new OwnedFrame
			{
				lateOwner = owner
			});
		}

		public static void ExitOwnedScope()
		{
			if (ownedScopes.Count > 0)
			{
				ownedScopes.Pop();
			}
		}

		public static void Reset()
		{
			ownedScopes.Clear();
			damageDepth = 0;
		}
	}
	internal static class NetProtocol
	{
		[Flags]
		public enum HostFeatures : byte
		{
			None = 0,
			ClientImpacts = 1,
			FastForward = 2
		}

		[Flags]
		public enum ClientFeatures : byte
		{
			None = 0,
			SimulatesProjectiles = 1
		}

		private sealed class ClientInfo
		{
			public ClientFeatures features;

			public readonly HashSet<int> sandboxDisabled = new HashSet<int>();
		}

		private sealed class EarlyReports
		{
			public float receivedAt;

			public uint confirmedShot;

			public readonly List<ImpactReportMessage> impacts = new List<ImpactReportMessage>();

			public readonly List<AttackReportMessage> attacks = new List<AttackReportMessage>();
		}

		private sealed class HelloMessage : MessageBase
		{
			public int protocolVersion;

			public ClientFeatures features;

			public override void Serialize(NetworkWriter writer)
			{
				writer.Write(protocolVersion);
				writer.Write((byte)features);
			}

			public override void Deserialize(NetworkReader reader)
			{
				protocolVersion = reader.ReadInt32();
				features = (ClientFeatures)((reader.Position < reader.Length) ? reader.ReadByte() : 0);
			}
		}

		private sealed class WelcomeMessage : MessageBase
		{
			public int protocolVersion;

			public HostFeatures features;

			public float maxFastForward;

			public float reportBuffer;

			public override void Serialize(NetworkWriter writer)
			{
				writer.Write(protocolVersion);
				writer.Write((byte)features);
				writer.Write(maxFastForward);
				writer.Write(reportBuffer);
			}

			public override void Deserialize(NetworkReader reader)
			{
				protocolVersion = reader.ReadInt32();
				features = (HostFeatures)reader.ReadByte();
				maxFastForward = reader.ReadSingle();
				reportBuffer = ((reader.Position < reader.Length) ? reader.ReadSingle() : 0f);
			}
		}

		private sealed class ImpactRejectedMessage : MessageBase
		{
			public ushort predictionId;

			public override void Serialize(NetworkWriter writer)
			{
				writer.WritePackedUInt32((uint)predictionId);
			}

			public override void Deserialize(NetworkReader reader)
			{
				predictionId = (ushort)reader.ReadPackedUInt32();
			}
		}

		private sealed class SimulatingMessage : MessageBase
		{
			public ushort predictionId;

			public uint shot;

			public override void Serialize(NetworkWriter writer)
			{
				writer.WritePackedUInt32((uint)predictionId);
				writer.WritePackedUInt32(shot);
			}

			public override void Deserialize(NetworkReader reader)
			{
				predictionId = (ushort)reader.ReadPackedUInt32();
				shot = reader.ReadPackedUInt32();
			}
		}

		private sealed class SandboxDisabledMessage : MessageBase
		{
			public int catalogIndex;

			public override void Serialize(NetworkWriter writer)
			{
				writer.WritePackedUInt32((uint)catalogIndex);
			}

			public override void Deserialize(NetworkReader reader)
			{
				catalogIndex = (int)reader.ReadPackedUInt32();
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Action <0>__OnStartServer;

			public static Action<NetworkClient> <1>__OnStartClient;

			public static Action<NetworkConnection> <2>__OnClientConnect;

			public static Action <3>__ResetClientState;

			public static NetworkMessageDelegate <4>__ServerHandleHello;

			public static NetworkMessageDelegate <5>__ServerHandleImpactReport;

			public static NetworkMessageDelegate <6>__ServerHandleSandboxDisabled;

			public static NetworkMessageDelegate <7>__ServerHandleAttackReport;

			public static NetworkMessageDelegate <8>__ServerHandleSimulating;

			public static NetworkMessageDelegate <9>__ClientHandleWelcome;

			public static NetworkMessageDelegate <10>__ClientHandleImpactRejected;
		}

		public const int ProtocolVersion = 5;

		private const short MsgHello = 21437;

		private const short MsgWelcome = 21438;

		private const short MsgImpactReport = 21439;

		private const short MsgImpactRejected = 21440;

		private const short MsgSandboxDisabled = 21441;

		private const short MsgAttackReport = 21442;

		private const short MsgSimulating = 21443;

		private static readonly Dictionary<long, EarlyReports> earlyReports = new Dictionary<long, EarlyReports>();

		private static readonly List<long> expiredEarly = new List<long>();

		private static readonly Dictionary<NetworkConnection, ClientInfo> moddedClients = new Dictionary<NetworkConnection, ClientInfo>();

		public static bool HostHasMod { get; private set; }

		public static HostFeatures HostFlags { get; private set; }

		public static float HostMaxFastForward { get; private set; }

		public static float HostReportBuffer { get; private set; }

		public static bool HostAllowsClientImpacts
		{
			get
			{
				if (HostHasMod)
				{
					return (HostFlags & HostFeatures.ClientImpacts) != 0;
				}
				return false;
			}
		}

		public static bool HostFastForwards
		{
			get
			{
				if (HostHasMod)
				{
					return (HostFlags & HostFeatures.FastForward) != 0;
				}
				return false;
			}
		}

		public static bool IsModdedConnection(NetworkConnection conn)
		{
			if (conn != null)
			{
				return moddedClients.ContainsKey(conn);
			}
			return false;
		}

		public static bool ClientSimulates(NetworkConnection conn, int catalogIndex)
		{
			if (conn != null && moddedClients.TryGetValue(conn, out var value) && (value.features & ClientFeatures.SimulatesProjectiles) != ClientFeatures.None)
			{
				return !value.sandboxDisabled.Contains(catalogIndex);
			}
			return false;
		}

		public static void Init()
		{
			NetworkManagerSystem.onStartServerGlobal += OnStartServer;
			NetworkManagerSystem.onStopServerGlobal += delegate
			{
				moddedClients.Clear();
				earlyReports.Clear();
				EchoSuppression.Reset();
			};
			NetworkManagerSystem.onServerDisconnectGlobal += delegate(NetworkConnection conn)
			{
				moddedClients.Remove(conn);
			};
			NetworkManagerSystem.onStartClientGlobal += OnStartClient;
			NetworkManagerSystem.onClientConnectGlobal += OnClientConnect;
			NetworkManagerSystem.onClientDisconnectGlobal += delegate
			{
				ResetClientState();
			};
			NetworkManagerSystem.onStopClientGlobal += ResetClientState;
		}

		private static void OnStartServer()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_005f: 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_006a: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			object obj = <>O.<4>__ServerHandleHello;
			if (obj == null)
			{
				NetworkMessageDelegate val = ServerHandleHello;
				<>O.<4>__ServerHandleHello = val;
				obj = (object)val;
			}
			NetworkServer.RegisterHandler((short)21437, (NetworkMessageDelegate)obj);
			object obj2 = <>O.<5>__ServerHandleImpactReport;
			if (obj2 == null)
			{
				NetworkMessageDelegate val2 = ServerHandleImpactReport;
				<>O.<5>__ServerHandleImpactReport = val2;
				obj2 = (object)val2;
			}
			NetworkServer.RegisterHandler((short)21439, (NetworkMessageDelegate)obj2);
			object obj3 = <>O.<6>__ServerHandleSandboxDisabled;
			if (obj3 == null)
			{
				NetworkMessageDelegate val3 = ServerHandleSandboxDisabled;
				<>O.<6>__ServerHandleSandboxDisabled = val3;
				obj3 = (object)val3;
			}
			NetworkServer.RegisterHandler((short)21441, (NetworkMessageDelegate)obj3);
			object obj4 = <>O.<7>__ServerHandleAttackReport;
			if (obj4 == null)
			{
				NetworkMessageDelegate val4 = ServerHandleAttackReport;
				<>O.<7>__ServerHandleAttackReport = val4;
				obj4 = (object)val4;
			}
			NetworkServer.RegisterHandler((short)21442, (NetworkMessageDelegate)obj4);
			object obj5 = <>O.<8>__ServerHandleSimulating;
			if (obj5 == null)
			{
				NetworkMessageDelegate val5 = ServerHandleSimulating;
				<>O.<8>__ServerHandleSimulating = val5;
				obj5 = (object)val5;
			}
			NetworkServer.RegisterHandler((short)21443, (NetworkMessageDelegate)obj5);
		}

		private static void OnStartClient(NetworkClient client)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			object obj = <>O.<9>__ClientHandleWelcome;
			if (obj == null)
			{
				NetworkMessageDelegate val = ClientHandleWelcome;
				<>O.<9>__ClientHandleWelcome = val;
				obj = (object)val;
			}
			client.RegisterHandler((short)21438, (NetworkMessageDelegate)obj);
			object obj2 = <>O.<10>__ClientHandleImpactRejected;
			if (obj2 == null)
			{
				NetworkMessageDelegate val2 = ClientHandleImpactRejected;
				<>O.<10>__ClientHandleImpactRejected = val2;
				obj2 = (object)val2;
			}
			client.RegisterHandler((short)21440, (NetworkMessageDelegate)obj2);
		}

		private static void OnClientConnect(NetworkConnection conn)
		{
			ResetClientState();
			if (!NetworkServer.active)
			{
				HelloMessage msg = new HelloMessage
				{
					protocolVersion = 5,
					features = (PluginConfig.SimulateLocally.Value ? ClientFeatures.SimulatesProjectiles : ClientFeatures.None)
				};
				SendIsolated(conn, 21437, (MessageBase)(object)msg, QosChannelIndex.defaultReliable.intVal);
			}
		}

		private static void SendIsolated(NetworkConnection conn, short msgType, MessageBase msg, int channel)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			NetworkWriter val = new NetworkWriter();
			val.StartMessage(msgType);
			msg.Serialize(val);
			val.FinishMessage();
			byte b = default(byte);
			if (!conn.TransportSend(val.AsArray(), (int)val.Position, channel, ref b))
			{
				Plugin.Log.LogWarning((object)$"Failed to send handshake (error {b}); staying vanilla for this session.");
			}
		}

		private static void ResetClientState()
		{
			if (HostHasMod)
			{
				Plugin.Log.LogInfo((object)"Disconnected from modded host; client projectile ownership disabled.");
			}
			HostHasMod = false;
			HostFlags = HostFeatures.None;
			HostMaxFastForward = 0f;
			HostReportBuffer = 0f;
			ProjectileProfiles.SetForcedPrediction(enabled: false);
			Sandbox.ResetSession();
			PredictedProjectile.ClearAll();
		}

		private static void ServerHandleHello(NetworkMessage netMsg)
		{
			HelloMessage helloMessage = netMsg.ReadMessage<HelloMessage>();
			if (helloMessage.protocolVersion != 5)
			{
				Plugin.Log.LogWarning((object)string.Format("A client has {0} protocol v{1} but the host has v{2}; treating that client as vanilla. Update everyone to the same version.", "ClientPredictedProjectiles", helloMessage.protocolVersion, 5));
				return;
			}
			moddedClients[netMsg.conn] = new ClientInfo
			{
				features = helloMessage.features
			};
			HostFeatures hostFeatures = HostFeatures.None;
			if (PluginConfig.ClientImpacts.Value)
			{
				hostFeatures |= HostFeatures.ClientImpacts;
			}
			if (PluginConfig.ServerFastForward.Value)
			{
				hostFeatures |= HostFeatures.FastForward;
			}
			WelcomeMessage welcomeMessage = new WelcomeMessage
			{
				protocolVersion = 5,
				features = hostFeatures,
				maxFastForward = PluginConfig.MaxFastForward.Value,
				reportBuffer = PluginConfig.ReportBuffer.Value
			};
			netMsg.conn.SendByChannel((short)21438, (MessageBase)(object)welcomeMessage, QosChannelIndex.defaultReliable.intVal);
		}

		private static void ClientHandleWelcome(NetworkMessage netMsg)
		{
			WelcomeMessage welcomeMessage = netMsg.ReadMessage<WelcomeMessage>();
			if (welcomeMessage.protocolVersion == 5 && !NetworkServer.active)
			{
				HostHasMod = true;
				HostFlags = welcomeMessage.features;
				HostMaxFastForward = welcomeMessage.maxFastForward;
				HostReportBuffer = welcomeMessage.reportBuffer;
				ProjectileProfiles.SetForcedPrediction(PluginConfig.PredictAll.Value);
				Sandbox.SetEnabled(PluginConfig.SimulateLocally.Value && HostAllowsClientImpacts);
				Plugin.Log.LogInfo((object)string.Format("Host has {0} (features: {1}); your projectiles are now client-owned.", "ClientPredictedProjectiles", welcomeMessage.features));
			}
		}

		public static void SendImpactReport(ImpactReportMessage report)
		{
			NetworkManager singleton = NetworkManager.singleton;
			object obj;
			if (singleton == null)
			{
				obj = null;
			}
			else
			{
				NetworkClient client = singleton.client;
				obj = ((client != null) ? client.connection : null);
			}
			NetworkConnection val = (NetworkConnection)obj;
			if (val == null || !val.isConnected)
			{
				return;
			}
			bool allowSend = Sandbox.allowSend;
			Sandbox.allowSend = true;
			try
			{
				val.SendByChannel((short)21439, (MessageBase)(object)report, QosChannelIndex.defaultReliable.intVal);
			}
			finally
			{
				Sandbox.allowSend = allowSend;
			}
		}

		public static void SendSandboxDisabled(int catalogIndex)
		{
			NetworkManager singleton = NetworkManager.singleton;
			object obj;
			if (singleton == null)
			{
				obj = null;
			}
			else
			{
				NetworkClient client = singleton.client;
				obj = ((client != null) ? client.connection : null);
			}
			NetworkConnection val = (NetworkConnection)obj;
			if (!HostHasMod || val == null || !val.isConnected)
			{
				return;
			}
			bool allowSend = Sandbox.allowSend;
			Sandbox.allowSend = true;
			try
			{
				val.SendByChannel((short)21441, (MessageBase)(object)new SandboxDisabledMessage
				{
					catalogIndex = catalogIndex
				}, QosChannelIndex.defaultReliable.intVal);
			}
			finally
			{
				Sandbox.allowSend = allowSend;
			}
		}

		public static void SendAttackReport(AttackReportMessage report)
		{
			NetworkManager singleton = NetworkManager.singleton;
			object obj;
			if (singleton == null)
			{
				obj = null;
			}
			else
			{
				NetworkClient client = singleton.client;
				obj = ((client != null) ? client.connection : null);
			}
			NetworkConnection val = (NetworkConnection)obj;
			if (val == null || !val.isConnected)
			{
				return;
			}
			bool allowSend = Sandbox.allowSend;
			Sandbox.allowSend = true;
			try
			{
				val.SendByChannel((short)21442, (MessageBase)(object)report, QosChannelIndex.defaultReliable.intVal);
			}
			finally
			{
				Sandbox.allowSend = allowSend;
			}
		}

		public static void SendImpactRejected(NetworkConnection conn, ushort predictionId)
		{
			if (conn != null && conn.isConnected)
			{
				conn.SendByChannel((short)21440, (MessageBase)(object)new ImpactRejectedMessage
				{
					predictionId = predictionId
				}, QosChannelIndex.defaultReliable.intVal);
			}
		}

		private static void ServerHandleImpactReport(NetworkMessage netMsg)
		{
			ImpactReportMessage impactReportMessage = netMsg.ReadMessage<ImpactReportMessage>();
			if (IsModdedConnection(netMsg.conn))
			{
				if (ClientOwnedProjectile.TryGet(netMsg.conn, impactReportMessage.predictionId, out var tracker))
				{
					tracker.EnqueueImpact(impactReportMessage);
				}
				else
				{
					HoldEarly(netMsg.conn, impactReportMessage.predictionId).impacts.Add(impactReportMessage);
				}
			}
		}

		private static void ServerHandleAttackReport(NetworkMessage netMsg)
		{
			AttackReportMessage attackReportMessage = netMsg.ReadMessage<AttackReportMessage>();
			if (IsModdedConnection(netMsg.conn))
			{
				if (ClientOwnedProjectile.TryGet(netMsg.conn, attackReportMessage.predictionId, out var tracker))
				{
					tracker.OnAttackReport(attackReportMessage);
				}
				else if (!ClientOwnedProjectile.TryApplyToFinished(netMsg.conn, attackReportMessage))
				{
					HoldEarly(netMsg.conn, attackReportMessage.predictionId).attacks.Add(attackReportMessage);
				}
			}
		}

		public static void SendSimulating(ushort predictionId, uint shot)
		{
			NetworkManager singleton = NetworkManager.singleton;
			object obj;
			if (singleton == null)
			{
				obj = null;
			}
			else
			{
				NetworkClient client = singleton.client;
				obj = ((client != null) ? client.connection : null);
			}
			NetworkConnection val = (NetworkConnection)obj;
			if (!HostAllowsClientImpacts || val == null || !val.isConnected)
			{
				return;
			}
			bool allowSend = Sandbox.allowSend;
			Sandbox.allowSend = true;
			try
			{
				val.SendByChannel((short)21443, (MessageBase)(object)new SimulatingMessage
				{
					predictionId = predictionId,
					shot = shot
				}, QosChannelIndex.defaultReliable.intVal);
			}
			finally
			{
				Sandbox.allowSend = allowSend;
			}
		}

		private static void ServerHandleSimulating(NetworkMessage netMsg)
		{
			SimulatingMessage simulatingMessage = netMsg.ReadMessage<SimulatingMessage>();
			if (IsModdedConnection(netMsg.conn))
			{
				if (ClientOwnedProjectile.TryGet(netMsg.conn, simulatingMessage.predictionId, out var tracker))
				{
					tracker.ConfirmOwnerSimulating(simulatingMessage.shot);
				}
				else
				{
					HoldEarly(netMsg.conn, simulatingMessage.predictionId).confirmedShot = simulatingMessage.shot;
				}
			}
		}

		private static EarlyReports HoldEarly(NetworkConnection conn, ushort predictionId)
		{
			long key = ClientOwnedProjectile.MakeKey(conn, predictionId);
			if (!earlyReports.TryGetValue(key, out var value))
			{
				value = new EarlyReports
				{
					receivedAt = Time.unscaledTime
				};
				earlyReports[key] = value;
			}
			return value;
		}

		public static void DeliverEarlyReports(ClientOwnedProjectile tracker, NetworkConnection conn, ushort predictionId)
		{
			long key = ClientOwnedProjectile.MakeKey(conn, predictionId);
			if (!earlyReports.TryGetValue(key, out var value))
			{
				return;
			}
			earlyReports.Remove(key);
			if (value.confirmedShot == 0)
			{
				return;
			}
			tracker.ConfirmOwnerSimulating(value.confirmedShot);
			foreach (ImpactReportMessage impact in value.impacts)
			{
				tracker.EnqueueImpact(impact);
			}
			foreach (AttackReportMessage attack in value.attacks)
			{
				tracker.OnAttackReport(attack);
			}
		}

		public static void Tick()
		{
			ClientOwnedProjectile.PruneFinished();
			if (earlyReports.Count == 0)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			foreach (KeyValuePair<long, EarlyReports> earlyReport in earlyReports)
			{
				if (unscaledTime - earlyReport.Value.receivedAt > 2f)
				{
					expiredEarly.Add(earlyReport.Key);
				}
			}
			foreach (long item in expiredEarly)
			{
				earlyReports.Remove(item);
			}
			expiredEarly.Clear();
		}

		private static void ServerHandleSandboxDisabled(NetworkMessage netMsg)
		{
			SandboxDisabledMessage sandboxDisabledMessage = netMsg.ReadMessage<SandboxDisabledMessage>();
			if (moddedClients.TryGetValue(netMsg.conn, out var value))
			{
				value.sandboxDisabled.Add(sandboxDisabledMessage.catalogIndex);
			}
		}

		private static void ClientHandleImpactRejected(NetworkMessage netMsg)
		{
			PredictedProjectile.OnImpactRejected(netMsg.ReadMessage<ImpactRejectedMessage>().predictionId);
		}
	}
	internal sealed class ImpactReportMessage : MessageBase
	{
		public ushort predictionId;

		public uint shot;

		public float age;

		public bool hitHurtBox;

		public HurtBoxReference hurtBox;

		public Vector3 center;

		public Vector3 point;

		public Vector3 normal;

		public Quaternion rotation;

		public Vector3 velocity;

		public override void Serialize(NetworkWriter writer)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			writer.WritePackedUInt32((uint)predictionId);
			writer.WritePackedUInt32(shot);
			writer.Write(age);
			writer.Write(hitHurtBox);
			if (hitHurtBox)
			{
				((HurtBoxReference)(ref hurtBox)).Write(writer);
			}
			writer.Write(center);
			writer.Write(point);
			writer.Write(normal);
			writer.Write(rotation);
			writer.Write(velocity);
		}

		public override void Deserialize(NetworkReader reader)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			predictionId = (ushort)reader.ReadPackedUInt32();
			shot = reader.ReadPackedUInt32();
			age = reader.ReadSingle();
			hitHurtBox = reader.ReadBoolean();
			hurtBox = default(HurtBoxReference);
			if (hitHurtBox)
			{
				((HurtBoxReference)(ref hurtBox)).Read(reader);
			}
			center = reader.ReadVector3();
			point = reader.ReadVector3();
			normal = reader.ReadVector3();
			rotation = reader.ReadQuaternion();
			velocity = reader.ReadVector3();
		}
	}
	internal static class PhysicsUtil
	{
		private static readonly int[] maskCache = new int[32];

		private static readonly bool[] maskCached = new bool[32];

		private static readonly RaycastHit[] hitBuffer = (RaycastHit[])(object)new RaycastHit[32];

		public static int CollisionMask(int layer)
		{
			if (layer < 0 || layer > 31)
			{
				return -5;
			}
			if (!maskCached[layer])
			{
				int num = 0;
				for (int i = 0; i < 32; i++)
				{
					if (!Physics.GetIgnoreLayerCollision(layer, i))
					{
						num |= 1 << i;
					}
				}
				maskCache[layer] = num;
				maskCached[layer] = true;
			}
			return maskCache[layer];
		}

		public static float EstimateRadius(GameObject obj)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f;
			Collider[] components = obj.GetComponents<Collider>();
			foreach (Collider val in components)
			{
				if (!Object.op_Implicit((Object)(object)val) || val.isTrigger)
				{
					continue;
				}
				Vector3 lossyScale = ((Component)val).transform.lossyScale;
				float num2 = Mathf.Max(new float[3]
				{
					Mathf.Abs(lossyScale.x),
					Mathf.Abs(lossyScale.y),
					Mathf.Abs(lossyScale.z)
				});
				SphereCollider val2 = (SphereCollider)(object)((val is SphereCollider) ? val : null);
				if (val2 == null)
				{
					CapsuleCollider val3 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null);
					if (val3 == null)
					{
						BoxCollider val4 = (BoxCollider)(object)((val is BoxCollider) ? val : null);
						if (val4 != null)
						{
							Vector3 val5 = Vector3.Scale(val4.size, lossyScale);
							num = Mathf.Max(num, 0.5f * Mathf.Min(new float[3]
							{
								Mathf.Abs(val5.x),
								Mathf.Abs(val5.y),
								Mathf.Abs(val5.z)
							}));
						}
						else
						{
							num = Mathf.Max(num, 0.1f);
						}
					}
					else
					{
						num = Mathf.Max(num, val3.radius * num2);
					}
				}
				else
				{
					num = Mathf.Max(num, val2.radius * num2);
				}
			}
			return Mathf.Clamp(num, 0.02f, 3f);
		}

		public static bool Sweep(Vector3 origin, float radius, Vector3 direction, float distance, int mask, GameObject self, GameObject owner, out float hitDistance)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			hitDistance = distance;
			int num = Physics.SphereCastNonAlloc(origin, radius, direction, hitBuffer, distance, mask, (QueryTriggerInteraction)1);
			bool result = false;
			for (int i = 0; i < num; i++)
			{
				RaycastHit val = hitBuffer[i];
				Collider collider = ((RaycastHit)(ref val)).collider;
				if (Object.op_Implicit((Object)(object)collider) && !(((RaycastHit)(ref val)).distance <= 0f) && !((Component)collider).transform.IsChildOf(self.transform) && (!Object.op_Implicit((Object)(object)owner) || !IsPartOf(collider, owner)) && ((RaycastHit)(ref val)).distance < hitDistance)
				{
					hitDistance = ((RaycastHit)(ref val)).distance;
					result = true;
				}
			}
			return result;
		}

		private static bool IsPartOf(Collider collider, GameObject body)
		{
			HurtBox val = default(HurtBox);
			if (((Component)collider).TryGetComponent<HurtBox>(ref val) && Object.op_Implicit((Object)(object)val.healthComponent))
			{
				return (Object)(object)((Component)val.healthComponent).gameObject == (Object)(object)body;
			}
			return ((Component)collider).transform.IsChildOf(body.transform);
		}
	}
	[BepInPlugin("daz.ClientPredictedProjectiles", "ClientPredictedProjectiles", "3.0.3")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "daz.ClientPredictedProjectiles";

		public const string PluginName = "ClientPredictedProjectiles";

		public const string PluginVersion = "3.0.3";

		internal static ManualLogSource Log;

		internal static Harmony Harmony;

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			Harmony = new Harmony("daz.ClientPredictedProjectiles");
			Harmony.PatchAll(typeof(Plugin).Assembly);
			NetProtocol.Init();
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(ProjectileProfiles.BuildAll));
			RoR2Application.onFixedUpdate += OnFixedUpdate;
			Log.LogInfo((object)string.Format("{0} {1} loaded (protocol v{2})", "ClientPredictedProjectiles", "3.0.3", 5));
		}

		private static void OnFixedUpdate()
		{
			Sandbox.FlushUnmarks();
			PredictedProjectile.Tick();
			NetProtocol.Tick();
			ProjectilePatches.FlushDeferredFires();
		}

		private void OnDestroy()
		{
			Harmony harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class PluginConfig
	{
		public static ConfigEntry<bool> ClientImpacts;

		public static ConfigEntry<bool> ServerFastForward;

		public static ConfigEntry<float> MaxFastForward;

		public static ConfigEntry<float> ReportBuffer;

		public static ConfigEntry<float> ImpactTolerance;

		public static ConfigEntry<bool> PredictAll;

		public static ConfigEntry<bool> SimulateLocally;

		public static ConfigEntry<bool> PredictedGhosts;

		public static ConfigEntry<float> GhostBlendTime;

		public static ConfigEntry<bool> ExtrapolateOthers;

		public static ConfigEntry<float> MaxExtrapolation;

		public static ConfigEntry<string> ExcludedProjectiles;

		public static ConfigEntry<bool> LogProjectileReport;

		public static ConfigEntry<bool> VerboseLogging;

		public static HashSet<string> ExcludedProjectileSet = new HashSet<string>();

		public static void Bind(ConfigFile cfg)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Expected O, but got Unknown
			ClientImpacts = cfg.Bind<bool>("Host", "Client Owned Hit Detection", true, "HOST SETTING. The shooter's own copy of a projectile decides everything it hits: collisions, explosion radius hits and piercing/overlap hits, against enemies where the shooter saw them. The host applies those hits (damage, procs, debuffs) after sanity checks.");
			ReportBuffer = cfg.Bind<float>("Host", "Client Report Buffer Seconds", 0.06f, new ConfigDescription("HOST SETTING. The host replays a client's projectile this much later than it arrives, so the client's hit reports are always there in time even with some network jitter. Raise it if the log shows late reports; it only delays what *other* players see of that projectile. 0 turns client hit detection off (the host then decides all hits).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.3f), Array.Empty<object>()));
			ServerFastForward = cfg.Bind<bool>("Host", "Fast Forward Client Projectiles", true, "HOST SETTING. For client projectiles the client does NOT simulate (vehicles, or when local simulation is off/failed): advance them by the client's one-way latency. Vanilla computes this value but never uses it.");
			MaxFastForward = cfg.Bind<float>("Host", "Max Fast Forward Seconds", 0.2f, new ConfigDescription("HOST SETTING. Cap on how far (in seconds) such a projectile may be fast-forwarded.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
			ImpactTolerance = cfg.Bind<float>("Host", "Hit Distance Tolerance", 6f, new ConfigDescription("HOST SETTING. Base distance (meters) a client-reported hit may differ from the host's view. Projectile speed, target speed and ping are added on top.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			PredictAll = cfg.Bind<bool>("Client", "Predict All Projectiles", true, "Show every projectile you fire instantly, including the ones vanilla never predicts. Requires the host to have the mod.");
			SimulateLocally = cfg.Bind<bool>("Client", "Simulate Projectiles Locally", true, "Run your projectiles' full behaviour (homing, sticking, bouncing, fuses, explosions) on your machine so they react instantly. Damage and anything that affects other players is never done locally; the host applies it from your impact reports. Requires the host to have the mod.");
			PredictedGhosts = cfg.Bind<bool>("Client", "Predicted Projectile Visuals", true, "Keep showing your projectiles on your local path instead of snapping back to the delayed host copy. Blends to the host's path if they really disagree.");
			GhostBlendTime = cfg.Bind<float>("Client", "Correction Blend Seconds", 0.15f, new ConfigDescription("How long to blend a projectile's visual from your local path to the host's path when they disagree.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			ExtrapolateOthers = cfg.Bind<bool>("Client", "Show Other Projectiles Where The Host Has Them", true, "Push the visuals of projectiles you didn't fire (enemies, other players) forward by your latency, so what you dodge is where the host actually has it.");
			MaxExtrapolation = cfg.Bind<float>("Client", "Max Extrapolation Seconds", 0.2f, new ConfigDescription("Cap on how far ahead other projectiles' visuals are pushed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
			ExcludedProjectiles = cfg.Bind<string>("Advanced", "Excluded Projectiles", "", "Comma separated projectile prefab names to leave completely vanilla. Use 'Log Projectile Report' to see the names.");
			LogProjectileReport = cfg.Bind<bool>("Advanced", "Log Projectile Report", false, "Log every projectile and how it is handled when the game finishes loading.");
			VerboseLogging = cfg.Bind<bool>("Advanced", "Verbose Logging", false, "Log every client impact report and how the host resolved it, and every sandbox block. Noisy; for debugging.");
			ExcludedProjectileSet = new HashSet<string>(from s in (ExcludedProjectiles.Value ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim() into s
				where s.Length > 0
				select s, StringComparer.Ordinal);
		}
	}
	internal sealed class PredictedGhostDriver : MonoBehaviour
	{
		private enum Mode
		{
			Predicted,
			Blending,
			Hidden
		}

		private const int DivergedFramesToCorrect = 5;

		private const float AuthoritySettleTime = 0.1f;

		private ProjectileGhostController ghost;

		private PredictedProjectile predicted;

		private Mode mode;

		private bool simulatedLocally;

		private float authorityFirstSeen = -1f;

		private int divergedFrames;

		private float blendStart;

		private Vector3 blendFromPosition;

		private Quaternion blendFromRotation;

		private Vector3 hiddenPosition;

		private Quaternion hiddenRotation;

		private readonly List<Renderer> hiddenRenderers = new List<Renderer>();

		private readonly List<Light> hiddenLights = new List<Light>();

		private readonly List<TrailRenderer> stoppedTrails = new List<TrailRenderer>();

		private readonly List<ParticleSystem> stoppedParticles = new List<ParticleSystem>();

		public static PredictedGhostDriver Attach(ProjectileGhostController ghost, PredictedProjectile predicted)
		{
			PredictedGhostDriver predictedGhostDriver = default(PredictedGhostDriver);
			if (((Component)ghost).TryGetComponent<PredictedGhostDriver>(ref predictedGhostDriver))
			{
				predictedGhostDriver.Restore(replayParticles: false);
				Object.DestroyImmediate((Object)(object)predictedGhostDriver);
			}
			PredictedGhostDriver predictedGhostDriver2 = ((Component)ghost).gameObject.AddComponent<PredictedGhostDriver>();
			predictedGhostDriver2.ghost = ghost;
			predictedGhostDriver2.predicted = predicted;
			predictedGhostDriver2.mode = Mode.Predicted;
			predictedGhostDriver2.simulatedLocally = Sandbox.IsSandboxed(((Component)predicted).gameObject) && NetProtocol.HostAllowsClientImpacts;
			return predictedGhostDriver2;
		}

		public bool Drive()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)ghost))
			{
				return false;
			}
			Transform authorityTransform = ghost.authorityTransform;
			Transform val = (Object.op_Implicit((Object)(object)predicted) ? ((Component)predicted).transform : null);
			if (!Object.op_Implicit((Object)(object)authorityTransform) && !Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			switch (mode)
			{
			case Mode.Hidden:
				((Component)this).transform.SetPositionAndRotation(hiddenPosition, hiddenRotation);
				return true;
			case Mode.Predicted:
				if (!Object.op_Implicit((Object)(object)val))
				{
					StartBlend();
					break;
				}
				if (Object.op_Implicit((Object)(object)authorityTransform))
				{
					CheckDivergence(authorityTransform);
				}
				if (mode != Mode.Predicted)
				{
					break;
				}
				Access.GhostCopyTransform(ghost, val);
				return true;
			}
			if (!Object.op_Implicit((Object)(object)authorityTransform))
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					Access.GhostCopyTransform(ghost, val);
					return true;
				}
				return false;
			}
			Access.GhostCopyTransform(ghost, authorityTransform);
			float value = PluginConfig.GhostBlendTime.Value;
			float num = ((value > 0f) ? ((Time.fixedTime - blendStart) / value) : 1f);
			if (num >= 1f)
			{
				ghost.predictionTransform = null;
				Object.Destroy((Object)(object)this);
				return true;
			}
			float num2 = num * num * (3f - 2f * num);
			((Component)this).transform.SetPositionAndRotation(Vector3.Lerp(blendFromPosition, ((Component)this).transform.position, num2), Quaternion.Slerp(blendFromRotation, ((Component)this).transform.rotation, num2));
			return true;
		}

		private void CheckDivergence(Transform authority)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (authorityFirstSeen < 0f)
			{
				authorityFirstSeen = Time.fixedTime;
			}
			if (Time.fixedTime - authorityFirstSeen < 0.1f || !predicted.TrySamplePosition(Time.fixedTime - EstimateAuthorityLag(authority), out var position))
			{
				return;
			}
			float num = 1.5f + predicted.Speed * 0.06f;
			Vector3 val = authority.position - position;
			if (((Vector3)(ref val)).sqrMagnitude > num * num)
			{
				if (++divergedFrames >= 5)
				{
					StartBlend();
				}
			}
			else
			{
				divergedFrames = 0;
			}
		}

		private float EstimateAuthorityLag(Transform authority)
		{
			float num = 1f / 30f;
			ProjectileNetworkTransform val = default(ProjectileNetworkTransform);
			if (((Component)authority).TryGetComponent<ProjectileNetworkTransform>(ref val))
			{
				if (val.checkForLocalPlayerAuthority)
				{
					return 0f;
				}
				num = val.positionTransmitInterval * val.interpolationFactor;
			}
			NetworkManagerSystem singleton = NetworkManagerSystem.singleton;
			float num2 = (Object.op_Implicit((Object)(object)singleton) ? singleton.filteredClientRttFixed : 0f);
			if (simulatedLocally)
			{
				return num2 + NetProtocol.HostReportBuffer + num;
			}
			float num3 = (NetProtocol.HostFastForwards ? Mathf.Min(num2 * 0.5f, NetProtocol.HostMaxFastForward) : 0f);
			return Mathf.Max(0f, num2 - num3) + num;
		}

		private void StartBlend()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			mode = Mode.Blending;
			blendStart = Time.fixedTime;
			blendFromPosition = ((Component)this).transform.position;
			blendFromRotation = ((Component)this).transform.rotation;
		}

		public void OnPredictionEnded(Vector3 position, Quaternion rotation)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (mode != Mode.Hidden && Object.op_Implicit((Object)(object)ghost) && Object.op_Implicit((Object)(object)ghost.authorityTransform))
			{
				mode = Mode.Hidden;
				hiddenPosition = position;
				hiddenRotation = rotation;
				((Component)this).transform.SetPositionAndRotation(position, rotation);
				Hide();
			}
		}

		public void OnImpactRejected()
		{
			if (mode == Mode.Hidden)
			{
				Restore(replayParticles: true);
			}
			if (mode != Mode.Blending)
			{
				StartBlend();
			}
		}

		private void Hide()
		{
			TrailRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<TrailRenderer>();
			foreach (TrailRenderer val in componentsInChildren)
			{
				if (val.emitting)
				{
					val.emitting = false;
					stoppedTrails.Add(val);
				}
			}
			ParticleSystem[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<ParticleSystem>();
			foreach (ParticleSystem val2 in componentsInChildren2)
			{
				if (val2.isEmitting)
				{
					val2.Stop(false, (ParticleSystemStopBehavior)1);
					stoppedParticles.Add(val2);
				}
			}
			Renderer[] componentsInChildren3 = ((Component)this).GetComponentsInChildren<Renderer>();
			foreach (Renderer val3 in componentsInChildren3)
			{
				if (val3.enabled && !(val3 is TrailRenderer) && !(val3 is ParticleSystemRenderer))
				{
					val3.enabled = false;
					hiddenRenderers.Add(val3);
				}
			}
			Light[] componentsInChildren4 = ((Component)this).GetComponentsInChildren<Light>();
			foreach (Light val4 in componentsInChildren4)
			{
				if (((Behaviour)val4).enabled)
				{
					((Behaviour)val4).enabled = false;
					hiddenLights.Add(val4);
				}
			}
		}

		private void Restore(bool replayParticles)
		{
			foreach (Renderer hiddenRenderer in hiddenRenderers)
			{
				if (Object.op_Implicit((Object)(object)hiddenRenderer))
				{
					hiddenRenderer.enabled = true;
				}
			}
			foreach (Light hiddenLight in hiddenLights)
			{
				if (Object.op_Implicit((Object)(object)hiddenLight))
				{
					((Behaviour)hiddenLight).enabled = true;
				}
			}
			foreach (TrailRenderer stoppedTrail in stoppedTrails)
			{
				if (Object.op_Implicit((Object)(object)stoppedTrail))
				{
					stoppedTrail.emitting = true;
				}
			}
			if (replayParticles)
			{
				foreach (ParticleSystem stoppedParticle in stoppedParticles)
				{
					if (Object.op_Implicit((Object)(object)stoppedParticle))
					{
						stoppedParticle.Play(false);
					}
				}
			}
			hiddenRenderers.Clear();
			hiddenLights.Clear();
			stoppedTrails.Clear();
			stoppedParticles.Clear();
		}

		private void OnDisable()
		{
			Restore(replayParticles: false);
			Object.Destroy((Object)(object)this);
		}
	}
	internal sealed class PredictedProjectile : MonoBehaviour
	{
		private sealed class Record
		{
			public ushort predictionId;

			public int catalogIndex;

			public GameObject owner;

			public PredictedProjectile live;

			public ProjectileController hostCopy;

			public bool vanillaHandoff;

			public float diedAt = -1f;
		}

		private const int HistorySize = 64;

		private const float UnmatchedRecordTimeout = 10f;

		private static readonly Dictionary<ushort, Record> records = new Dictionary<ushort, Record>();

		private static readonly List<ushort> expired = new List<ushort>();

		private Rigidbody rb;

		private ushort predictionId;

		private PredictedGhostDriver ghostDriver;

		private float age;

		private float maxLifetime;

		private readonly float[] historyTime = new float[64];

		private readonly Vector3[] historyPosition = (Vector3[])(object)new Vector3[64];

		private int historyCount;

		private int historyHead;

		public ProjectileController Controller { get; private set; }

		public float Speed { get; private set; }

		public static void Attach(ProjectileController controller)
		{
			ushort num = controller.predictionId;
			if (num != 0)
			{
				PredictedProjectile predictedProjectile = ((Component)controller).gameObject.AddComponent<PredictedProjectile>();
				predictedProjectile.Controller = controller;
				predictedProjectile.rb = ((Component)controller).GetComponent<Rigidbody>();
				predictedProjectile.predictionId = num;
				predictedProjectile.maxLifetime = EstimateLifetime(controller);
				predictedProjectile.RecordPosition();
				records[num] = new Record
				{
					predictionId = num,
					catalogIndex = controller.catalogIndex,
					owner = controller.owner,
					live = predictedProjectile
				};
				if (PluginConfig.PredictedGhosts.Value && Object.op_Implicit((Object)(object)controller.ghost))
				{
					predictedProjectile.ghostDriver = PredictedGhostDriver.Attach(controller.ghost, predictedProjectile);
				}
				if (Sandbox.IsSandboxed(((Component)controller).gameObject) && !Sandbox.IsPrefabDisabled(controller.catalogIndex))
				{
					NetProtocol.SendSimulating(num, Sandbox.GetShot(((Component)controller).gameObject));
				}
			}
		}

		private void OnDestroy()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (records.TryGetValue(predictionId, out var value) && (Object)(object)value.live == (Object)(object)this)
			{
				value.live = null;
				value.diedAt = Time.unscaledTime;
			}
			if (Object.op_Implicit((Object)(object)ghostDriver))
			{
				ghostDriver.OnPredictionEnded(((Component)this).transform.position, ((Component)this).transform.rotation);
			}
		}

		private void FixedUpdate()
		{
			RecordPosition();
			age += Time.fixedDeltaTime;
			if (age > maxLifetime * 2f + 5f)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		private static float EstimateLifetime(ProjectileController controller)
		{
			float num = 0f;
			ProjectileSimple val = default(ProjectileSimple);
			if (((Component)controller).TryGetComponent<ProjectileSimple>(ref val))
			{
				num = Mathf.Max(num, val.lifetime);
			}
			ProjectileImpactExplosion[] components = ((Component)controller).GetComponents<ProjectileImpactExplosion>();
			foreach (ProjectileImpactExplosion val2 in components)
			{
				num = Mathf.Max(num, val2.lifetime + (val2.timerAfterImpact ? val2.lifetimeAfterImpact : 0f));
			}
			if (!(num > 0f))
			{
				return 15f;
			}
			return num;
		}

		private void RecordPosition()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			historyTime[historyHead] = Time.fixedTime;
			historyPosition[historyHead] = ((Component)this).transform.position;
			historyHead = (historyHead + 1) % 64;
			historyCount = Mathf.Min(historyCount + 1, 64);
			if (Object.op_Implicit((Object)(object)rb))
			{
				Vector3 velocity = rb.velocity;
				Speed = ((Vector3)(ref velocity)).magnitude;
			}
		}

		public bool TrySamplePosition(float time, out Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			if (historyCount == 0)
			{
				return false;
			}
			int num = (historyHead - 1 + 64) % 64;
			int num2 = (historyHead - historyCount + 64) % 64;
			if (time >= historyTime[num])
			{
				position = historyPosition[num];
				return true;
			}
			if (time <= historyTime[num2])
			{
				return false;
			}
			for (int i = 0; i < historyCount - 1; i++)
			{
				int num3 = (num - i - 1 + 64) % 64;
				int num4 = (num3 + 1) % 64;
				if (historyTime[num3] <= time && time <= historyTime[num4])
				{
					float num5 = historyTime[num4] - historyTime[num3];
					float num6 = ((num5 > 0f) ? ((time - historyTime[num3]) / num5) : 1f);
					position = Vector3.Lerp(historyPosition[num3], historyPosition[num4], num6);
					return true;
				}
			}
			return false;
		}

		public static void ReportImpact(ProjectileController controller, in ProjectileImpactInfo info)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			if (NetProtocol.HostAllowsClientImpacts && controller.predictionId != 0 && Object.op_Implicit((Object)(object)info.collider))
			{
				Transform transform = ((Component)controller).transform;
				Rigidbody component = ((Component)controller).GetComponent<Rigidbody>();
				ImpactReportMessage impactReportMessage = new ImpactReportMessage
				{
					predictionId = controller.predictionId,
					age = (((Object)(object)Sandbox.Current.controller == (Object)(object)controller) ? Sandbox.Current.Age : 0f),
					shot = Sandbox.GetShot(((Component)controller).gameObject),
					normal = info.estimatedImpactNormal,
					rotation = transform.rotation,
					velocity = (Object.op_Implicit((Object)(object)component) ? component.velocity : Vector3.zero)
				};
				HurtBox val = default(HurtBox);
				if (((Component)info.collider).TryGetComponent<HurtBox>(ref val))
				{
					impactReportMessage.hitHurtBox = true;
					impactReportMessage.hurtBox = HurtBoxReference.FromHurtBox(val);
					impactReportMessage.center = ((Component)val).transform.InverseTransformPoint(transform.position);
					impactReportMessage.point = ((Component)val).transform.InverseTransformPoint(info.estimatedPointOfImpact);
				}
				else
				{
					impactReportMessage.center = transform.position;
					impactReportMessage.point = info.estimatedPointOfImpact;
				}
				NetProtocol.SendImpactReport(impactReportMessage);
			}
		}

		public static void OnImpactRejected(ushort predictionId)
		{
			if (records.TryGetValue(predictionId, out var value))
			{
				if (PluginConfig.VerboseLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"Host rejected an impact of prediction {predictionId}; following host copy");
				}
				PredictedGhostDriver predictedGhostDriver = null;
				if (Objec