Decompiled source of MushroomSync v1.0.0

MushroomSync.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("MushroomSync")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Shared server-authoritative sync for the Mushroom mods.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3c15a0cc54c8b70d1d4aad090ba6cdc3ca225dc9")]
[assembly: AssemblyProduct("Mushroom Sync")]
[assembly: AssemblyTitle("MushroomSync")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MushroomSync
{
	internal static class ChannelRegistry
	{
		private static readonly List<SyncChannel> Channels = new List<SyncChannel>();

		private static readonly object Gate = new object();

		private static ManualLogSource _log;

		private static bool _patched;

		internal static void Initialize(Harmony harmony, ManualLogSource log)
		{
			_log = log;
			if (!_patched)
			{
				Patch(harmony, AccessTools.Method(typeof(ZNet), "OnNewConnection", new Type[1] { typeof(ZNetPeer) }, (Type[])null), "ZNetOnNewConnectionPostfix", null, required: true);
				Patch(harmony, AccessTools.Method(typeof(ZNet), "RPC_PeerInfo", new Type[2]
				{
					typeof(ZRpc),
					typeof(ZPackage)
				}, (Type[])null), "ZNetRpcPeerInfoPostfix", null, required: true);
				Patch(harmony, AccessTools.Method(typeof(ZNet), "Disconnect", new Type[1] { typeof(ZNetPeer) }, (Type[])null), "ZNetDisconnectPostfix");
				Patch(harmony, AccessTools.Method(typeof(ZNet), "OnDestroy", Type.EmptyTypes, (Type[])null), null, "ZNetOnDestroyPrefix");
				_patched = true;
			}
		}

		internal static void Add(SyncChannel channel)
		{
			lock (Gate)
			{
				foreach (SyncChannel channel2 in Channels)
				{
					if (string.Equals(channel2.Id, channel.Id, StringComparison.Ordinal))
					{
						ManualLogSource log = _log;
						if (log != null)
						{
							log.LogWarning((object)("Two sync channels share the id '" + channel.Id + "'. The second is ignored; give each mod its own id."));
						}
						return;
					}
				}
				Channels.Add(channel);
			}
		}

		private static SyncChannel[] Snapshot()
		{
			lock (Gate)
			{
				return Channels.ToArray();
			}
		}

		private static void Patch(Harmony harmony, MethodInfo method, string postfix = null, string prefix = null, bool required = false)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (method == null)
			{
				string text = postfix ?? prefix;
				if (required)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogError((object)("MushroomSync: could not find the ZNet method for " + text + ". Sync will not work."));
					}
				}
				else
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogWarning((object)("MushroomSync: could not find the ZNet method for " + text + "."));
					}
				}
			}
			else
			{
				harmony.Patch((MethodBase)method, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ChannelRegistry), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ChannelRegistry), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static IEnumerator DeferFrames(int frameCount, Action action)
		{
			for (int i = 0; i < frameCount; i++)
			{
				yield return null;
			}
			try
			{
				action();
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("MushroomSync: deferred action failed: " + ex.Message));
				}
			}
		}

		public static void ZNetOnNewConnectionPostfix(ZNetPeer peer)
		{
			if (peer != null && peer.m_rpc != null)
			{
				SyncChannel[] array = Snapshot();
				for (int i = 0; i < array.Length; i++)
				{
					array[i].RegisterOn(peer.m_rpc);
				}
			}
		}

		public static void ZNetRpcPeerInfoPostfix(ZNet __instance, ZRpc rpc)
		{
			if ((Object)(object)__instance == (Object)null || rpc == null)
			{
				return;
			}
			SyncChannel[] channels = Snapshot();
			if (channels.Length == 0)
			{
				return;
			}
			if (__instance.IsServer())
			{
				((MonoBehaviour)__instance).StartCoroutine(DeferFrames(channels[0].DeferredFrames, delegate
				{
					SyncChannel[] array = channels;
					for (int i = 0; i < array.Length; i++)
					{
						array[i].SendTo(rpc);
					}
				}));
			}
			else
			{
				if (__instance.GetServerRPC() != rpc)
				{
					return;
				}
				((MonoBehaviour)__instance).StartCoroutine(DeferFrames(channels[0].DeferredFrames, delegate
				{
					SyncChannel[] array = channels;
					for (int i = 0; i < array.Length; i++)
					{
						array[i].RequestFromServer();
					}
				}));
			}
		}

		public static void ZNetDisconnectPostfix(ZNet __instance, ZNetPeer peer)
		{
			if (!((Object)(object)__instance == (Object)null) && !__instance.IsServer() && peer != null && peer.m_server)
			{
				SyncChannel[] array = Snapshot();
				for (int i = 0; i < array.Length; i++)
				{
					array[i].ClearClientState("disconnect");
				}
			}
		}

		public static void ZNetOnDestroyPrefix(ZNet __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !__instance.IsServer())
			{
				SyncChannel[] array = Snapshot();
				for (int i = 0; i < array.Length; i++)
				{
					array[i].ClearClientState("network shutdown");
				}
			}
		}
	}
	public sealed class ConfigSync
	{
		private static readonly string PathSeparator = '\u001f'.ToString();

		private readonly Dictionary<string, ConfigEntryBase> _entries = new Dictionary<string, ConfigEntryBase>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<ConfigEntryBase, object> _syncedValues = new Dictionary<ConfigEntryBase, object>();

		private readonly HashSet<ConfigEntryBase> _excluded = new HashSet<ConfigEntryBase>();

		private readonly HashSet<ConfigFile> _watchedFiles = new HashSet<ConfigFile>();

		private bool _hasHostValues;

		private readonly SyncChannel _channel;

		private readonly ManualLogSource _log;

		private readonly string _id;

		private Action _applied;

		private Action<string> _notify;

		public bool ClientSyncActive => _channel.ClientSyncActive;

		private ConfigSync(string id, string version, ManualLogSource log)
		{
			_id = id;
			_log = log;
			_channel = SyncChannel.Create(id + ".Config", version, log);
			_channel.WritePayload = WritePayload;
			_channel.ReadPayload = ReadPayload;
			_channel.Cleared = OnCleared;
		}

		public static bool IsServerAuthority()
		{
			return SyncChannel.IsServerAuthority();
		}

		public static ConfigSync Create(string id, string version, ManualLogSource log)
		{
			return new ConfigSync(id, version, log);
		}

		public ConfigSync Protecting(ConfigFile file)
		{
			ConfigValueOverlay.Protect(file);
			return this;
		}

		public ConfigSync OnApplied(Action applied)
		{
			_applied = applied;
			return this;
		}

		public ConfigSync Notifying(Action<string> notify)
		{
			_notify = notify;
			return this;
		}

		public ConfigSync GatedBy(Func<bool> gate)
		{
			_channel.SendGate = gate;
			return this;
		}

		public ConfigSync AcceptedWhen(Func<bool> gate)
		{
			_channel.AcceptGate = gate;
			return this;
		}

		public ConfigSync Start()
		{
			_channel.Start();
			return this;
		}

		public ConfigSync Register(ConfigEntryBase entry)
		{
			if (entry == null)
			{
				return this;
			}
			string key = BuildPath(entry.Definition.Section, entry.Definition.Key);
			if (_entries.ContainsKey(key))
			{
				return this;
			}
			_entries[key] = entry;
			ConfigValueOverlay.Claim(entry, this);
			return this;
		}

		public ConfigSync Register(params ConfigEntryBase[] entries)
		{
			if (entries != null)
			{
				foreach (ConfigEntryBase entry in entries)
				{
					Register(entry);
				}
			}
			return this;
		}

		public ConfigSync WatchForChanges(ConfigFile file)
		{
			if (file == null || !_watchedFiles.Add(file))
			{
				return this;
			}
			file.SettingChanged += OnSettingChanged;
			return this;
		}

		public ConfigSync Exclude(ConfigEntryBase entry)
		{
			if (entry == null)
			{
				return this;
			}
			_excluded.Add(entry);
			ConfigValueOverlay.Release(entry);
			_entries.Remove(BuildPath(entry.Definition.Section, entry.Definition.Key));
			return this;
		}

		public bool TryGetSyncedValue<T>(ConfigEntry<T> entry, out T value)
		{
			if (_channel.ClientSyncActive && entry != null && _syncedValues.TryGetValue((ConfigEntryBase)(object)entry, out var value2) && value2 is T)
			{
				value = (T)value2;
				return true;
			}
			value = default(T);
			return false;
		}

		public void Broadcast()
		{
			if (IsServerAuthority())
			{
				_channel.Broadcast();
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			if (args != null && args.ChangedSetting != null)
			{
				string key = BuildPath(args.ChangedSetting.Definition.Section, args.ChangedSetting.Definition.Key);
				if (_entries.ContainsKey(key))
				{
					Broadcast();
				}
			}
		}

		private static string BuildPath(string section, string key)
		{
			return (section ?? string.Empty) + PathSeparator + (key ?? string.Empty);
		}

		private bool ShouldSync(ConfigEntryBase entry)
		{
			if (entry != null)
			{
				return !_excluded.Contains(entry);
			}
			return false;
		}

		private void WritePayload(ZPackage payload)
		{
			List<ConfigEntryBase> list = new List<ConfigEntryBase>();
			foreach (ConfigEntryBase value in _entries.Values)
			{
				if (ShouldSync(value))
				{
					list.Add(value);
				}
			}
			list.Sort(CompareEntries);
			payload.Write(list.Count);
			foreach (ConfigEntryBase item in list)
			{
				payload.Write(item.Definition.Section ?? string.Empty);
				payload.Write(item.Definition.Key ?? string.Empty);
				payload.Write(ConfigValueOverlay.ReadLocalSerialized(item));
			}
		}

		private static int CompareEntries(ConfigEntryBase a, ConfigEntryBase b)
		{
			int num = StringComparer.OrdinalIgnoreCase.Compare(a.Definition.Section, b.Definition.Section);
			if (num == 0)
			{
				return StringComparer.OrdinalIgnoreCase.Compare(a.Definition.Key, b.Definition.Key);
			}
			return num;
		}

		private void ReadPayload(ZPackage payload)
		{
			int num = SyncChannel.ReadCount(payload);
			Dictionary<ConfigEntryBase, object> dictionary = new Dictionary<ConfigEntryBase, object>();
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				string text = payload.ReadString();
				string text2 = payload.ReadString();
				string text3 = payload.ReadString();
				object value2;
				if (!_entries.TryGetValue(BuildPath(text, text2), out var value) || !ShouldSync(value))
				{
					num2++;
				}
				else if (!TryConvert(value, text3, out value2))
				{
					_log.LogWarning((object)(_id + ": could not parse [" + text + "] " + text2 + " = '" + text3 + "' as " + value.SettingType.Name + "; keeping the local value."));
				}
				else
				{
					dictionary[value] = value2;
				}
			}
			bool flag = !_hasHostValues;
			_syncedValues.Clear();
			foreach (KeyValuePair<ConfigEntryBase, object> item in dictionary)
			{
				_syncedValues[item.Key] = item.Value;
			}
			_hasHostValues = true;
			RaiseApplied();
			if (flag)
			{
				_log.LogInfo((object)(_id + ": using host configuration (" + dictionary.Count + " settings)."));
				Notify(_id + ": using host configuration.");
			}
			else
			{
				_log.LogInfo((object)(_id + ": host configuration updated (" + dictionary.Count + " settings)."));
			}
			if (num2 > 0)
			{
				_log.LogInfo((object)(_id + ": ignored " + num2 + " setting(s) this build does not have."));
			}
		}

		private void OnCleared(string reason)
		{
			_syncedValues.Clear();
			_hasHostValues = false;
			RaiseApplied();
			_log.LogInfo((object)(_id + ": host configuration dropped (" + reason + "); using local settings."));
			if (reason == "version mismatch" || reason == "invalid payload")
			{
				Notify(_id + ": host config unavailable (" + reason + "); using your local settings.");
			}
		}

		private static bool TryConvert(ConfigEntryBase entry, string serialized, out object value)
		{
			try
			{
				if (entry.SettingType.IsEnum)
				{
					value = Enum.Parse(entry.SettingType, serialized ?? string.Empty, ignoreCase: true);
					return true;
				}
				value = TomlTypeConverter.ConvertToValue(serialized, entry.SettingType);
				return value != null;
			}
			catch (Exception)
			{
				value = null;
				return false;
			}
		}

		private void RaiseApplied()
		{
			if (_applied == null)
			{
				return;
			}
			try
			{
				_applied();
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(_id + ": OnApplied handler failed: " + ex.Message));
			}
		}

		private void Notify(string message)
		{
			if (_notify == null)
			{
				return;
			}
			try
			{
				_notify(message);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(_id + ": notify handler failed: " + ex.Message));
			}
		}
	}
	internal static class ConfigValueOverlay
	{
		private static readonly Dictionary<ConfigEntryBase, ConfigSync> Owners = new Dictionary<ConfigEntryBase, ConfigSync>();

		private static readonly HashSet<Type> PatchedTypes = new HashSet<Type>();

		private static readonly HashSet<ConfigFile> ProtectedFiles = new HashSet<ConfigFile>();

		private static readonly object Gate = new object();

		private static Harmony _harmony;

		private static ManualLogSource _log;

		[ThreadStatic]
		private static int _readLocalDepth;

		internal static void Initialize(Harmony harmony, ManualLogSource log)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_0074: Expected O, but got Unknown
			_harmony = harmony;
			_log = log;
			MethodInfo methodInfo = AccessTools.Method(typeof(ConfigFile), "Save", Type.EmptyTypes, (Type[])null);
			if (methodInfo == null)
			{
				_log.LogWarning((object)"MushroomSync: ConfigFile.Save not found; a synced client that saves its config could persist host values locally.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ConfigValueOverlay), "ConfigFileSavePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ConfigValueOverlay), "ConfigFileSaveFinalizer", (Type[])null), (HarmonyMethod)null);
			}
		}

		internal static void Claim(ConfigEntryBase entry, ConfigSync owner)
		{
			lock (Gate)
			{
				if (Owners.ContainsKey(entry))
				{
					return;
				}
				Owners[entry] = owner;
			}
			EnsurePatchedFor(entry.SettingType);
		}

		internal static void Release(ConfigEntryBase entry)
		{
			lock (Gate)
			{
				Owners.Remove(entry);
			}
		}

		internal static void Protect(ConfigFile file)
		{
			if (file == null)
			{
				return;
			}
			lock (Gate)
			{
				ProtectedFiles.Add(file);
			}
		}

		internal static string ReadLocalSerialized(ConfigEntryBase entry)
		{
			_readLocalDepth++;
			try
			{
				return entry.GetSerializedValue() ?? string.Empty;
			}
			finally
			{
				_readLocalDepth--;
			}
		}

		private static void EnsurePatchedFor(Type settingType)
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			if (settingType == null || _harmony == null)
			{
				return;
			}
			lock (Gate)
			{
				if (!PatchedTypes.Add(settingType))
				{
					return;
				}
			}
			try
			{
				MethodInfo methodInfo = typeof(ConfigEntry<>).MakeGenericType(settingType).GetProperty("Value", BindingFlags.Instance | BindingFlags.Public)?.GetGetMethod();
				if (methodInfo == null)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)("MushroomSync: no Value getter on ConfigEntry<" + settingType.Name + ">."));
					}
				}
				else
				{
					MethodInfo methodInfo2 = AccessTools.Method(typeof(ConfigValueOverlay), "ValueGetterPostfix", (Type[])null, (Type[])null).MakeGenericMethod(settingType);
					_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				lock (Gate)
				{
					PatchedTypes.Remove(settingType);
				}
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogWarning((object)("MushroomSync: could not overlay ConfigEntry<" + settingType.Name + ">.Value: " + ex.Message + ". TryGetSyncedValue still works for it."));
				}
			}
		}

		public static void ValueGetterPostfix<T>(ConfigEntry<T> __instance, ref T __result)
		{
			if (_readLocalDepth > 0 || __instance == null)
			{
				return;
			}
			ConfigSync value;
			lock (Gate)
			{
				if (!Owners.TryGetValue((ConfigEntryBase)(object)__instance, out value))
				{
					return;
				}
			}
			if (value.TryGetSyncedValue<T>(__instance, out var value2))
			{
				__result = value2;
			}
		}

		public static void ConfigFileSavePrefix(ConfigFile __instance, ref bool __state)
		{
			__state = false;
			lock (Gate)
			{
				if (!ProtectedFiles.Contains(__instance))
				{
					return;
				}
			}
			_readLocalDepth++;
			__state = true;
		}

		public static Exception ConfigFileSaveFinalizer(Exception __exception, bool __state)
		{
			if (__state && _readLocalDepth > 0)
			{
				_readLocalDepth--;
			}
			return __exception;
		}
	}
	[BepInPlugin("mushroom.sync", "Mushroom Sync", "1.0.0")]
	public class MushroomSyncPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "mushroom.sync";

		public const string PluginName = "Mushroom Sync";

		public const string PluginVersion = "1.0.0";

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("mushroom.sync");
			ChannelRegistry.Initialize(_harmony, ((BaseUnityPlugin)this).Logger);
			ConfigValueOverlay.Initialize(_harmony, ((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Mushroom Sync 1.0.0 loaded.");
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	public sealed class SyncChannel
	{
		public const int ProtocolVersion = 2;

		private const int DeferFrames = 2;

		private const int MaxEntries = 10000;

		private readonly string _id;

		private readonly string _version;

		private readonly ManualLogSource _log;

		private bool _clientSyncActive;

		private bool _started;

		public string Id => _id;

		public string RpcSync => _id + ".Sync";

		public string RpcSyncRequest => _id + ".SyncRequest";

		public bool ClientSyncActive => _clientSyncActive;

		public Action<ZPackage> WritePayload { get; set; }

		public Action<ZPackage> ReadPayload { get; set; }

		public Action<string> Cleared { get; set; }

		public Func<bool> SendGate { get; set; }

		public Func<bool> AcceptGate { get; set; }

		internal int DeferredFrames => 2;

		private SyncChannel(string id, string version, ManualLogSource log)
		{
			_id = id;
			_version = version ?? string.Empty;
			_log = log;
		}

		public static bool IsServerAuthority()
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				return instance.IsServer();
			}
			return true;
		}

		public static SyncChannel Create(string id, string version, ManualLogSource log)
		{
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("A sync channel needs an id.", "id");
			}
			if (log == null)
			{
				throw new ArgumentNullException("log");
			}
			return new SyncChannel(id, version, log);
		}

		public SyncChannel Start()
		{
			if (_started)
			{
				return this;
			}
			_started = true;
			ChannelRegistry.Add(this);
			return this;
		}

		public void Broadcast()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer())
			{
				return;
			}
			foreach (ZNetPeer connectedPeer in instance.GetConnectedPeers())
			{
				if (connectedPeer != null && connectedPeer.IsReady() && connectedPeer.m_rpc != null)
				{
					SendTo(connectedPeer.m_rpc);
				}
			}
		}

		public void ClearClientState(string reason)
		{
			if (_clientSyncActive)
			{
				_clientSyncActive = false;
				InvokeCleared(reason);
			}
		}

		internal void RegisterOn(ZRpc rpc)
		{
			try
			{
				rpc.Register<ZPackage>(RpcSync, (Action<ZRpc, ZPackage>)ReceiveSync);
				rpc.Register<ZPackage>(RpcSyncRequest, (Action<ZRpc, ZPackage>)ReceiveSyncRequest);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(_id + ": RPC registration failed: " + ex.Message));
			}
		}

		internal void SendTo(ZRpc rpc)
		{
			if (rpc == null || !rpc.IsConnected() || WritePayload == null || (SendGate != null && !SendGate()))
			{
				return;
			}
			try
			{
				rpc.Invoke(RpcSync, new object[1] { BuildEnvelope() });
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(_id + ": send failed: " + ex.Message));
			}
		}

		internal void RequestFromServer()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || instance.IsServer())
			{
				return;
			}
			ZRpc serverRPC = instance.GetServerRPC();
			if (serverRPC == null || !serverRPC.IsConnected())
			{
				return;
			}
			try
			{
				ZPackage val = new ZPackage();
				val.Write(2);
				val.Write(_version);
				serverRPC.Invoke(RpcSyncRequest, new object[1] { val });
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(_id + ": sync request failed: " + ex.Message));
			}
		}

		private ZPackage BuildEnvelope()
		{
			//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)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(2);
			val.Write(_version);
			ZPackage val2 = new ZPackage();
			WritePayload(val2);
			val.WriteCompressed(val2);
			return val;
		}

		private void ReceiveSyncRequest(ZRpc rpc, ZPackage request)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer() || rpc == null || request == null)
			{
				return;
			}
			try
			{
				int num = request.ReadInt();
				string text = request.ReadString();
				if (num != 2 || !string.Equals(text, _version, StringComparison.Ordinal))
				{
					_log.LogWarning((object)(_id + ": version mismatch for a guest (guest " + text + " / protocol " + num + "; host " + _version + " / protocol " + 2 + "). Sending host values anyway."));
				}
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(_id + ": sync request read failed: " + ex.Message));
			}
			SendTo(rpc);
		}

		private void ReceiveSync(ZRpc rpc, ZPackage envelope)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || instance.IsServer() || rpc == null || envelope == null || instance.GetServerRPC() != rpc || ReadPayload == null)
			{
				return;
			}
			if (AcceptGate != null && !AcceptGate())
			{
				ClearClientState("declined locally");
				return;
			}
			try
			{
				int num = envelope.ReadInt();
				string text = envelope.ReadString();
				if (num != 2 || !string.Equals(text, _version, StringComparison.Ordinal))
				{
					ClearClientState("version mismatch");
					_log.LogWarning((object)(_id + ": host mod version differs (" + text + " vs " + _version + "). Using local values."));
				}
				else
				{
					ZPackage obj = envelope.ReadCompressedPackage();
					_clientSyncActive = true;
					ReadPayload(obj);
				}
			}
			catch (Exception ex)
			{
				_clientSyncActive = false;
				InvokeCleared("invalid payload");
				_log.LogWarning((object)(_id + ": sync failed: " + ex.Message));
			}
		}

		private void InvokeCleared(string reason)
		{
			if (Cleared == null)
			{
				return;
			}
			try
			{
				Cleared(reason);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(_id + ": cleared handler failed: " + ex.Message));
			}
		}

		public static int ReadCount(ZPackage pkg)
		{
			int num = pkg.ReadInt();
			if (num < 0 || num > 10000)
			{
				throw new InvalidOperationException("Invalid sync entry count: " + num);
			}
			return num;
		}
	}
}