Decompiled source of TrollCompanion v0.2.10

plugins/TrollCompanion.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using TrollCompanion.Core;
using TrollCompanion.Gameplay;
using TrollCompanion.Runtime;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("TrollCompanion")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.10.0")]
[assembly: AssemblyInformationalVersion("0.2.10")]
[assembly: AssemblyProduct("TrollCompanion")]
[assembly: AssemblyTitle("TrollCompanion")]
[assembly: AssemblyVersion("0.2.10.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace TrollCompanion
{
	[BepInPlugin("de.steve.valheim.trollcompanion", "TrollCompanion", "0.2.10")]
	[BepInDependency("com.jotunn.jotunn", "2.30.2")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "de.steve.valheim.trollcompanion";

		public const string PluginName = "TrollCompanion";

		public const string PluginVersion = "0.2.10";

		private Harmony? harmony;

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			RuntimeBridge.Initialize(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config, (MonoBehaviour)(object)this);
			PrefabCatalog.Initialize();
			StaffControls.Initialize();
			CompanionPanel.Initialize();
			harmony = new Harmony("de.steve.valheim.trollcompanion");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"TrollCompanion loaded");
		}

		private void Update()
		{
			RuntimeBridge.Tick();
			CompanionPanel.Tick();
			StaffControls.Tick();
		}

		private void OnDestroy()
		{
			RuntimeBridge.Shutdown();
			CompanionPanel.Shutdown();
			RuntimeBridge.DisposeSettings();
			StaffControls.Shutdown();
			PrefabCatalog.Shutdown();
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
}
namespace TrollCompanion.Runtime
{
	internal sealed class ConfigDocument
	{
		internal const int MaximumBytes = 262144;

		internal readonly Dictionary<string, string> Values = new Dictionary<string, string>(StringComparer.Ordinal);

		internal static string Id(string section, string key)
		{
			return section + "\u001f" + key;
		}

		internal static ConfigDocument Parse(byte[] bytes)
		{
			if (bytes.Length > 262144)
			{
				throw new ArgumentException("Configuration is larger than 256 KiB.");
			}
			string s;
			if (bytes.Length >= 2 && bytes[0] == byte.MaxValue && bytes[1] == 254)
			{
				s = new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true).GetString(bytes, 2, bytes.Length - 2);
			}
			else if (bytes.Length >= 2 && bytes[0] == 254 && bytes[1] == byte.MaxValue)
			{
				s = new UnicodeEncoding(bigEndian: true, byteOrderMark: true, throwOnInvalidBytes: true).GetString(bytes, 2, bytes.Length - 2);
			}
			else
			{
				int num = ((bytes.Length >= 3 && bytes[0] == 239 && bytes[1] == 187 && bytes[2] == 191) ? 3 : 0);
				s = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(bytes, num, bytes.Length - num);
			}
			ConfigDocument configDocument = new ConfigDocument();
			string text = "";
			using StringReader stringReader = new StringReader(s);
			int num2 = 0;
			string text2;
			while ((text2 = stringReader.ReadLine()) != null)
			{
				num2++;
				text2 = text2.Trim();
				if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal) || text2.StartsWith(";", StringComparison.Ordinal))
				{
					continue;
				}
				if (text2.IndexOf('\0') >= 0 || text2.IndexOf('\u001f') >= 0)
				{
					throw new ArgumentException("Invalid character at configuration line " + num2 + ".");
				}
				if (text2[0] == '[')
				{
					if (text2.Length < 3 || text2[text2.Length - 1] != ']' || text2.IndexOf(']') != text2.Length - 1)
					{
						throw new ArgumentException("Invalid section at configuration line " + num2 + ".");
					}
					text = text2.Substring(1, text2.Length - 2).Trim();
					if (text.Length == 0)
					{
						throw new ArgumentException("Empty configuration section.");
					}
					continue;
				}
				int num3 = text2.IndexOf('=');
				if (text.Length == 0 || num3 <= 0)
				{
					throw new ArgumentException("Expected [section] and key = value at line " + num2 + ".");
				}
				string text3 = text2.Substring(0, num3).Trim();
				if (text3.Length == 0 || !configDocument.Values.TryAdd(Id(text, text3), text2.Substring(num3 + 1).Trim()))
				{
					throw new ArgumentException("Empty or duplicate configuration key at line " + num2 + ".");
				}
			}
			return configDocument;
		}

		internal ConfigDocument Clone()
		{
			ConfigDocument configDocument = new ConfigDocument();
			foreach (KeyValuePair<string, string> value in Values)
			{
				configDocument.Values.Add(value.Key, value.Value);
			}
			return configDocument;
		}

		internal byte[] Render(IReadOnlyList<ConfigSetting> definitions)
		{
			StringBuilder stringBuilder = new StringBuilder("## TrollCompanion configuration\n## Gameplay settings are controlled by the server. Input and display options remain local.\n\n");
			Dictionary<string, string> dictionary = definitions.ToDictionary<ConfigSetting, string, string>((ConfigSetting definition) => definition.Id, (ConfigSetting definition) => definition.Description, StringComparer.Ordinal);
			dictionary[ConfigSchema.MigrationId] = "Internal migration marker. Preserve this value to avoid repeating the 0.2.10 balance migration.";
			string text = "";
			foreach (KeyValuePair<string, string> item in Values.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> pair) => pair.Key, StringComparer.Ordinal))
			{
				string[] array = item.Key.Split('\u001f');
				if (array[0] != text)
				{
					text = array[0];
					stringBuilder.Append('[').Append(text).Append("]\n\n");
				}
				if (dictionary.TryGetValue(item.Key, out var value))
				{
					stringBuilder.Append("## ").Append(value).Append('\n');
				}
				stringBuilder.Append(array[1]).Append(" = ").Append(item.Value)
					.Append("\n\n");
			}
			return new UTF8Encoding(encoderShouldEmitUTF8Identifier: false).GetBytes(stringBuilder.ToString());
		}

		internal static object ParseValue(string value, Type type)
		{
			if (type == typeof(string))
			{
				return value;
			}
			if (type == typeof(bool) && bool.TryParse(value, out var result))
			{
				return result;
			}
			if (type == typeof(int) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return result2;
			}
			if (type == typeof(float) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && !float.IsNaN(result3) && !float.IsInfinity(result3))
			{
				return result3;
			}
			if (type == typeof(double) && double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) && !double.IsNaN(result4) && !double.IsInfinity(result4))
			{
				return result4;
			}
			throw new ArgumentException("Expected a finite " + type.Name + " value, got '" + value + "'.");
		}

		internal static string Format(object value)
		{
			if (value is float num)
			{
				return num.ToString("R", CultureInfo.InvariantCulture);
			}
			if (value is double num2)
			{
				return num2.ToString("R", CultureInfo.InvariantCulture);
			}
			if (value is bool)
			{
				if (!(bool)value)
				{
					return "false";
				}
				return "true";
			}
			return Convert.ToString(value, CultureInfo.InvariantCulture) ?? "";
		}
	}
	internal sealed class ConfigFileMonitor : IDisposable
	{
		private readonly string path;

		private string processed = "";

		private string pending = "";

		private byte[]? pendingBytes;

		private double changedAt;

		private double nextRead;

		private bool disposed;

		internal string LastError { get; private set; } = "";

		internal ConfigFileMonitor(string path)
		{
			this.path = path;
		}

		internal void AcceptBytes(byte[] bytes)
		{
			processed = Fingerprint(bytes);
			pending = "";
			pendingBytes = null;
		}

		internal bool Tick(double now, out byte[] bytes)
		{
			bytes = Array.Empty<byte>();
			if (disposed || now < nextRead)
			{
				return false;
			}
			nextRead = now + 0.25;
			byte[] bytes2;
			try
			{
				bytes2 = Read(path);
				LastError = "";
			}
			catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException)
			{
				LastError = ex.Message;
				pending = "";
				pendingBytes = null;
				return false;
			}
			string text = Fingerprint(bytes2);
			if (text == processed)
			{
				pending = "";
				pendingBytes = null;
				return false;
			}
			if (text != pending)
			{
				pending = text;
				pendingBytes = bytes2;
				changedAt = now;
				return false;
			}
			if (now - changedAt < 1.0 || pendingBytes == null)
			{
				return false;
			}
			bytes = pendingBytes;
			processed = pending;
			pending = "";
			pendingBytes = null;
			return true;
		}

		internal static byte[] Read(string path)
		{
			using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
			if (fileStream.Length > 262144)
			{
				throw new ArgumentException("Configuration is larger than 256 KiB.");
			}
			using MemoryStream memoryStream = new MemoryStream();
			byte[] array = new byte[4096];
			int num;
			while ((num = fileStream.Read(array, 0, array.Length)) > 0)
			{
				if (memoryStream.Length + num > 262144)
				{
					throw new ArgumentException("Configuration is larger than 256 KiB.");
				}
				memoryStream.Write(array, 0, num);
			}
			return memoryStream.ToArray();
		}

		internal static string Fingerprint(byte[] bytes)
		{
			using SHA256 sHA = SHA256.Create();
			return Convert.ToBase64String(sHA.ComputeHash(bytes));
		}

		internal static string ReplaceWithBackup(string path, byte[] expected, byte[] replacement, bool existed)
		{
			string directoryName = Path.GetDirectoryName(path);
			if (!string.IsNullOrEmpty(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			string sourceFileName = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
			string text = path + ".pre-0.2.10." + DateTime.UtcNow.ToString("yyyyMMdd-HHmmssfff", CultureInfo.InvariantCulture) + "." + Guid.NewGuid().ToString("N") + ".bak";
			try
			{
				using (FileStream fileStream = new FileStream(sourceFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None))
				{
					fileStream.Write(replacement, 0, replacement.Length);
					fileStream.Flush(flushToDisk: true);
				}
				if (!existed)
				{
					if (expected.Length != 0)
					{
						throw new IOException("The original configuration disappeared before migration.");
					}
					File.Move(sourceFileName, path);
					return "";
				}
				using (FileStream fileStream2 = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))
				{
					using MemoryStream memoryStream = new MemoryStream();
					fileStream2.CopyTo(memoryStream);
					if (!expected.SequenceEqual(memoryStream.ToArray()))
					{
						throw new IOException("Configuration changed during migration; retrying after the next edit.");
					}
					File.Replace(sourceFileName, path, text);
				}
				return text;
			}
			finally
			{
				if (File.Exists(sourceFileName))
				{
					File.Delete(sourceFileName);
				}
			}
		}

		public void Dispose()
		{
			disposed = true;
			pendingBytes = null;
		}
	}
	internal sealed class ConfigSetting
	{
		internal readonly string Section;

		internal readonly string Key;

		internal readonly string Id;

		internal readonly string LegacyId;

		internal readonly string Description;

		internal readonly object Default;

		internal readonly FieldInfo? Field;

		internal readonly bool Shortcut;

		internal ConfigSetting(string section, string key, string legacySection, string legacyKey, object value, string description, string? field = null, bool shortcut = false)
		{
			Section = section;
			Key = key;
			Id = ConfigDocument.Id(section, key);
			LegacyId = ((legacyKey.Length == 0) ? "" : ConfigDocument.Id(legacySection, legacyKey));
			Default = value;
			Description = description;
			Shortcut = shortcut;
			Field = ((field == null) ? null : (typeof(RuntimeRules).GetField(field) ?? throw new ArgumentException("Unknown rules field " + field)));
		}
	}
	internal sealed class ConfigCandidate
	{
		internal RuntimeRules Rules = new RuntimeRules();

		internal readonly Dictionary<string, object> Values = new Dictionary<string, object>(StringComparer.Ordinal);

		internal byte[]? MigrationBytes;
	}
	internal static class ConfigSchema
	{
		internal static readonly string MigrationId = ConfigDocument.Id("Configuration", "MigrationVersion");

		internal const string MigrationVersion = "1";

		internal static readonly IReadOnlyList<ConfigSetting> Definitions = new ConfigSetting[37]
		{
			new ConfigSetting("Companions", "CapacityByBloodMagic", "Begleiter", "LimitNachBlutmagie", "0:2,50:3,100:4", "Increasing Blood Magic:capacity pairs. Locked slots retain their identity and progress.", "CapacityCurve"),
			new ConfigSetting("Companions", "TrollVariant", "Begleiter", "Trolltyp", "Ashlands", "Ashlands or Forest. Changes take effect on the next summon; unverified variants remain unavailable.", "Variant"),
			new ConfigSetting("Progression", "FirstStarKills", "Fortschritt", "KillsErsterStern", 25, "Eligible kills required for the first star.", "FirstStarKills"),
			new ConfigSetting("Progression", "StarGrowth", "Fortschritt", "Wachstumsfaktor", 2.0, "Growth factor for each additional star requirement: 25, another 50, another 100 by default.", "StarGrowth"),
			new ConfigSetting("Progression", "MaxStars", "Fortschritt", "MaxSterne", 2, "Maximum stars, from 0 to 20. Lowering the limit preserves accumulated kills.", "MaxStars"),
			new ConfigSetting("Progression", "BloodMagicXpFactor", "Fortschritt", "BlutmagieXpFaktor", 0.25f, "Native Blood Magic skill XP factor per eligible kill. Set to zero to disable.", "BloodMagicXp"),
			new ConfigSetting("Progression", "ShareKillProgress", "", "", true, "Share eligible companion kills with the same owner's nearby active companions. Blood Magic XP is awarded only once per kill.", "ShareKillProgress"),
			new ConfigSetting("Progression", "SharedKillRadius", "", "", 25f, "Maximum distance in metres from the killed enemy for another active companion to receive shared progress.", "SharedKillRadius"),
			new ConfigSetting("Death", "CooldownAtBloodMagic0", "Tod", "WartezeitBeiBlutmagie0", 30f, "Seconds before resummoning after death at Blood Magic 0.", "DeathCooldownStart"),
			new ConfigSetting("Death", "CooldownAtBloodMagic100", "Tod", "WartezeitBeiBlutmagie100", 5f, "Seconds at Blood Magic 100, interpolated linearly. Existing death deadlines do not change.", "DeathCooldownMinimum"),
			new ConfigSetting("Death", "DropLoot", "Tod", "Todesbeute", false, "Allow loot on actual companion death. Recall never creates loot.", "DeathDrops"),
			new ConfigSetting("Combat", "BaseHealthMultiplier", "Kampf", "GrundLebenMultiplikator", 2f, "Multiplier of the shared 600 HP forest-troll baseline. Default 2 gives 1200/2400/3600 maximum HP at 0/1/2 stars. Live configuration changes preserve the health percentage; star gains preserve absolute health.", "BaseHealthMultiplier"),
			new ConfigSetting("Combat", "BaseDamageMultiplier", "Kampf", "GrundSchadenMultiplikator", 2f, "Multiplier of the shared club damage profile. Default 2 with DamagePerStar=0.5 gives damage factors 2/3/4 at 0/1/2 stars.", "BaseDamageMultiplier"),
			new ConfigSetting("Combat", "ResistancePenetration", "Kampf", "ResistenzDurchdringung", 0.75f, "Fraction of normal-enemy damage resistance bypassed, from 0 to 1. Default 0.75 turns 50 percent damage into 87.5 percent. Excludes bosses, immunities, weak spots and tree/rock damage.", "ResistancePenetration"),
			new ConfigSetting("Combat", "BossDamageMultiplier", "Kampf", "SchadenGegenBosseMultiplikator", 0.5f, "Additional damage factor against creatures marked as bosses. Stars still scale damage; native boss resistances remain active. Zero disables companion damage to bosses.", "BossDamageMultiplier"),
			new ConfigSetting("Combat", "HealthPerStar", "Kampf", "LebenJeStern", 1f, "Additional base-health fraction per star. 1 means +100 percent.", "HealthPerStar"),
			new ConfigSetting("Combat", "DamagePerStar", "Kampf", "SchadenJeStern", 0.5f, "Additional base-damage fraction per star. 0.5 means +50 percent.", "DamagePerStar"),
			new ConfigSetting("Costs", "EitrMultiplier", "Kosten", "EitrMultiplikator", 1f, "Multiplier of native Trollstaff Eitr costs.", "EitrCostMultiplier"),
			new ConfigSetting("Costs", "HealthMultiplier", "Kosten", "GesundheitMultiplikator", 1f, "Multiplier of native Trollstaff health costs.", "HealthCostMultiplier"),
			new ConfigSetting("Costs", "RefundFailedSummons", "Kosten", "ErstattungBeiFehler", true, "Refund actually charged Eitr and health when the server confirms a technical summon failure.", "RefundFailedSummons"),
			new ConfigSetting("Commands", "FollowCatchUpDistance", "Befehle", "AufholenDistanz", 100f, "Catch up beyond this distance while following. Attack-move, focus and hold orders suspend catch-up.", "FollowDistance"),
			new ConfigSetting("Commands", "CommandRange", "Befehle", "Befehlsreichweite", 80f, "Maximum range of target orders and summons in metres.", "CommandRange"),
			new ConfigSetting("Commands", "ArrivalDistance", "Befehle", "Ankunftstoleranz", 4f, "Horizontal arrival tolerance; additional ground and height checks remain active.", "ArrivalDistance"),
			new ConfigSetting("Commands", "CombatRadius", "Befehle", "KampfRadius", 25f, "Maximum combat and pursuit radius around the current order's reference point.", "CombatRadius"),
			new ConfigSetting("Commands", "StuckSeconds", "Befehle", "StillstandSekunden", 15f, "Hold the current position after this many seconds without movement progress. Combat pauses the timer.", "StuckSeconds"),
			new ConfigSetting("Protection", "Players", "Schutz", "Spieler", true, "Prevent companion attacks from damaging players.", "ProtectPlayers"),
			new ConfigSetting("Protection", "TamedAnimals", "Schutz", "GezaehmteTiere", true, "Prevent companion attacks from damaging tamed animals.", "ProtectTames"),
			new ConfigSetting("Protection", "PlayerStructures", "Schutz", "Spielerbauten", true, "Prevent companion attacks from damaging player structures.", "ProtectStructures"),
			new ConfigSetting("Protection", "Environment", "Schutz", "Umgebung", true, "Prevent tree, rock and neutral-object damage from companion attacks.", "ProtectEnvironment"),
			new ConfigSetting("Diagnostics", "DiagnosticOnly", "Diagnose", "NurDiagnose", false, "Inspect prefabs while keeping companion summoning disabled.", "DiagnosticOnly"),
			new ConfigSetting("Diagnostics", "CombatLogging", "Diagnose", "Kampfprotokoll", false, "Local detailed damage and actual health-change logging on the simulating peer. Enable for testing; produces many log lines."),
			new ConfigSetting("Input", "Recall", "Eingabe", "Rueckruf", "Mouse2", "Recall all active companions while the Trollstaff is equipped.", null, shortcut: true),
			new ConfigSetting("Input", "Command", "Eingabe", "Zielbefehl", "J", "With the Trollstaff equipped: target ground for attack-move or an enemy for focus attack, then hold position.", null, shortcut: true),
			new ConfigSetting("Input", "Follow", "Eingabe", "Folgen", "H", "End the current order and follow the owner.", null, shortcut: true),
			new ConfigSetting("Input", "CompanionPanel", "", "", "F6", "Open or close the companion overview with individual recall controls.", null, shortcut: true),
			new ConfigSetting("Display", "TargetMarkers", "Anzeige", "Zielmarkierung", true, "Briefly mark confirmed target orders; local display option."),
			new ConfigSetting("Display", "TextMessages", "Anzeige", "Textmeldungen", true, "Show mod messages in the HUD. Diagnostic logging remains complete.")
		};

		internal static ConfigCandidate Parse(byte[] bytes, Action<string>? validateShortcut = null)
		{
			ConfigDocument configDocument = ConfigDocument.Parse(bytes);
			string value;
			bool flag = !configDocument.Values.TryGetValue(MigrationId, out value);
			if (!flag && value != "1")
			{
				throw new ArgumentException("Unsupported configuration migration version. Expected 1.");
			}
			ConfigCandidate configCandidate = new ConfigCandidate();
			ConfigDocument configDocument2 = configDocument.Clone();
			foreach (ConfigSetting definition in Definitions)
			{
				string value2;
				bool flag2 = configDocument.Values.TryGetValue(definition.Id, out value2);
				if (!flag2 && flag && definition.LegacyId.Length != 0)
				{
					flag2 = configDocument.Values.TryGetValue(definition.LegacyId, out value2);
				}
				object obj;
				try
				{
					obj = (flag2 ? ConfigDocument.ParseValue(value2, definition.Default.GetType()) : definition.Default);
					if (definition.Shortcut)
					{
						validateShortcut?.Invoke((string)obj);
					}
				}
				catch (Exception ex) when (ex is ArgumentException || ex is FormatException || ex is OverflowException)
				{
					throw new ArgumentException("[" + definition.Section + "] " + definition.Key + ": " + ex.Message, ex);
				}
				configCandidate.Values.Add(definition.Id, obj);
				definition.Field?.SetValue(configCandidate.Rules, obj);
				if (flag)
				{
					if (definition.LegacyId.Length != 0)
					{
						configDocument2.Values.Remove(definition.LegacyId);
					}
					configDocument2.Values[definition.Id] = ConfigDocument.Format(obj);
				}
			}
			configCandidate.Rules.Validate();
			if (flag)
			{
				configCandidate.Rules.BaseHealthMultiplier = (configCandidate.Rules.BaseDamageMultiplier = 2f);
				string[] array = new string[2] { "BaseHealthMultiplier", "BaseDamageMultiplier" };
				foreach (string key in array)
				{
					string key2 = ConfigDocument.Id("Combat", key);
					configCandidate.Values[key2] = 2f;
					configDocument2.Values[key2] = "2";
				}
				configDocument2.Values[MigrationId] = "1";
				configCandidate.Rules.Validate();
				configCandidate.MigrationBytes = configDocument2.Render(Definitions);
			}
			return configCandidate;
		}
	}
	public static class RuntimeBridge
	{
		private enum ActuationKind
		{
			Authorize = 1,
			Order,
			Progress,
			Teleport,
			Name
		}

		private sealed class Actuation
		{
			internal string Token = "";

			internal string CompanionId = "";

			internal string Name = "";

			internal string NameAuthor = "";

			internal ZDOID Instance;

			internal int Generation;

			internal int Stars;

			internal long Owner;

			internal long Revision;

			internal long Sequence;

			internal long Kills;

			internal long RulesRevision;

			internal float BaseHealth;

			internal float BaseDamage;

			internal float HealthPerStar;

			internal float DamagePerStar;

			internal float ResistancePenetration;

			internal float BossDamage;

			internal ActuationKind Kind;

			internal CompanionOrder Order;

			internal Vector3 Point;

			internal ZDOID Target;

			internal float Expires;

			internal float NextSend;
		}

		private enum Message
		{
			Hello = 1,
			Summon = 2,
			Recall = 3,
			Order = 4,
			Death = 5,
			Kill = 6,
			Health = 7,
			CatchUp = 8,
			Stuck = 9,
			Portal = 10,
			SpawnResult = 11,
			GroundResult = 12,
			ActuationAck = 13,
			RecallSnapshotResult = 14,
			Rename = 15,
			PanelQuery = 16,
			RecallOne = 17,
			Snapshot = 100,
			Notice = 101,
			SkillReward = 102,
			SpawnPermit = 103,
			GroundQuery = 104,
			Actuation = 105,
			RecallSnapshot = 106,
			CancelArrival = 107,
			SummonOutcome = 108,
			PanelSnapshot = 109,
			RulesSnapshot = 110
		}

		private sealed class Tombstone
		{
			internal long Owner;

			internal int Prefab;

			internal bool Tamed;

			internal bool Aggravated;

			internal Vector3 Position;

			internal float Expires;
		}

		private sealed class RecallLease
		{
			internal string Token = "";

			internal string Id = "";

			internal long Owner;

			internal long Peer;

			internal int Generation;

			internal ZDOID Instance;

			internal float Expires;

			internal float NextAttempt;
		}

		private sealed class HealthSnapshot
		{
			internal float Health;

			internal float MaxHealth;

			internal float BaseHealth;

			internal float HealthPerStar;

			internal int Stars;

			internal int Generation;

			internal long Revision;

			internal long SampleSequence;

			internal bool Valid
			{
				get
				{
					if (!Finite(Health) || Health < 0f || !Finite(MaxHealth) || MaxHealth <= 0f || !Finite(BaseHealth) || BaseHealth <= 0f || !Finite(HealthPerStar) || HealthPerStar < 0f || Stars < 0 || Stars > 100 || Revision < 0 || Generation <= 0 || SampleSequence < 0)
					{
						return false;
					}
					try
					{
						return MaxHealth == CompanionHealth.Maximum(BaseHealth, HealthPerStar, Stars);
					}
					catch (ArgumentException)
					{
						return false;
					}
				}
			}
		}

		private enum SpawnFailure
		{
			None,
			Ground,
			Prefab,
			Effect,
			Initialization,
			Cancelled,
			OwnerDied,
			Teleport,
			OutOfRange,
			SessionEnded
		}

		private sealed class SpawnLease
		{
			internal string Token = "";

			internal string Id = "";

			internal string Variant = "";

			internal string CastId = "";

			internal long Peer;

			internal long Owner;

			internal long Kills;

			internal long RulesRevision;

			internal RuntimeRules Rules = new RuntimeRules();

			internal int Generation;

			internal Vector3 Point;

			internal float Expires;

			internal float Health;

			internal ZDOID Instance = ZDOID.None;

			internal ZDOID Summoner = ZDOID.None;
		}

		private sealed class ClientSpawn
		{
			internal ZDOID Instance;

			internal float Expires;
		}

		private sealed class GroundLease
		{
			internal string Id = "";

			internal long Peer;

			internal long OrderSequence;

			internal int Generation;

			internal float Expires;
		}

		private sealed class GenerationLease
		{
			internal string Id = "";

			internal int Generation;

			internal Vector3 Position;

			internal float Expires;
		}

		private static readonly Dictionary<string, Actuation> pendingActuations = new Dictionary<string, Actuation>();

		private static readonly Dictionary<string, float> appliedActuationTokens = new Dictionary<string, float>();

		private static long actuationRevision;

		private static float nextActuationTokenCleanup;

		private const string AppliedOrderKey = "tc_applied_order_sequence";

		private const string AppliedProgressKey = "tc_applied_progress_revision";

		private const string AppliedTeleportKey = "tc_applied_teleport_revision";

		private const string AppliedNameKey = "tc_applied_name_revision";

		private static Settings settings = null;

		private static CustomRPC rpc = null;

		private static MonoBehaviour coroutineHost = null;

		private static RosterStore? store;

		private static ZNet? session;

		private static bool ready;

		private static bool configValid = true;

		private static bool pendingSummon;

		private static bool snapshotValid;

		private static bool clientCanSummon;

		private static string clientReason = "Synchronizing companion roster.";

		private static float nextTick;

		private static float nextHello;

		private static float snapshotAt;

		private static float nextHealthSave;

		private static string configError = "";

		private static readonly Dictionary<long, float> skills = new Dictionary<long, float>();

		private static readonly Dictionary<string, HealthSnapshot> healthUpdates = new Dictionary<string, HealthSnapshot>();

		private static readonly Dictionary<long, HashSet<string>> seenRequests = new Dictionary<long, HashSet<string>>();

		private static readonly HashSet<string> rewarded = new HashSet<string>();

		private static readonly Dictionary<ZDOID, Tombstone> tombstones = new Dictionary<ZDOID, Tombstone>();

		private static readonly List<ZDO> sweep = new List<ZDO>();

		private static readonly HashSet<ZDOID> retiring = new HashSet<ZDOID>();

		private static readonly string[] prefabNames = new string[2] { "TrollCompanion_Forest", "TrollCompanion_Ashlands" };

		private static int sweepPrefab;

		private static int sweepIndex;

		private static bool sweepFound;

		private static long commandSequence;

		private static readonly Dictionary<long, float> lastSpawn = new Dictionary<long, float>();

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

		private static long localActionSequence;

		private static readonly PeerIdentityBindings peerIdentities = new PeerIdentityBindings();

		private static readonly Dictionary<long, float> nextIdentityWarning = new Dictionary<long, float>();

		private static readonly HashSet<long> confirmedHello = new HashSet<long>();

		private static RuntimeRules localRules = new RuntimeRules();

		private static long rulesRevision = 1L;

		private static long receivedRulesRevision;

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

		private static string lastSettingsError = "";

		private static readonly Dictionary<long, Dictionary<string, long>> renameSequences = new Dictionary<long, Dictionary<string, long>>();

		private static string panelSession = "";

		private static long panelQuerySequence;

		private static long panelReceivedSequence;

		private static float panelNextQuery;

		private static CompanionPanelSnapshot? panelSnapshot;

		private static readonly Dictionary<string, float> panelRecalls = new Dictionary<string, float>();

		private static readonly Dictionary<long, float> panelRateLimits = new Dictionary<long, float>();

		private static readonly Dictionary<string, RecallLease> pendingRecalls = new Dictionary<string, RecallLease>();

		private static readonly Dictionary<long, HashSet<string>> summonCasts = new Dictionary<long, HashSet<string>>();

		private static readonly Dictionary<long, HashSet<string>> settledSummonCasts = new Dictionary<long, HashSet<string>>();

		private static bool shuttingDown;

		private static readonly Dictionary<string, SpawnLease> pendingSpawns = new Dictionary<string, SpawnLease>();

		private static readonly Dictionary<string, ClientSpawn> clientSpawns = new Dictionary<string, ClientSpawn>();

		private static readonly HashSet<string> clientPermits = new HashSet<string>();

		private static readonly HashSet<string> clientArrivals = new HashSet<string>();

		private static readonly Dictionary<string, GroundLease> groundQueries = new Dictionary<string, GroundLease>();

		private static readonly Dictionary<ZDOID, GenerationLease> recentGenerations = new Dictionary<ZDOID, GenerationLease>();

		private static readonly Dictionary<string, float> missingInstances = new Dictionary<string, float>();

		private static readonly List<ZDO> audit = new List<ZDO>();

		private static int auditPrefab;

		private static int auditIndex;

		private static readonly Dictionary<ZDOID, long> requestedOrders = new Dictionary<ZDOID, long>();

		public static ManualLogSource Log { get; private set; } = null;

		public static RuntimeRules Rules { get; private set; } = new RuntimeRules();

		public static KeyboardShortcut RecallKey => settings.Local.Recall;

		public static KeyboardShortcut GotoKey => settings.Local.Order;

		public static KeyboardShortcut FollowKey => settings.Local.Follow;

		public static KeyboardShortcut PanelKey => settings.Local.PanelKey;

		public static bool ShowMarkers => settings.Local.ShowMarkers;

		public static bool LogCombat => settings.Local.CombatDiagnostics;

		internal static CompanionPanelSnapshot? PanelSnapshot => panelSnapshot;

		internal static bool PanelFresh
		{
			get
			{
				if (panelSnapshot != null)
				{
					return Time.unscaledTime - panelSnapshot.ReceivedAt < 4f;
				}
				return false;
			}
		}

		private unsafe static string ActuationKey(ZDOID id, ActuationKind kind)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			ZDOID val = id;
			string? text = ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString();
			int num = (int)kind;
			return text + "/" + num;
		}

		private static void QueueAuthorization(ZDO zdo)
		{
			EnqueueActuation(zdo, new Actuation
			{
				Kind = ActuationKind.Authorize
			});
		}

		private static void QueueName(ZDO zdo, string name, string author)
		{
			if (CompanionNameRules.IsValid(name, author))
			{
				EnqueueActuation(zdo, new Actuation
				{
					Kind = ActuationKind.Name,
					Name = name,
					NameAuthor = author
				});
			}
		}

		private static void QueueOrder(ZDO zdo, CompanionOrder order, Vector3 point, ZDOID target, long sequence)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (Enum.IsDefined(typeof(CompanionOrder), order) && Finite(point) && sequence > 0)
			{
				string key = ActuationKey(zdo.m_uid, ActuationKind.Teleport);
				if (pendingActuations.TryGetValue(key, out Actuation value) && value.Sequence < sequence)
				{
					pendingActuations.Remove(key);
				}
				EnqueueActuation(zdo, new Actuation
				{
					Kind = ActuationKind.Order,
					Order = order,
					Point = point,
					Target = target,
					Sequence = sequence
				});
			}
		}

		private static void QueueProgress(ZDO zdo, long kills, int stars)
		{
			if (kills >= 0 && stars >= 0 && stars <= 100)
			{
				EnqueueActuation(zdo, new Actuation
				{
					Kind = ActuationKind.Progress,
					Kills = kills,
					Stars = stars,
					RulesRevision = rulesRevision,
					BaseHealth = PrefabCatalog.ForestBaseHealth * Rules.BaseHealthMultiplier,
					BaseDamage = Rules.BaseDamageMultiplier,
					HealthPerStar = Rules.HealthPerStar,
					DamagePerStar = Rules.DamagePerStar,
					ResistancePenetration = Rules.ResistancePenetration,
					BossDamage = Rules.BossDamageMultiplier
				});
			}
		}

		private static void QueueTeleport(ZDO zdo, Vector3 point, long followSequence)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(point) && followSequence >= 0)
			{
				EnqueueActuation(zdo, new Actuation
				{
					Kind = ActuationKind.Teleport,
					Point = point,
					Sequence = followSequence,
					Order = CompanionOrder.Follow,
					Target = ZDOID.None
				});
			}
		}

		private static void EnqueueActuation(ZDO zdo, Actuation action)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || !IsOwnPrefab(zdo) || zdo.GetBool("tc_retired", false))
			{
				return;
			}
			action.Instance = zdo.m_uid;
			action.CompanionId = zdo.GetString("trollcompanion_id", "");
			action.Generation = zdo.GetInt("trollcompanion_generation", 0);
			action.Owner = zdo.GetLong("trollcompanion_owner", 0L);
			if (action.CompanionId.Length != 0 && action.Generation > 0 && action.Owner != 0L)
			{
				string key = ActuationKey(action.Instance, action.Kind);
				if (!pendingActuations.TryGetValue(key, out Actuation value) || value.Generation != action.Generation || !(value.CompanionId == action.CompanionId) || (!SameActuationPayload(value, action) && ((action.Kind != ActuationKind.Order && action.Kind != ActuationKind.Teleport) || action.Sequence >= value.Sequence) && (action.Kind != ActuationKind.Progress || action.Kills >= value.Kills)))
				{
					action.Token = Guid.NewGuid().ToString("N");
					action.Revision = ++actuationRevision;
					action.Expires = Time.unscaledTime + 30f;
					pendingActuations[key] = action;
				}
			}
		}

		private static bool SameActuationPayload(Actuation a, Actuation b)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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)
			if (a.Kind == b.Kind && a.Owner == b.Owner && a.Sequence == b.Sequence && a.Kills == b.Kills && a.Stars == b.Stars && a.Order == b.Order && a.Point == b.Point && a.Target == b.Target && a.Name == b.Name && a.NameAuthor == b.NameAuthor && a.RulesRevision == b.RulesRevision && a.BaseHealth == b.BaseHealth && a.BaseDamage == b.BaseDamage && a.HealthPerStar == b.HealthPerStar && a.DamagePerStar == b.DamagePerStar && a.ResistancePenetration == b.ResistancePenetration)
			{
				return a.BossDamage == b.BossDamage;
			}
			return false;
		}

		private static bool MatchesActuation(Actuation action, ZDO zdo)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (IsOwnPrefab(zdo) && zdo.m_uid == action.Instance && !zdo.GetBool("tc_retired", false) && zdo.GetString("trollcompanion_id", "") == action.CompanionId && zdo.GetInt("trollcompanion_generation", 0) == action.Generation)
			{
				return zdo.GetLong("trollcompanion_owner", 0L) == action.Owner;
			}
			return false;
		}

		private static bool HasActiveActuationRecord(Actuation action)
		{
			if (store != null && store.IsHealthy)
			{
				return store.Data.Companions.Any((CompanionRecord r) => r.Id == action.CompanionId && r.OwnerId == action.Owner && r.Generation == action.Generation && r.ActiveZdo == ((object)Unsafe.As<ZDOID, ZDOID>(ref action.Instance)/*cast due to .constrained prefix*/).ToString() && r.State == CompanionState.Active);
			}
			return false;
		}

		private static void TickActuations()
		{
			//IL_0058: 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)
			float unscaledTime = Time.unscaledTime;
			PruneActuationTokens(unscaledTime);
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || ZDOMan.instance == null)
			{
				return;
			}
			Actuation[] array = pendingActuations.Values.ToArray();
			foreach (Actuation action in array)
			{
				string key = ActuationKey(action.Instance, action.Kind);
				if (!pendingActuations.TryGetValue(key, out Actuation value) || value.Token != action.Token)
				{
					continue;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(action.Instance);
				if (zDO == null || !MatchesActuation(action, zDO) || !HasActiveActuationRecord(action) || zDO.GetFloat(ZDOVars.s_health, 1f) <= 0f)
				{
					pendingActuations.Remove(key);
				}
				else if (unscaledTime >= action.Expires)
				{
					pendingActuations.Remove(key);
					Log.LogWarning((object)("Companion action was not acknowledged: " + action.Kind.ToString() + ", " + action.CompanionId + "."));
					NoticeOwner(action.Owner, "A companion action was not acknowledged. Please issue the command again.");
				}
				else if (!(unscaledTime < action.NextSend))
				{
					long num = zDO.GetOwner();
					if (num == 0L)
					{
						zDO.SetOwner(ZNet.GetUID());
						num = ZNet.GetUID();
					}
					action.NextSend = unscaledTime + 1f;
					Reply(num, Message.Actuation, delegate(ZPackage p)
					{
						WriteActuation(p, action);
					});
				}
			}
		}

		private static void WriteActuation(ZPackage p, Actuation action)
		{
			//IL_0026: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			p.Write(action.Token);
			p.Write(action.CompanionId);
			p.Write(action.Generation);
			p.Write(action.Instance);
			p.Write(action.Owner);
			p.Write((int)action.Kind);
			p.Write(action.Revision);
			p.Write((int)action.Order);
			p.Write(action.Point);
			p.Write(action.Target);
			p.Write(action.Sequence);
			p.Write(action.Kills);
			p.Write(action.Stars);
			p.Write(action.Name);
			p.Write(action.NameAuthor);
			p.Write(action.RulesRevision);
			p.Write(action.BaseHealth);
			p.Write(action.BaseDamage);
			p.Write(action.HealthPerStar);
			p.Write(action.DamagePerStar);
			p.Write(action.ResistancePenetration);
			p.Write(action.BossDamage);
		}

		private static Actuation ReadActuation(ZPackage p)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			return new Actuation
			{
				Token = p.ReadString(),
				CompanionId = p.ReadString(),
				Generation = p.ReadInt(),
				Instance = p.ReadZDOID(),
				Owner = p.ReadLong(),
				Kind = (ActuationKind)p.ReadInt(),
				Revision = p.ReadLong(),
				Order = (CompanionOrder)p.ReadInt(),
				Point = p.ReadVector3(),
				Target = p.ReadZDOID(),
				Sequence = p.ReadLong(),
				Kills = p.ReadLong(),
				Stars = p.ReadInt(),
				Name = p.ReadString(),
				NameAuthor = p.ReadString(),
				RulesRevision = p.ReadLong(),
				BaseHealth = p.ReadSingle(),
				BaseDamage = p.ReadSingle(),
				HealthPerStar = p.ReadSingle(),
				DamagePerStar = p.ReadSingle(),
				ResistancePenetration = p.ReadSingle(),
				BossDamage = p.ReadSingle()
			};
		}

		private static void HandleActuation(ZPackage package)
		{
			//IL_00a4: 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_0145: Unknown result type (might be due to invalid IL or missing references)
			PruneActuationTokens(Time.unscaledTime);
			Actuation action = ReadActuation(package);
			if (action.Token.Length != 32 || !Guid.TryParseExact(action.Token, "N", out var _) || action.CompanionId.Length > 64 || action.Generation <= 0 || action.Revision <= 0 || !Enum.IsDefined(typeof(ActuationKind), action.Kind) || !Finite(action.Point) || ZDOMan.instance == null)
			{
				return;
			}
			ZDO zdo = ZDOMan.instance.GetZDO(action.Instance);
			if (zdo == null || !zdo.IsOwner() || !MatchesActuation(action, zdo) || !zdo.GetBool("trollcompanion_ready", false) || zdo.GetFloat(ZDOVars.s_health, 1f) <= 0f)
			{
				return;
			}
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(action.Instance) : null);
			CompanionAgent companionAgent = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<CompanionAgent>() : null);
			if (Object.op_Implicit((Object)(object)val) && (!Object.op_Implicit((Object)(object)companionAgent) || companionAgent.IsRemoving || (Object.op_Implicit((Object)(object)companionAgent.Character) && companionAgent.Character.IsDead())))
			{
				return;
			}
			if (!appliedActuationTokens.ContainsKey(action.Token))
			{
				if (!ApplyActuation(action, zdo, val))
				{
					return;
				}
				appliedActuationTokens[action.Token] = Time.unscaledTime + 120f;
			}
			Send(Message.ActuationAck, delegate(ZPackage p)
			{
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				p.Write(action.Token);
				p.Write(action.CompanionId);
				p.Write(action.Generation);
				p.Write(action.Instance);
				p.Write((int)action.Kind);
				p.Write(action.Revision);
				if (action.Kind == ActuationKind.Progress)
				{
					WriteHealth(p, SampleOwnerHealth(zdo));
				}
			});
		}

		private static bool ApplyActuation(Actuation action, ZDO zdo, GameObject? go)
		{
			//IL_0101: 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_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_043b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0440: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0405: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			switch (action.Kind)
			{
			case ActuationKind.Authorize:
				zdo.Set("tc_authorized", true);
				return true;
			case ActuationKind.Name:
			{
				if (!CompanionNameRules.IsValid(action.Name, action.NameAuthor))
				{
					return false;
				}
				long num2 = zdo.GetLong("tc_applied_name_revision", 0L);
				if (num2 > action.Revision)
				{
					return false;
				}
				if (num2 == action.Revision)
				{
					return true;
				}
				if (!CompanionNameDisplay.Apply(zdo, action.Name, action.NameAuthor))
				{
					return false;
				}
				zdo.Set("tc_applied_name_revision", action.Revision);
				return true;
			}
			case ActuationKind.Order:
			{
				if (!Enum.IsDefined(typeof(CompanionOrder), action.Order) || action.Sequence <= 0)
				{
					return false;
				}
				long num4 = zdo.GetLong("trollcompanion_sequence", 0L);
				long num5 = zdo.GetLong("tc_applied_order_sequence", 0L);
				if (num4 > action.Sequence || num5 > action.Sequence)
				{
					return false;
				}
				if (num5 == action.Sequence)
				{
					return true;
				}
				SetActuatedOrder(zdo, action.Order, action.Point, action.Target, action.Sequence);
				zdo.Set("tc_applied_order_sequence", action.Sequence);
				return true;
			}
			case ActuationKind.Progress:
			{
				if (action.Kills < 0 || action.Stars < 0 || action.Stars > 100 || action.RulesRevision <= 0 || !Finite(action.BaseHealth) || action.BaseHealth <= 0f || !Finite(action.BaseDamage) || action.BaseDamage <= 0f || !Finite(action.HealthPerStar) || action.HealthPerStar < 0f || !Finite(action.DamagePerStar) || action.DamagePerStar < 0f || !Finite(action.ResistancePenetration) || action.ResistancePenetration < 0f || action.ResistancePenetration > 1f || !Finite(action.BossDamage) || action.BossDamage < 0f)
				{
					return false;
				}
				long num3 = zdo.GetLong("tc_applied_progress_revision", 0L);
				if (num3 > action.Revision || zdo.GetLong("trollcompanion_kills", 0L) > action.Kills)
				{
					return false;
				}
				if (num3 == action.Revision)
				{
					return true;
				}
				HealthSnapshot healthSnapshot = CaptureHealth(zdo);
				if (!healthSnapshot.Valid || healthSnapshot.Health <= 0f)
				{
					return false;
				}
				CompanionHealthResult companionHealthResult = CompanionHealth.Calculate(healthSnapshot.Health, healthSnapshot.MaxHealth, healthSnapshot.Stars, action.BaseHealth, action.HealthPerStar, action.Stars);
				zdo.Set("trollcompanion_basehealth", action.BaseHealth);
				zdo.Set("trollcompanion_basedamage", action.BaseDamage);
				zdo.Set("trollcompanion_healthperstar", action.HealthPerStar);
				zdo.Set("trollcompanion_damageperstar", action.DamagePerStar);
				zdo.Set("trollcompanion_resistancepenetration", action.ResistancePenetration);
				zdo.Set("trollcompanion_bossdamage", action.BossDamage);
				zdo.Set("trollcompanion_kills", action.Kills);
				zdo.Set("trollcompanion_stars", action.Stars);
				if (Object.op_Implicit((Object)(object)go))
				{
					go.GetComponent<CompanionAgent>().ApplySynchronizedStats(companionHealthResult.Health);
				}
				else
				{
					zdo.Set(ZDOVars.s_maxHealth, companionHealthResult.MaxHealth);
					zdo.Set(ZDOVars.s_health, companionHealthResult.Health);
				}
				zdo.Set("trollcompanion_rulesrevision", action.RulesRevision);
				zdo.Set("tc_applied_progress_revision", action.Revision);
				return true;
			}
			case ActuationKind.Teleport:
			{
				if (action.Sequence < 0)
				{
					return false;
				}
				if (zdo.GetLong("trollcompanion_sequence", 0L) != action.Sequence || zdo.GetInt("trollcompanion_order", 0) != 0)
				{
					return false;
				}
				long num = zdo.GetLong("tc_applied_teleport_revision", 0L);
				if (num > action.Revision)
				{
					return false;
				}
				if (num == action.Revision)
				{
					return true;
				}
				if (Object.op_Implicit((Object)(object)go))
				{
					Rigidbody component = go.GetComponent<Rigidbody>();
					if (Object.op_Implicit((Object)(object)component))
					{
						component.position = action.Point;
						component.linearVelocity = Vector3.zero;
						component.angularVelocity = Vector3.zero;
					}
					go.transform.position = action.Point;
				}
				zdo.SetPosition(action.Point);
				SetActuatedOrder(zdo, CompanionOrder.Follow, action.Point, ZDOID.None, action.Sequence);
				zdo.Set("tc_applied_teleport_revision", action.Revision);
				return true;
			}
			default:
				return false;
			}
		}

		private static void SetActuatedOrder(ZDO zdo, CompanionOrder order, Vector3 point, ZDOID target, long sequence)
		{
			//IL_0012: 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)
			zdo.Set("trollcompanion_order", (int)order);
			zdo.Set("trollcompanion_point", point);
			zdo.Set("trollcompanion_target", target);
			zdo.Set("trollcompanion_sequence", sequence);
		}

		private static void HandleActuationAck(long sender, ZPackage p)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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)
			string text = p.ReadString();
			string id = p.ReadString();
			int num = p.ReadInt();
			ZDOID val = p.ReadZDOID();
			ActuationKind actuationKind = (ActuationKind)p.ReadInt();
			long num2 = p.ReadLong();
			string key = ActuationKey(val, actuationKind);
			if (!pendingActuations.TryGetValue(key, out Actuation value) || value.Token != text || value.CompanionId != id || value.Generation != num || value.Revision != num2)
			{
				return;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(val);
			if (zDO == null || zDO.GetOwner() != sender || !MatchesActuation(value, zDO) || !HasActiveActuationRecord(value))
			{
				return;
			}
			if (actuationKind == ActuationKind.Progress)
			{
				HealthSnapshot health = ReadHealth(p);
				if (!health.Valid || health.Health <= 0f || health.Revision != value.Revision || health.Generation != num || health.BaseHealth != value.BaseHealth || health.HealthPerStar != value.HealthPerStar || health.Stars != value.Stars || !Commit(delegate(RosterData data)
				{
					SaveHealth(data.Companions.Single((CompanionRecord r) => r.Id == id), health);
				}))
				{
					return;
				}
				if (healthUpdates.TryGetValue(id, out HealthSnapshot value2) && value2.Revision < health.Revision)
				{
					healthUpdates.Remove(id);
				}
			}
			pendingActuations.Remove(key);
		}

		private static void ClearActuations()
		{
			pendingActuations.Clear();
			appliedActuationTokens.Clear();
			actuationRevision = 0L;
			nextActuationTokenCleanup = 0f;
		}

		private static void PruneActuationTokens(float now)
		{
			if (!(now < nextActuationTokenCleanup))
			{
				nextActuationTokenCleanup = now + 5f;
				string[] array = (from p in appliedActuationTokens
					where p.Value <= now
					select p.Key).ToArray();
				foreach (string key in array)
				{
					appliedActuationTokens.Remove(key);
				}
			}
		}

		public static void Initialize(ManualLogSource logger, ConfigFile config, MonoBehaviour host)
		{
			//IL_002d: 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: Expected O, but got Unknown
			//IL_0043: Expected O, but got Unknown
			Log = logger;
			coroutineHost = host;
			settings = new Settings(config);
			RefreshRules();
			rpc = NetworkManager.Instance.AddRPC("TrollCompanion_Protocol2", new CoroutineHandler(ReceiveServer), new CoroutineHandler(ReceiveClient));
		}

		private static void RefreshRules()
		{
			try
			{
				localRules = settings.Read();
				Rules = localRules.Clone();
				configValid = true;
				configError = "";
			}
			catch (Exception ex)
			{
				configValid = false;
				if (configError != ex.Message)
				{
					Log.LogError((object)("Invalid configuration; summoning disabled: " + ex.Message));
				}
				configError = ex.Message;
			}
		}

		public static void Tick()
		{
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: 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_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime < nextTick)
			{
				return;
			}
			nextTick = Time.unscaledTime + 0.25f;
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance != (Object)(object)session)
			{
				ClearSession();
				session = instance;
				shuttingDown = false;
			}
			TickRules();
			if ((Object)(object)instance == (Object)null || !Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance) || ZDOMan.instance == null || shuttingDown)
			{
				return;
			}
			if (instance.IsServer())
			{
				if (store == null)
				{
					OpenWorld();
				}
				if (store != null && store.IsHealthy)
				{
					if (!ready)
					{
						SweepOldInstances();
					}
					else
					{
						ProcessPendingSpawns();
						MaintainRoster();
						AuditInstances();
						TickActuations();
					}
				}
				else if (store != null)
				{
					SpawnLease[] array = pendingSpawns.Values.ToArray();
					for (int i = 0; i < array.Length; i++)
					{
						FailSpawn(array[i], "Summon failed: companion storage is locked.", technicalFailure: true);
					}
				}
				TickRecalls();
			}
			else
			{
				TickActuations();
			}
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.GetPlayerID() != 0L && Time.unscaledTime >= nextHello)
			{
				nextHello = Time.unscaledTime + 2f;
				Send(Message.Hello, delegate(ZPackage p)
				{
					p.Write(((Character)Player.m_localPlayer).GetSkillLevel((SkillType)10));
					p.Write(receivedRulesRevision);
				});
			}
			ZDOID[] array2 = (from x in tombstones
				where x.Value.Expires < Time.unscaledTime
				select x.Key).ToArray();
			foreach (ZDOID key in array2)
			{
				tombstones.Remove(key);
			}
			array2 = (from x in recentGenerations
				where x.Value.Expires < Time.unscaledTime
				select x.Key).ToArray();
			foreach (ZDOID key2 in array2)
			{
				recentGenerations.Remove(key2);
			}
			CleanupClientSpawns();
		}

		private static void OpenWorld()
		{
			long worldUID = ZNet.instance.GetWorldUID();
			store = new RosterStore(Path.Combine(Paths.ConfigPath, "TrollCompanion", "worlds"), worldUID);
			if (!store.Load())
			{
				Log.LogError((object)("Companion roster locked: " + store.Error));
				return;
			}
			Log.LogInfo((object)("Companion roster loaded; reconciling old network objects. World " + worldUID));
			Commit(delegate(RosterData data)
			{
				foreach (CompanionRecord companion in data.Companions)
				{
					if (companion.State == CompanionState.Active || companion.State == CompanionState.Spawning)
					{
						companion.State = CompanionState.Recalling;
					}
				}
			});
		}

		private static void SweepOldInstances()
		{
			int num = 0;
			while (sweepPrefab < prefabNames.Length && num < 8)
			{
				sweep.Clear();
				bool allZDOsWithPrefabIterative = ZDOMan.instance.GetAllZDOsWithPrefabIterative(prefabNames[sweepPrefab], sweep, ref sweepIndex);
				ZDO[] array = sweep.ToArray();
				foreach (ZDO val in array)
				{
					sweepFound = true;
					string id = val.GetString("trollcompanion_id", "");
					CompanionRecord companionRecord = store.Data.Companions.FirstOrDefault((CompanionRecord r) => r.Id == id);
					if (companionRecord != null && companionRecord.State == CompanionState.Recalling && companionRecord.Generation == val.GetInt("trollcompanion_generation", 0) && companionRecord.ActiveZdo == ((object)Unsafe.As<ZDOID, ZDOID>(ref val.m_uid)/*cast due to .constrained prefix*/).ToString() && val.GetFloat(ZDOVars.s_health, companionRecord.Health) > 0f)
					{
						BufferHealth(companionRecord, CaptureHealth(val));
					}
					RetireZdo(val);
				}
				if (allZDOsWithPrefabIterative)
				{
					sweepPrefab++;
					sweepIndex = 0;
				}
				num++;
			}
			retiring.RemoveWhere((ZDOID val2) => ZDOMan.instance.GetZDO(val2) == null);
			if (sweepPrefab != prefabNames.Length || retiring.Count != 0)
			{
				return;
			}
			if (sweepFound)
			{
				sweepFound = false;
				sweepPrefab = (sweepIndex = 0);
				return;
			}
			ready = Commit(delegate(RosterData data)
			{
				foreach (CompanionRecord companion in data.Companions)
				{
					if (companion.State == CompanionState.Recalling || companion.State == CompanionState.Spawning || companion.State == CompanionState.Active)
					{
						if (healthUpdates.TryGetValue(companion.Id, out HealthSnapshot value))
						{
							SaveHealth(companion, value);
						}
						companion.State = CompanionState.Available;
						companion.ActiveZdo = "";
					}
				}
			});
			healthUpdates.Clear();
			if (ready)
			{
				Log.LogInfo((object)"Companion roster ready; old instances removed.");
			}
		}

		private static void MaintainRoster()
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			RefreshStoredStats();
			CompanionRecord[] array = store.Data.Companions.Where((CompanionRecord x) => x.State == CompanionState.Active).ToArray();
			foreach (CompanionRecord companionRecord in array)
			{
				bool num2 = IsConnected(companionRecord.OwnerId);
				int num3 = Progression.Capacity(Skill(companionRecord.OwnerId), Rules.CapacityCurve);
				ZDO val = PlayerZdo(companionRecord.OwnerId);
				ZDO zDO = ZDOMan.instance.GetZDO(ParseZdo(companionRecord.ActiveZdo));
				if (!num2 || OwnerHasNoCharacter(companionRecord.OwnerId) || (val != null && val.GetFloat(ZDOVars.s_health, 1f) <= 0f) || companionRecord.Slot >= num3)
				{
					BeginRecall(companionRecord);
					continue;
				}
				if (zDO == null)
				{
					MissingInstance(companionRecord);
					continue;
				}
				missingInstances.Remove(companionRecord.Id);
				BufferHealth(companionRecord, CaptureHealth(zDO));
				if (zDO.GetFloat(ZDOVars.s_health, 1f) <= 0f)
				{
					RecordDeath(companionRecord);
				}
				else if (zDO.GetInt("trollcompanion_order", 0) == 0 && val != null && Vector3.Distance(zDO.GetPosition(), val.GetPosition()) > Rules.FollowDistance)
				{
					QueueCatchUp(companionRecord);
				}
				SyncProgress(companionRecord, zDO);
				SyncName(companionRecord, zDO);
			}
			HashSet<string> completed = (from r in store.Data.Companions
				where r.State == CompanionState.Recalling && !IsRecallPending(r) && (r.ActiveZdo.Length == 0 || ZDOMan.instance.GetZDO(ParseZdo(r.ActiveZdo)) == null)
				select r.Id).ToHashSet();
			if (completed.Count != 0)
			{
				Commit(delegate(RosterData d)
				{
					foreach (CompanionRecord item in d.Companions.Where((CompanionRecord x) => completed.Contains(x.Id)))
					{
						item.State = CompanionState.Available;
						item.ActiveZdo = "";
					}
				});
			}
			if (Time.unscaledTime >= nextHealthSave && healthUpdates.Count > 0)
			{
				nextHealthSave = Time.unscaledTime + 2f;
				Dictionary<string, HealthSnapshot> updates = new Dictionary<string, HealthSnapshot>(healthUpdates);
				if (Commit(delegate(RosterData d)
				{
					foreach (CompanionRecord companion in d.Companions)
					{
						if (companion.State == CompanionState.Active && updates.TryGetValue(companion.Id, out HealthSnapshot value) && value.Health > 0f)
						{
							SaveHealth(companion, value);
						}
					}
				}))
				{
					healthUpdates.Clear();
				}
			}
		}

		public static bool CanSummon(out string reason)
		{
			if (!configValid)
			{
				reason = "Invalid TrollCompanion configuration.";
				return false;
			}
			if (Rules.DiagnosticOnly)
			{
				reason = "TrollCompanion is in diagnostic mode.";
				return false;
			}
			if (!PrefabCatalog.CanUseVariant(Rules.Variant, out reason))
			{
				return false;
			}
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead())
			{
				reason = "No living summoner.";
				return false;
			}
			if (((Character)Player.m_localPlayer).InInterior())
			{
				reason = "There is no safe space for a troll here.";
				return false;
			}
			if (pendingSummon)
			{
				reason = "Waiting for summon confirmation.";
				return false;
			}
			if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer())
			{
				return Available(Player.m_localPlayer.GetPlayerID(), out reason) != null;
			}
			reason = clientReason;
			if (snapshotValid && Time.unscaledTime - snapshotAt < 6f)
			{
				return clientCanSummon;
			}
			return false;
		}

		public static void RequestSummon(Vector3 position, string castId)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			if (castId.Length == 32 && Guid.TryParseExact(castId, "N", out var _))
			{
				ZDOID casterId = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? ((Character)Player.m_localPlayer).GetZDOID() : ZDOID.None);
				pendingSummon = true;
				Send(Message.Summon, delegate(ZPackage p)
				{
					//IL_0021: 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)
					p.Write(++localActionSequence);
					p.Write(LocalSkill());
					p.Write(position);
					p.Write(castId);
					p.Write(casterId);
				});
			}
		}

		public static void RequestRecall()
		{
			Send(Message.Recall, delegate(ZPackage p)
			{
				p.Write(++localActionSequence);
			});
		}

		public static void RequestFollow()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			RequestOrder(Vector3.zero, ZDOID.None, focus: false, follow: true);
		}

		public static void RequestOrder(Vector3 point, ZDOID enemy, bool focus)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			RequestOrder(point, enemy, focus, follow: false);
		}

		private static void RequestOrder(Vector3 point, ZDOID enemy, bool focus, bool follow)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Send(Message.Order, delegate(ZPackage p)
			{
				//IL_0033: 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)
				p.Write(++localActionSequence);
				p.Write((!follow) ? ((!focus) ? 1 : 2) : 0);
				p.Write(point);
				p.Write(enemy);
			});
		}

		public static void ReportPortal()
		{
			Send(Message.Portal, delegate
			{
			});
		}

		public static void ReportDeath(CompanionAgent agent)
		{
			AgentEvent(Message.Death, agent);
		}

		public static void ReportHealth(CompanionAgent agent)
		{
			AgentEvent(Message.Health, agent);
		}

		public static void RequestCatchUp(CompanionAgent agent)
		{
			AgentEvent(Message.CatchUp, agent);
		}

		public static void ReportStuck(CompanionAgent agent)
		{
			AgentEvent(Message.Stuck, agent);
		}

		public static void ReportKill(CompanionAgent killer, Character victim)
		{
			ZNetView view = ((Component)victim).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)view) && view.IsValid() && view.IsOwner() && Object.op_Implicit((Object)(object)killer.View) && killer.View.IsValid())
			{
				Send(Message.Kill, delegate(ZPackage p)
				{
					//IL_0033: Unknown result type (might be due to invalid IL or missing references)
					//IL_0049: Unknown result type (might be due to invalid IL or missing references)
					p.Write(killer.CompanionId);
					p.Write(killer.Generation);
					p.Write(killer.View.GetZDO().m_uid);
					p.Write(view.GetZDO().m_uid);
				});
			}
		}

		private static void AgentEvent(Message kind, CompanionAgent agent)
		{
			if (Object.op_Implicit((Object)(object)agent.View) && agent.View.IsValid())
			{
				Send(kind, delegate(ZPackage p)
				{
					//IL_0033: Unknown result type (might be due to invalid IL or missing references)
					p.Write(agent.CompanionId);
					p.Write(agent.Generation);
					p.Write(agent.View.GetZDO().m_uid);
					WriteHealth(p, SampleOwnerHealth(agent.View.GetZDO()));
				});
			}
		}

		private static void Send(Message kind, Action<ZPackage> write)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || rpc == null)
			{
				return;
			}
			ZPackage val = new ZPackage();
			val.Write((int)kind);
			val.Write(Guid.NewGuid().ToString("N"));
			write(val);
			if (ZNet.instance.IsServer())
			{
				val.SetPos(0);
				HandleServer(ZNet.GetUID(), val);
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null)
			{
				rpc.SendPackage(serverPeer.m_uid, val);
			}
			else
			{
				pendingSummon = false;
			}
		}

		private static IEnumerator ReceiveServer(long sender, ZPackage package)
		{
			if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer())
			{
				HandleServer(sender, package);
			}
			yield break;
		}

		private static IEnumerator ReceiveClient(long sender, ZPackage package)
		{
			ZNetPeer val = (Object.op_Implicit((Object)(object)ZNet.instance) ? ZNet.instance.GetServerPeer() : null);
			if (val != null && val.m_uid == sender)
			{
				HandleReply(package);
			}
			yield break;
		}

		private static void HandleServer(long sender, ZPackage p)
		{
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_060d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0613: Unknown result type (might be due to invalid IL or missing references)
			//IL_0681: Unknown result type (might be due to invalid IL or missing references)
			//IL_0697: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (sender != ZNet.GetUID())
				{
					ZNetPeer peer = ZNet.instance.GetPeer(sender);
					if (peer == null || !peer.IsReady())
					{
						return;
					}
					PreparePeerConnection(sender, peer);
				}
				else if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					PreparePeerConnection(sender, ZNet.instance);
				}
				Message kind = (Message)p.ReadInt();
				string text = p.ReadString();
				if (text.Length != 32 || !Guid.TryParseExact(text, "N", out var _))
				{
					return;
				}
				if (!seenRequests.TryGetValue(sender, out HashSet<string> value))
				{
					value = (seenRequests[sender] = new HashSet<string>());
				}
				if (!value.Add(text))
				{
					return;
				}
				ZDO character;
				string reason;
				long owner = ResolveOwner(sender, out character, out reason);
				if (kind == Message.PanelQuery)
				{
					ReceivePanelQuery(sender, owner, p);
					return;
				}
				if (kind == Message.ActuationAck)
				{
					HandleActuationAck(sender, p);
					return;
				}
				if (kind == Message.RecallSnapshotResult)
				{
					HandleRecallSnapshotResult(sender, p);
					return;
				}
				if (kind == Message.SpawnResult)
				{
					ReceiveSpawnResult(sender, p);
					return;
				}
				if (kind == Message.GroundResult)
				{
					ReceiveGroundResult(sender, p);
					return;
				}
				if (kind == Message.Hello)
				{
					float value2 = p.ReadSingle();
					if (p.ReadLong() != rulesRevision)
					{
						sentRules.Remove(sender);
					}
					SendRules(sender);
					if (owner == 0L)
					{
						if (!nextIdentityWarning.TryGetValue(sender, out var value3) || Time.unscaledTime >= value3)
						{
							nextIdentityWarning[sender] = Time.unscaledTime + 30f;
							Log.LogWarning((object)("TrollCompanion handshake waiting for peer " + sender + ": " + reason));
						}
						Reply(sender, Message.Snapshot, delegate(ZPackage response)
						{
							response.Write(false);
							response.Write("Synchronizing player identity.");
						});
						return;
					}
					SetSkill(owner, value2);
					if (confirmedHello.Add(sender))
					{
						Log.LogInfo((object)("TrollCompanion handshake confirmed for peer " + sender + "; character " + owner + "."));
					}
					if (ready && store != null && store.IsHealthy)
					{
						int count = Progression.Capacity(Skill(owner), Rules.CapacityCurve);
						if (store.Data.Companions.Count((CompanionRecord x) => x.OwnerId == owner) < count)
						{
							Commit(delegate(RosterData d)
							{
								RosterOperations.EnsureSlots(d, owner, count);
							});
						}
					}
					Snapshot(sender, owner);
					return;
				}
				if (kind == Message.Summon)
				{
					ReceiveSummon(sender, owner, p);
					return;
				}
				if (!ready || store == null || !store.IsHealthy)
				{
					Notice(sender, "Companion roster is not ready or is locked.");
					Snapshot(sender, owner);
					return;
				}
				if (kind == Message.Rename)
				{
					RenameCompanion(sender, owner, p);
					return;
				}
				if (kind == Message.RecallOne)
				{
					ReceiveRecallOne(sender, owner, p);
					return;
				}
				if (kind == Message.Recall || kind == Message.Order)
				{
					long num = p.ReadLong();
					if (owner == 0L || num <= 0 || (lastActionSequence.TryGetValue(sender, out var value4) && num <= value4))
					{
						return;
					}
					lastActionSequence[sender] = num;
					if (kind == Message.Recall)
					{
						foreach (CompanionRecord item in store.Data.Companions.Where((CompanionRecord x) => x.OwnerId == owner && x.State == CompanionState.Active))
						{
							BeginRecall(item);
						}
						SpawnLease[] array = pendingSpawns.Values.Where((SpawnLease x) => x.Owner == owner).ToArray();
						for (int num2 = 0; num2 < array.Length; num2++)
						{
							FailSpawn(array[num2], "Summon recalled during arrival.", technicalFailure: false);
						}
						Notice(sender, "Recalling companions.");
					}
					else
					{
						IssueOrder(sender, owner, (CompanionOrder)p.ReadInt(), p.ReadVector3(), p.ReadZDOID());
					}
					Snapshot(sender, owner);
					return;
				}
				if (kind == Message.Portal)
				{
					if (owner == 0L)
					{
						return;
					}
					SpawnLease[] array = pendingSpawns.Values.Where((SpawnLease x) => x.Owner == owner).ToArray();
					for (int num2 = 0; num2 < array.Length; num2++)
					{
						FailSpawn(array[num2], "Summon cancelled after a portal transition.", technicalFailure: false);
					}
					{
						foreach (CompanionRecord item2 in store.Data.Companions.Where((CompanionRecord r) => r.OwnerId == owner && r.State == CompanionState.Active))
						{
							QueueCatchUp(item2);
						}
						return;
					}
				}
				string id = p.ReadString();
				int generation = p.ReadInt();
				ZDOID activeId = p.ReadZDOID();
				if (id.Length > 64)
				{
					return;
				}
				if (kind == Message.Kill)
				{
					AwardKill(sender, id, generation, activeId, p.ReadZDOID());
					return;
				}
				CompanionRecord companionRecord = store.Data.Companions.FirstOrDefault((CompanionRecord r) => r.Id == id && r.Generation == generation && r.ActiveZdo == ((object)Unsafe.As<ZDOID, ZDOID>(ref activeId)/*cast due to .constrained prefix*/).ToString() && (r.State == CompanionState.Active || (kind == Message.Death && r.State == CompanionState.Recalling)));
				if (companionRecord == null)
				{
					return;
				}
				HealthSnapshot healthSnapshot = ReadHealth(p);
				float health = healthSnapshot.Health;
				if (!Finite(health) || healthSnapshot.Generation != generation)
				{
					return;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(activeId);
				bool num3;
				if (zDO == null)
				{
					if (!tombstones.TryGetValue(activeId, out Tombstone value5))
					{
						return;
					}
					num3 = value5.Owner == sender;
				}
				else
				{
					num3 = zDO.GetOwner() == sender;
				}
				if (!num3)
				{
					return;
				}
				if (kind == Message.Death)
				{
					if (health <= 0f)
					{
						if (companionRecord.State == CompanionState.Recalling)
						{
							ConfirmRecallDeath(companionRecord);
						}
						else
						{
							RecordDeath(companionRecord);
						}
					}
				}
				else if (kind == Message.Health && health > 0f)
				{
					BufferHealth(companionRecord, healthSnapshot);
				}
				else if (kind == Message.CatchUp)
				{
					QueueCatchUp(companionRecord);
				}
				else if (kind == Message.Stuck)
				{
					NoticeOwner(companionRecord.OwnerId, "The troll cannot reach its destination and is holding position.");
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("TrollCompanion request rejected: " + ex.Message));
			}
		}

		private static CompanionRecord? Available(long owner, out string reason)
		{
			reason = "Preparing companion roster.";
			if (!ready || store == null || !store.IsHealthy || !configValid)
			{
				return null;
			}
			if (Rules.DiagnosticOnly)
			{
				reason = "Diagnostic-only mode is enabled.";
				return null;
			}
			if (!PrefabCatalog.CanUseVariant(Rules.Variant, out reason))
			{
				return null;
			}
			ZDO val = PlayerZdo(owner);
			if (val == null || val.GetFloat(ZDOVars.s_health, 1f) <= 0f)
			{
				reason = "No living owner.";
				return null;
			}
			int capacity = Progression.Capacity(Skill(owner), Rules.CapacityCurve);
			CompanionRecord companionRecord = (from r in store.Data.Companions
				where r.OwnerId == owner && r.Slot < capacity
				orderby r.Slot
				select r).ToArray().FirstOrDefault((CompanionRecord r) => (r.State == CompanionState.Available || r.State == CompanionState.Recovering) && r.ReadyAtUtcTicks <= DateTime.UtcNow.Ticks && r.Generation < int.MaxValue);
			reason = ((companionRecord != null) ? "" : "All companion slots are active, being recalled or recovering.");
			return companionRecord;
		}

		private static void RetireZdo(ZDO zdo)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			retiring.Add(zdo.m_uid);
			zdo.SetOwner(ZNet.GetUID());
			zdo.Set("tc_retired", true);
			GameObject val = ZNetScene.instance.FindInstance(zdo.m_uid);
			if (Object.op_Implicit((Object)(object)val))
			{
				CompanionAgent component = val.GetComponent<CompanionAgent>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.IsRemoving = true;
				}
				CharacterDrop component2 = val.GetComponent<CharacterDrop>();
				if (Object.op_Implicit((Object)(object)component2))
				{
					component2.SetDropsEnabled(false);
				}
				ZNetScene.instance.Destroy(val);
			}
			else
			{
				ZDOMan.instance.DestroyZDO(zdo);
			}
		}

		private static bool SafeGround(Vector3 requested, out Vector3 result)
		{
			//IL_0001: 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_000f: 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_0049: 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_005d: 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_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)
			//IL_0099: 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_00a3: 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_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			result = default(Vector3);
			if (!Finite(requested) || Character.InInterior(requested))
			{
				return false;
			}
			int mask = LayerMask.GetMask(new string[4] { "terrain", "Default", "static_solid", "piece" });
			RaycastHit val = default(RaycastHit);
			if (mask == 0 || !Physics.Raycast(requested + Vector3.up * 8f, Vector3.down, ref val, 24f, mask, (QueryTriggerInteraction)1) || ((RaycastHit)(ref val)).normal.y < 0.65f)
			{
				return false;
			}
			result = ((RaycastHit)(ref val)).point + Vector3.up * 0.1f;
			if (Object.op_Implicit((Object)(object)ZoneSystem.instance) && result.y < ZoneSystem.instance.m_waterLevel + 0.2f)
			{
				return false;
			}
			int mask2 = LayerMask.GetMask(new string[6] { "Default", "static_solid", "piece", "character", "character_net", "character_ghost" });
			return !Physics.CheckCapsule(result + Vector3.up * 1.6f, result + Vector3.up * 5f, 1.25f, mask2, (QueryTriggerInteraction)1);
		}

		public static void BeforeZdoDestroyed(ZDOID id)
		{
			//IL_0025: 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_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)
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || ZDOMan.instance == null)
			{
				return;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(id);
			if (zDO != null)
			{
				GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(zDO.GetPrefab()) : null);
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.GetComponent<Character>()))
				{
					tombstones[id] = new Tombstone
					{
						Owner = zDO.GetOwner(),
						Prefab = zDO.GetPrefab(),
						Tamed = zDO.GetBool(ZDOVars.s_tamed, false),
						Aggravated = zDO.GetBool(ZDOVars.s_aggravated, false),
						Position = zDO.GetPosition(),
						Expires = Time.unscaledTime + 30f
					};
				}
			}
		}

		public static void BeforeDisconnect(ZNetPeer peer)
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
			{
				return;
			}
			long owner = peerIdentities.BoundCharacter(peer.m_uid, peer);
			if (owner == 0L && ZNet.instance.GetPeer(peer.m_uid) == peer)
			{
				owner = OwnerFromPeer(peer.m_uid);
			}
			if (owner != 0L && ready && store != null && store.IsHealthy)
			{
				foreach (CompanionRecord item in store.Data.Companions.Where((CompanionRecord r) => r.OwnerId == owner && r.State == CompanionState.Active))
				{
					BeginRecall(item);
				}
				SpawnLease[] array = pendingSpawns.Values.Where((SpawnLease x) => x.Owner == owner).ToArray();
				for (int num = 0; num < array.Length; num++)
				{
					FailSpawn(array[num], "Summon cancelled because the connection ended.", technicalFailure: false);
				}
			}
			if (peerIdentities.Forget(peer.m_uid, peer))
			{
				ClearPeerProtocol(peer.m_uid);
			}
		}

		public static void Shutdown()
		{
			shuttingDown = true;
			if (store != null && store.IsHealthy && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer() && ZDOMan.instance != null && Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				foreach (CompanionRecord item in store.Data.Companions.Where((CompanionRecord r) => r.State == CompanionState.Active))
				{
					BeginRecall(item);
				}
			}
			FinishRecallsForShutdown();
			ClearSession();
		}

		private static void ClearSession()
		{
			store = null;
			ready = false;
			pendingSummon = false;
			snapshotValid = false;
			clientCanSummon = false;
			clientReason = "Synchronizing companion roster.";
			sweepPrefab = (sweepIndex = 0);
			commandSequence = (localActionSequence = 0L);
			sweepFound = false;
			skills.Clear();
			healthUpdates.Clear();
			seenRequests.Clear();
			rewarded.Clear();
			tombstones.Clear();
			sweep.Clear();
			retiring.Clear();
			lastSpawn.Clear();
			lastActionSequence.Clear();
			pendingSpawns.Clear();
			clientSpawns.Clear();
			clientPermits.Clear();
			clientArrivals.Clear();
			groundQueries.Clear();
			recentGenerations.Clear();
			missingInstances.Clear();
			auditPrefab = (auditIndex = 0);
			ClearActuations();
			ClearRecalls();
			requestedOrders.Clear();
			nextHello = (nextHealthSave = 0f);
			peerIdentities.Clear();
			nextIdentityWarning.Clear();
			confirmedHello.Clear();
			summonCasts.Clear();
			settledSummonCasts.Clear();
			StaffControls.ClearCastSession();
			renameSequences.Clear();
			ClearPanelSession();
			ClearRulesSession();
		}

		private static void Snapshot(long peer, long owner)
		{
			string reason;
			bool can = owner != 0L && Available(owner, out reason) != null;
			Available(owner, out string reason2);
			Reply(peer, Message.Snapshot, delegate(ZPackage p)
			{
				p.Write(can);
				p.Write(reason2);
			});
		}

		private static void Notice(long peer, string text, bool marker = false, Vector3 position = default(Vector3))
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Reply(peer, Message.Notice, delegate(ZPackage p)
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				p.Write(text);
				p.Write(marker);
				p.Write(position);
			});
		}

		private static void NoticeOwner(long owner, string text)
		{
			//IL_000f: 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)
			long num = PeerForOwner(owner);
			if (num != 0L)
			{
				Notice(num, text);
			}
		}

		private static void Reply(long peer, Message kind, Action<ZPackage> write)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((int)kind);
			write(val);
			if (peer == ZNet.GetUID())
			{
				val.SetPos(0);
				HandleReply(val);
			}
			else
			{
				rpc.SendPackage(peer, val);
			}
		}

		private static void HandleReply(ZPackage p)
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				switch ((Message)p.ReadInt())
				{
				case Message.RulesSnapshot:
					ReceiveRules(p);
					break;
				case Message.PanelSnapshot:
					ReceivePanelSnapshot(p);
					break;
				case Message.SpawnPermit:
					ExecuteSpawn(p);
					break;
				case Message.SummonOutcome:
				{
					string castId = p.ReadString();
					bool success = p.ReadBool();
					bool refund = p.ReadBool();
					string reason = p.ReadString();
					pendingSummon = false;
					StaffControls.CompleteSummon(castId, success, refund, reason);
					break;
				}
				case Message.CancelArrival:
					CancelClientArrival(p.ReadString());
					break;
				case Message.Actuation:
					HandleActuation(p);
					break;
				case Message.RecallSnapshot:
					HandleRecallSnapshot(p);
					break;
				case Message.GroundQuery:
					ExecuteGroundQuery(p);
					break;
				case Message.Snapshot:
					clientCanSummon = p.ReadBool();
					clientReason = p.ReadString();
					snapshotAt = Time.unscaledTime;
					if (!snapshotValid)
					{
						Log.LogInfo((object)"TrollCompanion network: received the first roster response from the server.");
					}
					snapshotValid = true;
					pendingSummon = false;
					break;
				case Message.Notice:
				{
					Notify(p.ReadString());
					bool num3 = p.ReadBool();
					Vector3 position = p.ReadVector3();
					if (num3)
					{
						StaffControls.ShowMarker(position);
					}
					break;
				}
				case Message.SkillReward:
				{
					string item = p.ReadString();
					long num = p.ReadLong();
					float num2 = p.ReadSingle();
					if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.GetPlayerID() == num && Finite(num2) && num2 >= 0f && rewarded.Add(item))
					{
						((Character)Player.m_localPlayer).RaiseSkill((SkillType)10, num2);
					}
					break;
				}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Invalid server response: " + ex.Message));
			}
		}

		public static void Notify(string text)
		{
			Log.LogInfo((object)text);
			if (settings.Local.ShowMessages && Object.op_Implicit((Object)(object)MessageHud.instance))
			{
				MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
			}
		}

		private static bool Commit(Action<RosterData> mutate)
		{
			if (store == null)
			{
				return false;
			}
			if (store.Commit(mutate))
			{
				return true;
			}
			ready = false;
			Log.LogError((object)("Unable to save companion data; actions disabled: " + store.Error));
			return false;
		}

		private static float LocalSkill()
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return 0f;
			}
			return ((Character)Player.m_localPlayer).GetSkillLevel((SkillType)10);
		}

		private static void SetSkill(long owner, float value)
		{
			if (!Finite(value) || value < 0f || value > 100f)
			{
				throw new ArgumentException("Invalid skill value.");
			}
			skills[owner] = value;
		}

		private static float Skill(long owner)
		{
			if (!skills.TryGetValue(owner, out var value))
			{
				return 0f;
			}
			return value;
		}

		private static bool IsConnected(long owner)
		{
			return PeerForOwner(owner) != 0;
		}

		private static bool OwnerHasNoCharacter(long owner)
		{
			long num = PeerForOwner(owner);
			if (num == 0L)
			{
				return false;
			}
			if (num == ZNet.GetUID())
			{
				return !Object.op_Implicit((Object)(object)Player.m_localPlayer);
			}
			ZNetPeer peer = ZNet.instance.GetPeer(num);
			if (peer != null)
			{
				return ((ZDOID)(ref peer.m_characterID)).IsNone();
			}
			return false;
		}

		private static long OwnerFromPeer(long sender)
		{
			ZDO character;
			string reason;
			return ResolveOwner(sender, out character, out reason);
		}

		private static void ClearPeerProtocol(long peer)
		{
			sentRules.Remove(peer);
			panelRateLimits.Remove(peer);
			seenRequests.Remove(peer);
			lastActionSequence.Remove(peer);
			renameSequences.Remove(peer);
			nextIdentityWarning.Remove(peer);
			confirmedHello.Remove(peer);
			summonCasts.Remove(peer);
			settledSummonCasts.Remove(peer);
		}

		private static void PreparePeerConnection(long peer, object connection)
		{
			if (peerIdentities.BeginConnection(peer, connection))
			{
				ClearPeerProtocol(peer);
			}
		}

		private static long ResolveOwner(long sender, out ZDO? character, out string reason)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00d8: Unknown result type (might be due to invalid IL or missing references)
			character = null;
			reason = "The network world is not ready.";
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance) || ZDOMan.instance == null || sender == 0L)
			{
				return 0L;
			}
			object connection;
			ZDOID val;
			if (sender == ZNet.GetUID())
			{
				if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					reason = "Local player is unavailable.";
					return 0L;
				}
				ZNetView component = ((Component)Player.m_localPlayer).GetComponent<ZNetView>();
				if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
				{
					reason = "The local player has no network object yet.";
					return 0L;
				}
				connection = ZNet.instance;
				val = component.GetZDO().m_uid;
			}
			else
			{
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				if (peer == null || !peer.IsReady())
				{
					reason = "The sender is not a connected peer.";
					return 0L;
				}
				connection = peer;
				val = peer.m_characterID;
			}
			PreparePeerConnection(sender, connection);
			if (((ZDOID)(ref val)).IsNone())
			{
				reason = "Waiting for native character identity synchronization.";
				return 0L;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(val);
			if (zDO == null)
			{
				reason = "The announced player ZDO has not arrived yet.";
				return 0L;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab());
			long num = zDO.GetLong(ZDOVars.s_playerID, 0L);
			if (!peerIdentities.TryBind(sender, connection, ((ZDOID)(ref zDO.m_uid)).UserID, zDO.GetOwner(), num, Object.op_Implicit((Object)(object)prefab) && Object.op_Implicit((Object)(object)prefab.GetComponent<Player>()), out reason))
			{
				return 0L;
			}
			character = zDO;
			return num;
		}

		private static long PeerForOwner(long owner)
		{
			if (owner == 0L || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return 0L;
			}
			long uID = ZNet.GetUID();
			if (peerIdentities.BoundCharacter(uID, ZNet.instance) == owner || OwnerFromPeer(uID) == owner)
			{
				return uID;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.IsReady() && (peerIdentities.BoundCharacter(peer.m_uid, peer) == owner || OwnerFromPeer(peer.m_uid) == owner))
				{
					return peer.m_uid;
				}
			}
			return 0L;
		}

		private static CompanionAgent? FindAgent(ZDOID id)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = ZNetScene.instance.FindInstance(id);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			return val.GetComponent<CompanionAgent>();
		}

		private static ZDOID ParseZdo(string text)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			string[] array = text.Split(':');
			if (array.Length != 2 || !long.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !uint.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return ZDOID.None;
			}
			return new ZDOID(result, result2);
		}

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

		private static bool Finite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(value.x) && Finite(value.y))
			{
				return Finite(value.z);
			}
			return false;
		}

		private static void TickRules()
		{
			if (settings.Tick(out RuntimeRules rules))
			{
				localRules = rules;
				if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsServer())
				{
					if (!configValid || Rules.Serialize() != rules.Serialize())
					{
						Rules = rules.Clone();
						rulesRevision++;
						sentRules.Clear();
						Log.LogInfo((object)"Server configuration accepted; companion stats are being synchronized.");
					}
					configValid = true;
				}
			}
			if (settings.LastError != lastSettingsError)
			{
				lastSettingsError = settings.LastError;
				if (lastSettingsError.Length > 0)
				{
					Log.LogError((object)("Configuration rejected; the last accepted rules remain active: " + lastSettingsError));
				}
			}
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.IsReady())
				{
					SendRules(peer.m_uid);
				}
			}
		}

		private static void SendRules(long peer)
		{
			if (peer != ZNet.GetUID() && (!sentRules.TryGetValue(peer, out var value) || value != rulesRevision))
			{
				sentRules[peer] = rulesRevision;
				Reply(peer, Message.RulesSnapshot, delegate(ZPackage p)
				{
					p.Write(rulesRevision);
					p.Write(configValid);
					p.Write(configValid ? Rules.Serialize() : "");
				});
			}
		}

		private static void ReceiveRules(ZPackage p)
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsServer())
			{
				return;
			}
			long num = p.ReadLong();
			bool flag = p.ReadBool();
			string text = p.ReadString();
			if (num > receivedRulesRevision && num > 0 && text.Length <= 16384)
			{
				if (!flag)
				{
					configValid = false;
					receivedRulesRevision = num;
					return;
				}
				RuntimeRules runtimeRules = RuntimeRules.ReadSerialized(text);
				runtimeRules.Validate();
				Rules = runtimeRules;
				rulesRevision = (receivedRulesRevision = num);
				configValid = true;
			}
		}

		private static void ClearRulesSession()
		{
			sentRules.Clear();
			receivedRulesRevision = 0L;
			rulesRevision = 1L;
			Rules = localRules.Clone();
			configValid = settings.HasValidConfiguration && (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsServer());
		}

		public static void DisposeSettings()
		{
			settings.Dispose();
		}

		public static void RequestRename(CompanionAgent agent, string text)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || !agent.Authorized || agent.IsRemoving || agent.OwnerId != localPlayer.GetPlayerID())
			{
				Notify("Only the owner can rename this active companion.");
				return;
			}
			if (!CompanionNameRules.TryNormalize(text, out string name))
			{
				Notify("Invalid name: use at most 10 characters, with no control characters.");
				return;
			}
			string author;
			try
			{
				author = CompanionNameDisplay.GetLocalAuthor();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Unable to resolve the name author: " + ex.GetType().Name));
				Notify("Unable to save the name.");
				return;
			}
			if (!CompanionNameRules.IsValid(name, author) || !CompanionNameDisplay.NativeAuthorValid(author))
			{
				Notify("Unable to save the name: invalid name data.");
				return;
			}
			long sequence = ++localActionSequence;
			Send(Message.Rename, delegate(ZPackage p)
			{
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				p.Write(agent.CompanionId);
				p.Write(agent.Generation);
				p.Write(agent.View.GetZDO().m_uid);
				p.Write(sequence);
				p.Write(name);
				p.Write(author);
			});
		}

		private static void RenameCompanion(long sender, long owner, ZPackage p)
		{
			//IL_0027: 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_00ed: 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_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			string id = p.ReadString();
			int generation = p.ReadInt();
			ZDOID instance = p.ReadZDOID();
			long num = p.ReadLong();
			string value = p.ReadString();
			string author = p.ReadString();
			if (owner == 0L || id.Length > 64 || generation <= 0 || ((ZDOID)(ref instance)).IsNone() || num <= 0 || !CompanionNameRules.TryNormalize(value, out string name) || !CompanionNameRules.IsValid(name, author) || !CompanionNameDisplay.NativeAuthorValid(author))
			{
				return;
			}
			CompanionRecord companionRecord = store.Data.Companions.FirstOrDef