Decompiled source of CardShopCoop v1.0.6

CardShopCoop.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CC;
using CardShopCoop.Net;
using CardShopCoop.Patches;
using CardShopCoop.Sync;
using CardShopCoop.UI;
using CardShopCoop.Util;
using HarmonyLib;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[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("CardShopCoop")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.8.0.0")]
[assembly: AssemblyInformationalVersion("0.8.0+7fe1bd680ed79bbda02583aeb3494b610d84fb83")]
[assembly: AssemblyProduct("CardShopCoop")]
[assembly: AssemblyTitle("CardShopCoop")]
[assembly: AssemblyVersion("0.8.0.0")]
namespace CardShopCoop
{
	public enum CoopRole
	{
		None,
		Host,
		Client
	}
	public class CoopCore : MonoBehaviour
	{
		private struct PendingCard
		{
			public bool IsAdd;

			public int Amount;

			public CardData Card;
		}

		public string StatusLine = "Not connected";

		public string ErrorLine = "";

		public string HostTimeLine = "";

		public string RegisterLine = "";

		public float RegisterLineTimer;

		private float _serveThrottle;

		public readonly Dictionary<int, string> PeerNames = new Dictionary<int, string>();

		private ICoopTransport _net;

		private readonly SteamLobby _steamLobby = new SteamLobby();

		private ulong _autoJoinSteamLobby;

		private readonly AvatarManager _avatars = new AvatarManager();

		private readonly WorldSync _world = new WorldSync();

		private readonly NpcSync _npcs = new NpcSync();

		private readonly CardShelfSync _cardShelves = new CardShelfSync();

		private readonly ObjMoveSync _objMoves = new ObjMoveSync();

		private readonly BoxSync _boxes = new BoxSync();

		private readonly PopulationSync _population = new PopulationSync();

		private readonly GradingSync _grading = new GradingSync();

		private readonly TradeServe _trades = new TradeServe();

		private readonly PlayTableSync _tables = new PlayTableSync();

		private readonly StaffSync _staff = new StaffSync();

		private readonly ShopStateSync _shopState = new ShopStateSync();

		private readonly SettingsSync _settings = new SettingsSync();

		private readonly MarketSync _market = new MarketSync();

		private readonly ReportSync _report = new ReportSync();

		private readonly ContainerSync _containers = new ContainerSync();

		private readonly TournamentSync _tournament = new TournamentSync();

		private readonly CardBoxSync _cardBoxes = new CardBoxSync();

		private string _lastShopNameSent;

		private float _shopNameTimer = -1f;

		private readonly RegisterMirror _registerMirror = new RegisterMirror();

		private float _npcSweepTimer = -1.3f;

		private float _regStateTimer = -0.17f;

		public string PromptLine = "";

		private readonly ConcurrentQueue<Action> _mainThread = new ConcurrentQueue<Action>();

		private CoopUI _ui;

		private MemoryStream _saveBuf;

		private int _saveExpected = -1;

		private byte[] _pendingSave;

		private MemoryStream _bundleBuf;

		private int _bundleExpected = -1;

		private int _hostSlot;

		private bool _worldRequested;

		private float _priceTimer = -0.45f;

		private int _lastPriceHash;

		private float _stateTimer;

		private float _pingTimer;

		private float _econTimer = -0.11f;

		private float _dayTimer = -0.9f;

		private Vector3 _lastPos;

		private bool _hasLastPos;

		private double _lastCoinSent = double.MinValue;

		private long _lastProgressSent = long.MinValue;

		private readonly HashSet<int> _gotStateFrom = new HashSet<int>();

		private bool _loggedEconLink;

		private bool _loggedTimeLink;

		private long _diagSent;

		private long _diagRecvStates;

		private float _diagTimer = -7.3f;

		private float _errLogCooldown;

		private static readonly FieldInfo FiTimeHour = typeof(LightManager).GetField("m_TimeHour", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FiTimeMin = typeof(LightManager).GetField("m_TimeMin", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FiTimeMinFloat = typeof(LightManager).GetField("m_TimeMinFloat", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FiHasDayEnded = typeof(LightManager).GetField("m_HasDayEnded", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo MiDayReset = typeof(LightManager).GetMethod("DelayUpdateEnv", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FiTimeOfDayIdx = typeof(LightManager).GetField("m_TImeOfDayIndex", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FiFinishLoading = typeof(LightManager).GetField("m_FinishLoading", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo MiLightInit = typeof(LightManager).GetMethod("Init", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo MiUpdateLightData = typeof(LightManager).GetMethod("UpdateLightTimeData", BindingFlags.Instance | BindingFlags.NonPublic);

		private float _lightSyncTimer = -2.3f;

		private LightManager _lightManager;

		private float _cardResyncTimer = -5.2f;

		private float _licenseSyncTimer = -3.7f;

		private double _lastLicenseBuyTime = -999.0;

		private string _lastLightJson;

		private float _lightHeal;

		private int _lastLicenseHash;

		private float _licenseHeal;

		private float _dt;

		private bool _syncActive;

		private Action _actNetPump;

		private Action _actAvatars;

		private Action _actWorld;

		private Action _actCardShelves;

		private Action _actObjMoves;

		private Action _actBoxes;

		private Action _actPopulation;

		private Action _actNpcPuppets;

		private Action _actRegisterMirror;

		private Action _actNpcSweep;

		private Action _actStateSend;

		private Action _actNpcCollect;

		private Action _actRegisterCollect;

		private Action _actModules;

		private CustomerManager _cmSweep;

		private bool _renamerHandled;

		private int _heldBoxFrame = -1;

		private object _heldBoxA;

		private object _heldBoxB;

		private object _heldBoxC;

		private readonly List<InMsg> _dispatchBuf = new List<InMsg>(64);

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

		private int _autoHostSlot = -1;

		private string _autoJoinIp;

		private int _autoPhase;

		private float _autoTimer;

		public string HostPassword = "";

		private string _joinPassword = "";

		public CSteamID LastFailedLobby = CSteamID.Nil;

		private readonly List<KeyValuePair<int, float>> _pendingKicks = new List<KeyValuePair<int, float>>();

		private readonly List<PendingCard> _pendingCardDeltas = new List<PendingCard>();

		private readonly List<KeyValuePair<CardData, float>> _pendingCardPrices = new List<KeyValuePair<CardData, float>>();

		private int _selfId = -1;

		private readonly HashSet<int> _relayIds = new HashSet<int>();

		private Transform _playerTf;

		private Transform _playerCamTf;

		private InteractionPlayerController _playerIpc;

		private static readonly FieldInfo FiHoldBox = AccessTools.Field(typeof(InteractionPlayerController), "m_CurrentHoldingBox");

		private static readonly FieldInfo FiHoldItemBox = AccessTools.Field(typeof(InteractionPlayerController), "m_CurrentHoldingItemBox");

		private static readonly FieldInfo FiHoldBoxShelf = AccessTools.Field(typeof(InteractionPlayerController), "m_CurrentHoldingBoxShelf");

		private static readonly FieldInfo FiHoldBoxCard = AccessTools.Field(typeof(InteractionPlayerController), "m_CurrentHoldingBoxCard");

		private static readonly FieldInfo FiHoldItemList = AccessTools.Field(typeof(InteractionPlayerController), "m_HoldItemList");

		private readonly List<int> _holdTypesBuf = new List<int>(6);

		private readonly List<CardData> _holdCardsBuf = new List<CardData>(4);

		private static readonly FieldInfo FiHoldCard3dList = AccessTools.Field(typeof(InteractionPlayerController), "m_CurrentHoldingCard3dList");

		private static readonly FieldInfo FiViewAlbum = AccessTools.Field(typeof(InteractionPlayerController), "m_IsViewCardAlbumMode");

		private static readonly FieldInfo FiPanelIndex = AccessTools.Field(typeof(RestockItemPanelUI), "m_Index");

		private static readonly FieldInfo FiPanelLicGrp = AccessTools.Field(typeof(RestockItemPanelUI), "m_LicenseUIGrp");

		private static readonly FieldInfo FiPanelUIGrp = AccessTools.Field(typeof(RestockItemPanelUI), "m_UIGrp");

		private bool _catalogSent;

		private float _catalogTimer;

		private int _lastCatalogSentHash;

		private readonly Dictionary<int, string> _rosterNames = new Dictionary<int, string>();

		private HashSet<int> _clientPriced = new HashSet<int>();

		private HashSet<int> _incomingPriced = new HashSet<int>();

		public static CoopCore Instance { get; private set; }

		public static CoopRole Role { get; private set; } = CoopRole.None;

		public bool IsSteamSession { get; private set; }

		public SteamLobby Lobby => _steamLobby;

		private void Guarded(string stage, Action action)
		{
			try
			{
				action();
			}
			catch (Exception arg)
			{
				if (_errLogCooldown <= 0f)
				{
					_errLogCooldown = 5f;
					CoopPlugin.Log.LogError((object)$"[{stage}] {arg}");
				}
			}
		}

		private unsafe void Awake()
		{
			Instance = this;
			_ui = new CoopUI();
			_world.OnLocalChanges = OnLocalWorldChanges;
			_cardShelves.OnLocalChanges = delegate(List<CardShelfSync.Entry> changes)
			{
				if (Role == CoopRole.Host)
				{
					Broadcast(MsgType.CardShelfDelta, delegate(BinaryWriter bw)
					{
						CardShelfSync.WriteEntries(bw, changes);
					});
				}
				else if (Role == CoopRole.Client)
				{
					Send(1, MsgType.CardShelfRequest, delegate(BinaryWriter bw)
					{
						CardShelfSync.WriteEntries(bw, changes);
					});
				}
			};
			_objMoves.OnLocalChanges = delegate(List<ObjMoveSync.Entry> changes)
			{
				if (Role == CoopRole.Host)
				{
					Broadcast(MsgType.ObjMoveDelta, delegate(BinaryWriter bw)
					{
						ObjMoveSync.WriteEntries(bw, changes);
					});
				}
				else if (Role == CoopRole.Client)
				{
					Send(1, MsgType.ObjMoveRequest, delegate(BinaryWriter bw)
					{
						ObjMoveSync.WriteEntries(bw, changes);
					});
				}
			};
			_population.OnHostSnapshot = delegate(List<List<PopulationSync.Entry>> all)
			{
				Broadcast(MsgType.PopState, delegate(BinaryWriter bw)
				{
					PopulationSync.Write(bw, all);
				});
			};
			_boxes.OnHostSnapshot = delegate(List<BoxSync.Entry> list)
			{
				Broadcast(MsgType.BoxState, delegate(BinaryWriter bw)
				{
					BoxSync.WriteEntries(bw, list);
				});
			};
			_boxes.OnClientChanges = delegate(List<BoxSync.Entry> list)
			{
				Send(1, MsgType.BoxRequest, delegate(BinaryWriter bw)
				{
					BoxSync.WriteEntries(bw, list);
				});
			};
			BoxSync.IsLocallyCarried = delegate(InteractablePackagingBox_Item box)
			{
				if ((Object)(object)_playerIpc == (Object)null || (Object)(object)box == (Object)null)
				{
					return false;
				}
				try
				{
					if (_heldBoxFrame != Time.frameCount)
					{
						_heldBoxFrame = Time.frameCount;
						_heldBoxA = FiHoldItemBox?.GetValue(_playerIpc);
						_heldBoxB = FiHoldBox?.GetValue(_playerIpc);
						_heldBoxC = FiHoldBoxCard?.GetValue(_playerIpc);
					}
					return _heldBoxA == box || _heldBoxB == box;
				}
				catch
				{
					return false;
				}
			};
			CardBoxSync.IsLocallyCarried = delegate(InteractablePackagingBox_Card box)
			{
				if ((Object)(object)_playerIpc == (Object)null || (Object)(object)box == (Object)null)
				{
					return false;
				}
				try
				{
					if (_heldBoxFrame != Time.frameCount)
					{
						_heldBoxFrame = Time.frameCount;
						_heldBoxA = FiHoldItemBox?.GetValue(_playerIpc);
						_heldBoxB = FiHoldBox?.GetValue(_playerIpc);
						_heldBoxC = FiHoldBoxCard?.GetValue(_playerIpc);
					}
					return _heldBoxC == box || _heldBoxB == box;
				}
				catch
				{
					return false;
				}
			};
			BoxSync.LocalBoxDestroyed = delegate(InteractablePackagingBox_Item box)
			{
				if (InGameLevel())
				{
					if (Role == CoopRole.Client)
					{
						_boxes.NotifyLocalDestroyed(box);
					}
					else if (Role == CoopRole.Host)
					{
						_boxes.HostNotifyLocalDestroyed();
					}
				}
			};
			_boxes.OnLocalRemoved = delegate(int idx, int type)
			{
				Send(1, MsgType.BoxRemoved, delegate(BinaryWriter bw)
				{
					bw.Write(idx);
					bw.Write(type);
				});
			};
			PopulationSync.OnClientStructureChanged = delegate(int kind)
			{
				if (Role == CoopRole.Client && (kind == 2 || kind == 3))
				{
					_cardShelves.InvalidateBaseline();
				}
			};
			_actNetPump = delegate
			{
				_net.PumpMainThread();
			};
			_actAvatars = delegate
			{
				AvatarManager.ViewCamera = _playerCamTf;
				_avatars.Tick(_dt);
			};
			_actWorld = delegate
			{
				_world.Tick(_dt, _syncActive);
			};
			_actCardShelves = delegate
			{
				_cardShelves.IsClientRole = Role == CoopRole.Client;
				_cardShelves.Tick(_dt, _syncActive);
			};
			_actObjMoves = delegate
			{
				_objMoves.Tick(_dt, _syncActive);
			};
			_actBoxes = delegate
			{
				if (Role == CoopRole.Host)
				{
					_boxes.HostTick(_dt, _syncActive);
				}
				else if (Role == CoopRole.Client)
				{
					_boxes.ClientTick(_dt, _syncActive);
				}
			};
			_actPopulation = delegate
			{
				if (Role == CoopRole.Host)
				{
					_population.HostTick(_dt, _syncActive);
				}
			};
			_actNpcPuppets = delegate
			{
				_npcs.TickPuppets(_dt, InGameLevel());
			};
			_actRegisterMirror = RegisterMirrorTick;
			_actNpcSweep = NpcSweepTick;
			_actStateSend = StateSendTick;
			_actNpcCollect = NpcCollectTick;
			_actRegisterCollect = RegisterCollectTick;
			_grading.SendOp = delegate(Action<BinaryWriter> w)
			{
				Send(1, MsgType.GradingOp, w);
			};
			_grading.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.GradingState, w);
			};
			_trades.SendOp = delegate(Action<BinaryWriter> w)
			{
				Send(1, MsgType.TradeOp, w);
			};
			_trades.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.TradeState, w);
			};
			_tables.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.TableState, w);
			};
			_staff.SendOp = delegate(Action<BinaryWriter> w)
			{
				Send(1, MsgType.StaffOp, w);
			};
			_staff.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.StaffState, w);
			};
			_shopState.SendOp = delegate(Action<BinaryWriter> w)
			{
				Send(1, MsgType.ShopOp, w);
			};
			_shopState.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.ShopState, w);
			};
			_settings.SendOp = delegate(Action<BinaryWriter> w)
			{
				Send(1, MsgType.SettingsOp, w);
			};
			_settings.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.SettingsState, w);
			};
			_market.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.MarketState, w);
			};
			_report.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.ReportState, w);
			};
			_containers.SendOp = delegate(Action<BinaryWriter> w)
			{
				Send(1, MsgType.ContainerOp, w);
			};
			_containers.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.ContainerState, w);
			};
			_tournament.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.TournamentState, w);
			};
			_cardBoxes.SendOp = delegate(Action<BinaryWriter> w)
			{
				Send(1, MsgType.CardBoxOp, w);
			};
			_cardBoxes.BroadcastState = delegate(Action<BinaryWriter> w)
			{
				Broadcast(MsgType.CardBoxState, w);
			};
			_actModules = ModulesTick;
			SceneManager.sceneLoaded += OnSceneLoaded;
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			for (int num = 0; num < commandLineArgs.Length; num++)
			{
				string text = commandLineArgs[num];
				ulong result2;
				if (text.StartsWith("-coopautohost=") && int.TryParse(text.Substring(14), out var result))
				{
					_autoHostSlot = result;
				}
				else if (text.StartsWith("-coopautojoin="))
				{
					_autoJoinIp = text.Substring(14);
				}
				else if (text == "+connect_lobby" && num + 1 < commandLineArgs.Length && ulong.TryParse(commandLineArgs[num + 1], out result2))
				{
					_autoJoinSteamLobby = result2;
				}
			}
			if (_autoHostSlot >= 0)
			{
				CoopPlugin.Log.LogInfo((object)$"AUTO: will load slot {_autoHostSlot} and host");
			}
			if (_autoJoinIp != null)
			{
				CoopPlugin.Log.LogInfo((object)("AUTO: will join " + _autoJoinIp));
			}
			if (_autoJoinSteamLobby != 0L)
			{
				CoopPlugin.Log.LogInfo((object)$"AUTO: will join Steam lobby {_autoJoinSteamLobby}");
			}
			_steamLobby.Init();
			_steamLobby.OnError = delegate(string err)
			{
				ErrorLine = err;
				CoopPlugin.Log.LogWarning((object)err);
			};
			_steamLobby.OnLobbyCreated = delegate(CSteamID lobby)
			{
				//IL_002b: 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_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				if (_net is SteamTransport steamTransport)
				{
					steamTransport.LobbyId = lobby;
				}
				StatusLine = "Hosting via Steam - click 'Invite friend'";
				ManualLogSource log = CoopPlugin.Log;
				CSteamID val = lobby;
				log.LogInfo((object)("steam: lobby live " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString()));
			};
			_steamLobby.OnEnteredLobby = delegate(CSteamID owner)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				if (Role == CoopRole.Client && _net is SteamTransport steamTransport)
				{
					steamTransport.LobbyId = _steamLobby.LobbyId;
					steamTransport.ConnectToHost(owner);
					StatusLine = "Connected via Steam - requesting world...";
					SendHello();
				}
			};
			_steamLobby.OnInviteAccepted = delegate(CSteamID lobby)
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				ManualLogSource log = CoopPlugin.Log;
				CSteamID val = lobby;
				log.LogInfo((object)("steam: invite accepted -> lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString()));
				JoinSteam(lobby);
			};
			CEventManager.AddListener<CEventPlayer_OnOpenCardPack>((EventDelegate<CEventPlayer_OnOpenCardPack>)OnLocalPackOpened);
		}

		public void JoinSteam(CSteamID lobby, string password = "")
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			ErrorLine = "";
			if (Role != CoopRole.None)
			{
				ErrorLine = "Already in a session.";
				return;
			}
			if (InGameLevel())
			{
				ErrorLine = "Go to the main menu first, then accept the invite again.";
				return;
			}
			if (!_steamLobby.SteamAvailable())
			{
				ErrorLine = "Steam isn't running.";
				return;
			}
			Role = CoopRole.Client;
			IsSteamSession = true;
			_joinPassword = password ?? "";
			LastFailedLobby = lobby;
			_net = new SteamTransport(isHost: false)
			{
				KeepaliveFrame = Msg.Build(MsgType.Ping)
			};
			StatusLine = "Joining Steam lobby...";
			_steamLobby.Join(lobby);
		}

		public void StartHostingSteam(bool isPublic, string lobbyName, string password)
		{
			ErrorLine = "";
			if (Role != CoopRole.None)
			{
				ErrorLine = "Already in a session.";
				return;
			}
			if (!InGameLevel())
			{
				ErrorLine = "Load your shop first, then host.";
				return;
			}
			if (!_steamLobby.SteamAvailable())
			{
				ErrorLine = "Steam isn't running - use LAN instead.";
				return;
			}
			Role = CoopRole.Host;
			IsSteamSession = true;
			HostPassword = password ?? "";
			_net = new SteamTransport(isHost: true)
			{
				KeepaliveFrame = Msg.Build(MsgType.Ping)
			};
			StatusLine = "Creating Steam lobby...";
			_steamLobby.Host(isPublic, lobbyName, HostPassword.Length > 0);
		}

		public void OpenSteamInvite()
		{
			_steamLobby.OpenInviteDialog();
		}

		private void SendHello()
		{
			Send(1, MsgType.Hello, delegate(BinaryWriter bw)
			{
				bw.Write("1.0.6");
				bw.Write(CoopPlugin.PlayerName.Value);
				bw.Write(_joinPassword ?? "");
				bw.Write(ModParity.PluginHash());
				bw.Write(ModParity.EnumHash());
			});
		}

		private void RejectConn(int connId, string reason)
		{
			CoopPlugin.Log.LogWarning((object)$"rejected connection {connId}: {reason}");
			Send(connId, MsgType.Bye, delegate(BinaryWriter bw)
			{
				bw.Write(reason);
			});
			_pendingKicks.Add(new KeyValuePair<int, float>(connId, 1.5f));
		}

		private static void ReadHoldPayload(BinaryReader br, byte hold, out List<int> types, out List<CardData> cards)
		{
			types = null;
			cards = null;
			int num = br.ReadByte();
			if (num == 0)
			{
				return;
			}
			if (hold == 3)
			{
				cards = new List<CardData>(num);
				for (int i = 0; i < num; i++)
				{
					cards.Add(Msg.ReadCard(br));
				}
			}
			else
			{
				types = new List<int>(num);
				for (int j = 0; j < num; j++)
				{
					types.Add(br.ReadInt32());
				}
			}
		}

		private static void WriteHoldPayload(BinaryWriter bw, byte hold, List<int> types, List<CardData> cards)
		{
			if (hold == 3)
			{
				bw.Write((byte)(cards?.Count ?? 0));
				if (cards == null)
				{
					return;
				}
				{
					foreach (CardData card in cards)
					{
						Msg.WriteCard(bw, card);
					}
					return;
				}
			}
			bw.Write((byte)(types?.Count ?? 0));
			if (types == null)
			{
				return;
			}
			foreach (int type in types)
			{
				bw.Write(type);
			}
		}

		private static void ApplyCardDelta(bool isAdd, int amount, CardData card)
		{
			GamePatches.ApplyingRemoteCards = true;
			try
			{
				if (isAdd)
				{
					CPlayerData.AddCard(card, amount);
				}
				else
				{
					CPlayerData.ReduceCard(card, amount);
				}
			}
			finally
			{
				GamePatches.ApplyingRemoteCards = false;
			}
		}

		private void FlushPendingCardWork()
		{
			if (!InGameLevel() || (_pendingCardDeltas.Count == 0 && _pendingCardPrices.Count == 0))
			{
				return;
			}
			Guarded("pending-cards", delegate
			{
				foreach (PendingCard pendingCardDelta in _pendingCardDeltas)
				{
					ApplyCardDelta(pendingCardDelta.IsAdd, pendingCardDelta.Amount, pendingCardDelta.Card);
				}
				if (_pendingCardDeltas.Count > 0)
				{
					CoopPlugin.Log.LogInfo((object)$"applied {_pendingCardDeltas.Count} card change(s) held during loading");
				}
				_pendingCardDeltas.Clear();
				GamePatches.ApplyingRemotePrice = true;
				try
				{
					foreach (KeyValuePair<CardData, float> pendingCardPrice in _pendingCardPrices)
					{
						CPlayerData.SetCardPrice(pendingCardPrice.Key, pendingCardPrice.Value);
					}
				}
				finally
				{
					GamePatches.ApplyingRemotePrice = false;
				}
				_pendingCardPrices.Clear();
			});
		}

		private void RelayTagToOthers(int senderConn, byte kind, int extra = -1)
		{
			if (Role != CoopRole.Host || _net == null || _net.ConnectionCount <= 1)
			{
				return;
			}
			byte[] frame = Msg.Build(MsgType.RelayTag, delegate(BinaryWriter bw)
			{
				bw.Write((byte)senderConn);
				bw.Write(kind);
				bw.Write(extra);
			});
			foreach (int item in _net.ConnIds())
			{
				if (item != senderConn)
				{
					_net.Send(item, frame);
				}
			}
		}

		private void BroadcastRoster()
		{
			if (Role != CoopRole.Host)
			{
				return;
			}
			List<KeyValuePair<int, string>> entries = new List<KeyValuePair<int, string>>(PeerNames);
			Broadcast(MsgType.Roster, delegate(BinaryWriter bw)
			{
				bw.Write((byte)entries.Count);
				foreach (KeyValuePair<int, string> item in entries)
				{
					bw.Write((byte)item.Key);
					bw.Write(item.Value);
				}
			});
		}

		private void OnLocalPackOpened(CEventPlayer_OnOpenCardPack evt)
		{
			if (Role != CoopRole.None && _net != null && _net.ConnectionCount > 0)
			{
				Broadcast(MsgType.Activity, delegate(BinaryWriter bw)
				{
					bw.Write((byte)1);
					bw.Write(evt.m_PackIndex);
				});
			}
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			CEventManager.RemoveListener<CEventPlayer_OnOpenCardPack>((EventDelegate<CEventPlayer_OnOpenCardPack>)OnLocalPackOpened);
			Shutdown("plugin unloaded");
		}

		private void OnApplicationQuit()
		{
			Shutdown("game closed");
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			_avatars.Clear();
			_world.Reset();
			_npcs.Reset();
			_cardShelves.Reset();
			_objMoves.Reset();
			_boxes.Reset();
			_population.Reset();
			_registerMirror.Reset();
			ModulesReset();
			PromptLine = "";
			_lightManager = null;
			_cmSweep = null;
			_renamerHandled = false;
			_catalogSent = false;
			_playerTf = null;
			_playerCamTf = null;
			_playerIpc = null;
			if (((Scene)(ref scene)).name == "Title" && Role == CoopRole.Client && _net != null)
			{
				Shutdown("left the session");
			}
		}

		private bool InGameLevel()
		{
			CGameManager instance = CSingleton<CGameManager>.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				return instance.m_IsGameLevel;
			}
			return false;
		}

		private void ModulesTick()
		{
			bool flag = InGameLevel();
			if (Role == CoopRole.Host)
			{
				_grading.HostTick(_dt, flag);
				_trades.HostTick(_dt, flag);
				_tables.HostTick(_dt, flag);
				_staff.HostTick(_dt, flag);
				_shopState.HostTick(_dt, flag);
				_settings.HostTick(_dt, flag);
				_market.HostTick(_dt, flag);
				_report.HostTick(_dt, flag);
				_containers.HostTick(_dt, flag);
				_tournament.HostTick(_dt, flag);
				_cardBoxes.HostTick(_dt, flag);
			}
			else
			{
				if (Role != CoopRole.Client)
				{
					return;
				}
				_trades.ClientTick(_dt, flag);
				_cardBoxes.ClientTick(_dt, flag);
				_catalogTimer += _dt;
				if (flag && (_catalogTimer >= 45f || !_catalogSent))
				{
					_catalogTimer = 0f;
					_catalogSent = true;
					int num = LocalCatalogHash();
					if (num != _lastCatalogSentHash)
					{
						_lastCatalogSentHash = num;
						SendCatalogDigest();
					}
				}
			}
		}

		private static int LocalCatalogHash()
		{
			//IL_002a: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected I4, but got Unknown
			try
			{
				List<RestockData> restockDataList = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList;
				int num = 17;
				foreach (RestockData item in restockDataList)
				{
					if (item != null)
					{
						num = num * 31 + ((item.itemType << 1) | item.isBigBox);
					}
				}
				return num;
			}
			catch
			{
				return 0;
			}
		}

		private void ModulesReset()
		{
			_grading.Reset();
			_trades.Reset();
			_tables.Reset();
			_staff.Reset();
			_shopState.Reset();
			_settings.Reset();
			_market.Reset();
			_report.Reset();
			_containers.Reset();
			_tournament.Reset();
			_cardBoxes.Reset();
		}

		private void ModulesForceResend()
		{
			_grading.ForceResend();
			_trades.ForceResend();
			_tables.ForceResend();
			_staff.ForceResend();
			_shopState.ForceResend();
			_settings.ForceResend();
			_market.ForceResend();
			_report.ForceResend();
			_containers.ForceResend();
			_tournament.ForceResend();
			_cardBoxes.ForceResend();
		}

		private void RegisterMirrorTick()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			_registerMirror.Tick(_dt);
			_regStateTimer += _dt;
			if (_regStateTimer >= 0.5f && InGameLevel())
			{
				_regStateTimer -= 0.5f;
				Transform val = ResolvePlayer();
				int nearestCounter = (((Object)(object)val != (Object)null) ? RegisterServe.FindNearestCounter(val.position, CoopPlugin.ServeReach.Value, quiet: true) : (-1));
				PromptLine = _registerMirror.PromptFor(nearestCounter) ?? _trades.PromptFor(nearestCounter) ?? "";
			}
		}

		private void NpcSweepTick()
		{
			if (!_renamerHandled)
			{
				_renamerHandled = true;
				ShopRenamer val = Object.FindObjectOfType<ShopRenamer>();
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(false);
					CoopPlugin.Log.LogInfo((object)"disabled shop-renamer trigger (host names the shop)");
				}
			}
			if ((Object)(object)_cmSweep == (Object)null)
			{
				_cmSweep = Object.FindObjectOfType<CustomerManager>();
			}
			if ((Object)(object)_cmSweep != (Object)null)
			{
				List<Customer> customerList = _cmSweep.GetCustomerList();
				for (int i = 0; i < customerList.Count; i++)
				{
					if ((Object)(object)customerList[i] != (Object)null && ((Component)customerList[i]).gameObject.activeSelf)
					{
						((Component)customerList[i]).gameObject.SetActive(false);
					}
				}
			}
			List<Worker> workerList = WorkerManager.GetWorkerList();
			if (workerList == null)
			{
				return;
			}
			for (int j = 0; j < workerList.Count; j++)
			{
				if ((Object)(object)workerList[j] != (Object)null && ((Component)workerList[j]).gameObject.activeSelf)
				{
					((Component)workerList[j]).gameObject.SetActive(false);
				}
			}
		}

		private void StateSendTick()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			float num = 1f / Mathf.Clamp(CoopPlugin.SendRateHz.Value, 4f, 30f);
			Transform val = (InGameLevel() ? ResolvePlayer() : null);
			if (_stateTimer < num || (Object)(object)val == (Object)null)
			{
				return;
			}
			Vector3 pos = val.position;
			float speed = 0f;
			if (_hasLastPos)
			{
				Vector3 val2 = pos - _lastPos;
				val2.y = 0f;
				speed = Mathf.Clamp(((Vector3)(ref val2)).magnitude / _stateTimer, 0f, 6f);
			}
			_lastPos = pos;
			_hasLastPos = true;
			float yaw = (((Object)(object)_playerCamTf != (Object)null) ? _playerCamTf.eulerAngles.y : (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform.eulerAngles.y : val.eulerAngles.y));
			byte hold = ComputeHoldState();
			BroadcastTransient(MsgType.PlayerState, delegate(BinaryWriter bw)
			{
				bw.Write(pos.x);
				bw.Write(pos.y);
				bw.Write(pos.z);
				bw.Write(yaw);
				bw.Write(speed);
				bw.Write(hold);
				if (hold == 3)
				{
					bw.Write((byte)_holdCardsBuf.Count);
					{
						foreach (CardData item in _holdCardsBuf)
						{
							Msg.WriteCard(bw, item);
						}
						return;
					}
				}
				bw.Write((byte)_holdTypesBuf.Count);
				foreach (int item2 in _holdTypesBuf)
				{
					bw.Write(item2);
				}
			});
			_diagSent++;
			_stateTimer = 0f;
		}

		private void NpcCollectTick()
		{
			List<byte[]> list = _npcs.HostCollect(_dt);
			if (list == null)
			{
				return;
			}
			for (int i = 0; i < list.Count; i++)
			{
				byte[] c = list[i];
				BroadcastTransient(MsgType.NpcState, delegate(BinaryWriter bw)
				{
					bw.Write(c);
				});
			}
		}

		private void RegisterCollectTick()
		{
			_regStateTimer += _dt;
			if (!(_regStateTimer >= 0.5f))
			{
				return;
			}
			_regStateTimer -= 0.5f;
			byte[] batch = RegisterServe.CollectStates();
			if (batch != null)
			{
				BroadcastTransient(MsgType.RegisterState, delegate(BinaryWriter bw)
				{
					bw.Write(batch);
				});
			}
		}

		private Transform ResolvePlayer()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_playerTf != (Object)null)
			{
				return _playerTf;
			}
			InteractionPlayerController val = InteractionPlayerController.m_Instance;
			if ((Object)(object)val == (Object)null)
			{
				val = Object.FindObjectOfType<InteractionPlayerController>();
			}
			if ((Object)(object)val != (Object)null)
			{
				_playerIpc = val;
				_playerTf = (((Object)(object)val.m_WalkerCtrl != (Object)null) ? ((Component)val.m_WalkerCtrl).transform : ((Component)val).transform);
				_playerCamTf = (((Object)(object)val.m_Cam != (Object)null) ? ((Component)val.m_Cam).transform : null);
				CoopPlugin.Log.LogInfo((object)string.Format("Player body resolved: {0} at {1}, cam={2}", ((Object)_playerTf).name, _playerTf.position, ((Object)(object)_playerCamTf != (Object)null) ? ((Object)_playerCamTf).name : "none"));
			}
			return _playerTf;
		}

		private byte ComputeHoldState()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected I4, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected I4, but got Unknown
			_holdTypesBuf.Clear();
			_holdCardsBuf.Clear();
			if ((Object)(object)_playerIpc == (Object)null)
			{
				return 0;
			}
			try
			{
				if (IsAlive(FiHoldBox) || IsAlive(FiHoldItemBox) || IsAlive(FiHoldBoxShelf) || IsAlive(FiHoldBoxCard))
				{
					object? obj = FiHoldItemBox?.GetValue(_playerIpc);
					InteractablePackagingBox_Item val = (InteractablePackagingBox_Item)((obj is InteractablePackagingBox_Item) ? obj : null);
					if (val != null && (Object)(object)val != (Object)null)
					{
						_holdTypesBuf.Add(val.m_IsBigBox ? 1 : 0);
						try
						{
							_holdTypesBuf.Add((int)val.m_ItemCompartment.GetItemType());
						}
						catch
						{
							_holdTypesBuf.Add(0);
						}
					}
					return 1;
				}
				if (FiHoldItemList?.GetValue(_playerIpc) is List<Item> { Count: >0 } list)
				{
					for (int i = 0; i < list.Count; i++)
					{
						if (_holdTypesBuf.Count >= 6)
						{
							break;
						}
						if ((Object)(object)list[i] != (Object)null)
						{
							_holdTypesBuf.Add((int)list[i].GetItemType());
						}
					}
					return 2;
				}
				if (FiHoldCard3dList?.GetValue(_playerIpc) is List<InteractableCard3d> { Count: >0 } list2)
				{
					for (int j = 0; j < list2.Count; j++)
					{
						if (_holdCardsBuf.Count >= 4)
						{
							break;
						}
						InteractableCard3d val2 = list2[j];
						if ((Object)(object)val2 != (Object)null && (Object)(object)val2.m_Card3dUI != (Object)null && (Object)(object)val2.m_Card3dUI.m_CardUI != (Object)null)
						{
							_holdCardsBuf.Add(val2.m_Card3dUI.m_CardUI.GetCardData());
						}
					}
					if (_holdCardsBuf.Count > 0)
					{
						return 3;
					}
				}
				object obj3 = FiViewAlbum?.GetValue(_playerIpc);
				bool flag = default(bool);
				int num;
				if (obj3 is bool)
				{
					flag = (bool)obj3;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return 4;
				}
			}
			catch
			{
			}
			return 0;
		}

		private bool IsAlive(FieldInfo fi)
		{
			object? obj = fi?.GetValue(_playerIpc);
			return (Object)((obj is Object) ? obj : null) != (Object)null;
		}

		public void StartHosting()
		{
			ErrorLine = "";
			if (Role != CoopRole.None)
			{
				ErrorLine = "Already in a session.";
				return;
			}
			if (!InGameLevel())
			{
				ErrorLine = "Load your shop first, then host.";
				return;
			}
			try
			{
				Transport transport = new Transport
				{
					KeepaliveFrame = Msg.Build(MsgType.Ping)
				};
				transport.StartHost(CoopPlugin.Port.Value);
				_net = transport;
				Role = CoopRole.Host;
				StatusLine = "Hosting - waiting for a player...";
				CoopPlugin.Log.LogInfo((object)$"Hosting on port {CoopPlugin.Port.Value}");
			}
			catch (Exception ex)
			{
				ErrorLine = "Could not host: " + ex.Message;
				_net?.Stop();
				_net = null;
				Role = CoopRole.None;
			}
		}

		public void Join(string ip)
		{
			ErrorLine = "";
			if (Role != CoopRole.None)
			{
				ErrorLine = "Already in a session.";
				return;
			}
			if (InGameLevel())
			{
				ErrorLine = "Join from the main menu (Title screen).";
				return;
			}
			ip = (ip ?? "").Trim();
			if (ip.Length == 0)
			{
				ErrorLine = "Enter the host's IP address.";
				return;
			}
			CoopPlugin.LastJoinIP.Value = ip;
			Role = CoopRole.Client;
			StatusLine = "Connecting to " + ip + "...";
			Transport net = new Transport
			{
				KeepaliveFrame = Msg.Build(MsgType.Ping)
			};
			_net = net;
			int port = CoopPlugin.Port.Value;
			Thread thread = new Thread((ThreadStart)delegate
			{
				try
				{
					net.StartClient(ip, port);
					_mainThread.Enqueue(delegate
					{
						StatusLine = "Connected - requesting world...";
						SendHello();
					});
				}
				catch (Exception ex)
				{
					Exception ex2 = ex;
					Exception e = ex2;
					_mainThread.Enqueue(delegate
					{
						ErrorLine = "Could not connect: " + e.Message;
						Shutdown(null);
					});
				}
			});
			thread.IsBackground = true;
			thread.Name = "CoopConnect";
			thread.Start();
		}

		public void Disconnect()
		{
			Shutdown("disconnected");
		}

		public void SendEmote()
		{
			if (_net != null && Role != CoopRole.None)
			{
				Broadcast(MsgType.Emote, delegate(BinaryWriter bw)
				{
					bw.Write((byte)1);
				});
			}
		}

		public void ForwardContribution(byte kind, float value)
		{
			if (Role == CoopRole.Client && _net != null)
			{
				Send(1, MsgType.EconContrib, delegate(BinaryWriter bw)
				{
					bw.Write(kind);
					bw.Write(value);
				});
			}
		}

		public void ForwardCardDelta(CardData card, int amount, bool isAdd)
		{
			if (Role != CoopRole.None && _net != null && card != null && amount > 0)
			{
				Broadcast(MsgType.CardDelta, delegate(BinaryWriter bw)
				{
					bw.Write(isAdd);
					bw.Write(amount);
					Msg.WriteCard(bw, card);
				});
			}
		}

		public void ForwardOrder(int restockIndex, int count)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (Role != CoopRole.Client || _net == null)
			{
				return;
			}
			RestockData rd = null;
			try
			{
				rd = InventoryBase.GetRestockData(restockIndex);
			}
			catch
			{
			}
			if (rd == null)
			{
				CoopPlugin.Log.LogWarning((object)$"order: bad restock index {restockIndex}");
				return;
			}
			float lineCost = 0f;
			try
			{
				lineCost = CPlayerData.GetItemCost(rd.itemType) * (float)RestockManager.GetMaxItemCountInBox(rd.itemType, rd.isBigBox) * (float)count;
			}
			catch
			{
			}
			Send(1, MsgType.OrderRequest, delegate(BinaryWriter bw)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected I4, but got Unknown
				bw.Write((int)rd.itemType);
				bw.Write(rd.isBigBox);
				bw.Write(rd.name ?? "");
				bw.Write(count);
				bw.Write(lineCost);
			});
		}

		public void ForwardLicense(int restockIndex)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected I4, but got Unknown
			if (Role == CoopRole.None || _net == null)
			{
				return;
			}
			RestockData val = null;
			try
			{
				val = InventoryBase.GetRestockData(restockIndex);
			}
			catch
			{
			}
			if (val == null)
			{
				return;
			}
			_lastLicenseBuyTime = Time.realtimeSinceStartupAsDouble;
			int itemType = (int)val.itemType;
			bool isBig = val.isBigBox;
			string rdName = val.name ?? "";
			if (Role == CoopRole.Host)
			{
				Broadcast(MsgType.LicenseUnlock, delegate(BinaryWriter bw)
				{
					bw.Write(itemType);
					bw.Write(isBig);
					bw.Write(rdName);
				});
			}
			else
			{
				Send(1, MsgType.LicenseUnlock, delegate(BinaryWriter bw)
				{
					bw.Write(itemType);
					bw.Write(isBig);
					bw.Write(rdName);
				});
			}
		}

		private static int ResolveRestockIndex(int itemType, bool isBig, string name, out bool sizeDiffers)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Invalid comparison between Unknown and I4
			sizeDiffers = false;
			try
			{
				List<RestockData> restockDataList = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList;
				for (int i = 0; i < restockDataList.Count; i++)
				{
					if (restockDataList[i] != null && (int)restockDataList[i].itemType == itemType && restockDataList[i].isBigBox == isBig)
					{
						return i;
					}
				}
				if (!string.IsNullOrEmpty(name))
				{
					for (int j = 0; j < restockDataList.Count; j++)
					{
						if (restockDataList[j] != null && restockDataList[j].name == name && restockDataList[j].isBigBox == isBig)
						{
							return j;
						}
					}
				}
				sizeDiffers = true;
				for (int k = 0; k < restockDataList.Count; k++)
				{
					if (restockDataList[k] != null && (int)restockDataList[k].itemType == itemType)
					{
						return k;
					}
				}
				if (!string.IsNullOrEmpty(name))
				{
					for (int l = 0; l < restockDataList.Count; l++)
					{
						if (restockDataList[l] != null && restockDataList[l].name == name)
						{
							return l;
						}
					}
				}
			}
			catch
			{
			}
			return -1;
		}

		private bool ApplyLicenseUnlock(int itemType, bool isBig, string name)
		{
			bool sizeDiffers;
			int num = ResolveRestockIndex(itemType, isBig, name, out sizeDiffers);
			if (num < 0)
			{
				CoopPlugin.Log.LogWarning((object)$"license unlock: no local product for type {itemType} big={isBig} '{name}'");
				return false;
			}
			if (CPlayerData.GetIsItemLicenseUnlocked(num))
			{
				return true;
			}
			GamePatches.ApplyingRemoteLicense = true;
			try
			{
				CPlayerData.SetUnlockItemLicense(num);
				try
				{
					AchievementManager.OnItemLicenseUnlocked((EItemType)itemType);
				}
				catch
				{
				}
				try
				{
					GameInstance.m_IsItemLicenseUnlocked = true;
				}
				catch
				{
				}
				try
				{
					if (itemType == 1)
					{
						TutorialManager.AddTaskValue((ETutorialTaskCondition)14, 1f);
					}
				}
				catch
				{
				}
			}
			finally
			{
				GamePatches.ApplyingRemoteLicense = false;
			}
			RefreshLicensePanels();
			CoopPlugin.Log.LogInfo((object)$"license unlocked by partner: {(object)(EItemType)itemType} big={isBig}");
			return true;
		}

		private static void RefreshLicensePanels()
		{
			try
			{
				RestockItemPanelUI[] array = Object.FindObjectsOfType<RestockItemPanelUI>();
				foreach (RestockItemPanelUI obj in array)
				{
					if (FiPanelIndex?.GetValue(obj) is int num && num >= 0 && num < CPlayerData.m_IsItemLicenseUnlocked.Count && CPlayerData.GetIsItemLicenseUnlocked(num))
					{
						object? obj2 = FiPanelLicGrp?.GetValue(obj);
						object? obj3 = ((obj2 is GameObject) ? obj2 : null);
						if (obj3 != null)
						{
							((GameObject)obj3).SetActive(false);
						}
						object? obj4 = FiPanelUIGrp?.GetValue(obj);
						object? obj5 = ((obj4 is GameObject) ? obj4 : null);
						if (obj5 != null)
						{
							((GameObject)obj5).SetActive(true);
						}
					}
				}
			}
			catch
			{
			}
		}

		private void SendCatalogDigest()
		{
			try
			{
				List<RestockData> restockDataList = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList;
				List<RestockData> entries = new List<RestockData>(restockDataList.Count);
				foreach (RestockData item in restockDataList)
				{
					if (item != null)
					{
						entries.Add(item);
					}
				}
				Send(1, MsgType.CatalogDigest, delegate(BinaryWriter bw)
				{
					//IL_002f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0039: Expected I4, but got Unknown
					int num = Mathf.Min(entries.Count, 65535);
					bw.Write((ushort)num);
					for (int i = 0; i < num; i++)
					{
						bw.Write((int)entries[i].itemType);
						bw.Write(entries[i].isBigBox);
						bw.Write(Fnv(entries[i].name ?? ""));
					}
				});
			}
			catch (Exception ex)
			{
				CoopPlugin.Log.LogWarning((object)("catalog digest: " + ex.Message));
			}
		}

		private void CompareCatalogs(BinaryReader br, int connId)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected I4, but got Unknown
			int num = br.ReadUInt16();
			HashSet<long> hashSet = new HashSet<long>();
			for (int i = 0; i < num; i++)
			{
				int type = br.ReadInt32();
				bool big = br.ReadBoolean();
				int nameHash = br.ReadInt32();
				hashSet.Add(CatalogKey(type, big, nameHash));
			}
			List<RestockData> restockDataList = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList;
			int num2 = 0;
			int num3 = 0;
			List<string> list = new List<string>();
			foreach (RestockData item in restockDataList)
			{
				if (item == null)
				{
					continue;
				}
				if (hashSet.Contains(CatalogKey((int)item.itemType, item.isBigBox, Fnv(item.name ?? ""))))
				{
					num3++;
					continue;
				}
				num2++;
				if (list.Count < 6)
				{
					list.Add(item.name);
				}
			}
			int num4 = hashSet.Count - num3;
			if (num2 == 0 && num4 == 0)
			{
				CoopPlugin.Log.LogInfo((object)$"catalog check: identical ({num3} products)");
				return;
			}
			string value;
			string arg = (PeerNames.TryGetValue(connId, out value) ? value : "joiner");
			string summary = $"heads-up: product catalogs differ ({num2} only on host, {num4} only on {arg}) - mismatched items can't be ordered; match your content packs";
			CoopPlugin.Log.LogWarning((object)("catalog check: " + summary + ((list.Count > 0) ? (" | host-only e.g.: " + string.Join(" / ", list.ToArray())) : "")));
			RegisterLine = summary;
			RegisterLineTimer = 10f;
			Send(connId, MsgType.Toast, delegate(BinaryWriter bw)
			{
				bw.Write(summary);
			});
		}

		private void LogCatalogCandidates(string name)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected I4, but got Unknown
			try
			{
				if (string.IsNullOrEmpty(name))
				{
					return;
				}
				string text = name.Split(new char[1] { ' ' })[0];
				List<RestockData> restockDataList = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList;
				List<string> list = new List<string>();
				foreach (RestockData item in restockDataList)
				{
					if (list.Count < 8)
					{
						if (item != null && item.name != null && item.name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
						{
							list.Add($"{item.name} (type {(int)item.itemType}, big={item.isBigBox})");
						}
						continue;
					}
					break;
				}
				CoopPlugin.Log.LogInfo((object)((list.Count > 0) ? ("similar host entries: " + string.Join(" | ", list.ToArray())) : ("no host entries resembling '" + text + "'")));
			}
			catch
			{
			}
		}

		private static long CatalogKey(int type, bool big, int nameHash)
		{
			return (long)((ulong)((long)type << 33) ^ ((ulong)(uint)nameHash << 1)) ^ (long)(big ? 1 : 0);
		}

		private static int Fnv(string s)
		{
			uint num = 2166136261u;
			for (int i = 0; i < s.Length; i++)
			{
				num ^= s[i];
				num *= 16777619;
			}
			return (int)num;
		}

		public void ForwardFurniture(int objType, Vector3 pos, Quaternion rot)
		{
			//IL_000e: 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_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)
			if (Role == CoopRole.Client && _net != null)
			{
				Send(1, MsgType.FurnitureOrder, delegate(BinaryWriter bw)
				{
					bw.Write(objType);
					bw.Write(pos.x);
					bw.Write(pos.y);
					bw.Write(pos.z);
					bw.Write(rot.x);
					bw.Write(rot.y);
					bw.Write(rot.z);
					bw.Write(rot.w);
				});
			}
		}

		public void ForwardItemPrice(EItemType itemType, float price)
		{
			//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)
			if (Role == CoopRole.Client && _net != null)
			{
				Send(1, MsgType.ItemPriceContrib, delegate(BinaryWriter bw)
				{
					//IL_0002: Unknown result type (might be due to invalid IL or missing references)
					//IL_000c: Expected I4, but got Unknown
					bw.Write((int)itemType);
					bw.Write(price);
				});
			}
		}

		public void ForwardCardPrice(CardData card, float price)
		{
			if (Role != CoopRole.None && _net != null && card != null)
			{
				Broadcast(MsgType.CardPriceSet, delegate(BinaryWriter bw)
				{
					Msg.WriteCard(bw, card);
					bw.Write(price);
				});
			}
		}

		private void Shutdown(string reason)
		{
			if (_net != null)
			{
				try
				{
					Broadcast(MsgType.Bye, null);
				}
				catch
				{
				}
				_net.Stop();
				_net = null;
			}
			_avatars.Clear();
			PeerNames.Clear();
			_saveBuf = null;
			_saveExpected = -1;
			_pendingSave = null;
			_bundleBuf = null;
			_bundleExpected = -1;
			_worldRequested = false;
			_hasLastPos = false;
			_lastCoinSent = double.MinValue;
			_lastPriceHash = 0;
			_lastProgressSent = long.MinValue;
			_world.Reset();
			_npcs.Reset();
			_cardShelves.Reset();
			_objMoves.Reset();
			_boxes.Reset();
			_population.Reset();
			_registerMirror.Reset();
			ModulesReset();
			PromptLine = "";
			_lastShopNameSent = null;
			_steamLobby.Leave();
			IsSteamSession = false;
			HostPassword = "";
			_joinPassword = "";
			_selfId = -1;
			_relayIds.Clear();
			_pendingKicks.Clear();
			Application.runInBackground = false;
			Role = CoopRole.None;
			if (reason != null)
			{
				StatusLine = "Not connected (" + reason + ")";
				CoopPlugin.Log.LogInfo((object)("Session ended: " + reason));
			}
		}

		private void Send(int connId, MsgType type, Action<BinaryWriter> write)
		{
			_net?.Send(connId, Msg.Build(type, write));
		}

		private void Broadcast(MsgType type, Action<BinaryWriter> write)
		{
			_net?.Broadcast(Msg.Build(type, write));
		}

		private void BroadcastTransient(MsgType type, Action<BinaryWriter> write)
		{
			_net?.BroadcastTransient(Msg.Build(type, write));
		}

		private void Update()
		{
			//IL_0042: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0600: Unknown result type (might be due to invalid IL or missing references)
			//IL_0611: Unknown result type (might be due to invalid IL or missing references)
			//IL_0622: Unknown result type (might be due to invalid IL or missing references)
			Action result;
			while (_mainThread.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception ex)
				{
					CoopPlugin.Log.LogError((object)ex);
				}
			}
			AutoTick(Time.deltaTime);
			if (Input.GetKeyDown(CoopPlugin.UiToggleKey.Value))
			{
				_ui.Visible = !_ui.Visible;
			}
			if (Role != CoopRole.None && Input.GetKeyDown(CoopPlugin.EmoteKey.Value) && !CoopUI.TextFieldFocused)
			{
				SendEmote();
			}
			if (_serveThrottle > 0f)
			{
				_serveThrottle -= Time.deltaTime;
			}
			if (RegisterLineTimer > 0f)
			{
				RegisterLineTimer -= Time.deltaTime;
				if (RegisterLineTimer <= 0f)
				{
					RegisterLine = "";
				}
			}
			bool serveTap = Input.GetKeyDown(CoopPlugin.ServeKey.Value);
			if (Role == CoopRole.Client && _serveThrottle <= 0f && InGameLevel() && (serveTap || Input.GetKey(CoopPlugin.ServeKey.Value)) && !CoopUI.TextFieldFocused)
			{
				_serveThrottle = 0.25f;
				Guarded("serve", delegate
				{
					//IL_0020: Unknown result type (might be due to invalid IL or missing references)
					Transform val2 = ResolvePlayer();
					int idx = (((Object)(object)val2 != (Object)null) ? RegisterServe.FindNearestCounter(val2.position, CoopPlugin.ServeReach.Value, !serveTap) : (-1));
					if (idx < 0 || !_trades.HasOffer(idx))
					{
						if (idx < 0)
						{
							if (serveTap)
							{
								RegisterLine = "walk up to the register first";
								RegisterLineTimer = 2f;
							}
						}
						else
						{
							Send(1, MsgType.ServeRequest, delegate(BinaryWriter bw)
							{
								bw.Write(idx);
							});
						}
					}
				});
			}
			if (Role == CoopRole.Client && _serveThrottle <= 0f && InGameLevel() && Input.GetMouseButtonDown(0) && !CoopUI.TextFieldFocused)
			{
				Guarded("serve-click", delegate
				{
					//IL_0017: 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_0093: Unknown result type (might be due to invalid IL or missing references)
					Camera main = Camera.main;
					if (!((Object)(object)main == (Object)null))
					{
						RaycastHit val2 = default(RaycastHit);
						if (Physics.Raycast(main.ScreenPointToRay(Input.mousePosition), ref val2, 6f) && _registerMirror.TryGetPropCounter(((RaycastHit)(ref val2)).collider, out var propIdx))
						{
							_serveThrottle = 0.25f;
							Send(1, MsgType.ServeRequest, delegate(BinaryWriter bw)
							{
								bw.Write(propIdx);
							});
						}
						else
						{
							Transform val3 = ResolvePlayer();
							int near = (((Object)(object)val3 != (Object)null) ? RegisterServe.FindNearestCounter(val3.position, CoopPlugin.ServeReach.Value, quiet: true) : (-1));
							if (near >= 0 && _registerMirror.IsPaymentPhase(near))
							{
								_serveThrottle = 0.3f;
								Send(1, MsgType.ServeRequest, delegate(BinaryWriter bw)
								{
									bw.Write(near);
								});
							}
						}
					}
				});
			}
			if (_net == null)
			{
				return;
			}
			Guarded("net-pump", _actNetPump);
			if (!Application.runInBackground)
			{
				Application.runInBackground = true;
				CoopPlugin.Log.LogInfo((object)"Forced runInBackground=true for the co-op session");
			}
			int result2;
			while (_net.Connects.TryDequeue(out result2))
			{
				CoopPlugin.Log.LogInfo((object)("Connection " + result2 + " opened"));
				if (Role == CoopRole.Host)
				{
					ModulesForceResend();
				}
			}
			int result3;
			while (_net.Disconnects.TryDequeue(out result3))
			{
				string value;
				string text = (PeerNames.TryGetValue(result3, out value) ? value : ("player " + result3));
				PeerNames.Remove(result3);
				_avatars.Remove(result3);
				if (Role == CoopRole.Host)
				{
					BroadcastRoster();
					StatusLine = ((_net.ConnectionCount == 0) ? "Hosting - waiting for a player..." : $"Hosting - {_net.ConnectionCount} player(s)");
					CoopPlugin.Log.LogInfo((object)(text + " left"));
				}
				else if (Role == CoopRole.Client)
				{
					ErrorLine = "Lost connection to the host. You can keep walking around; nothing here touches your own saves.";
					Shutdown("host connection lost");
					return;
				}
			}
			_dispatchBuf.Clear();
			InMsg result4;
			while (_net != null && _net.Incoming.TryDequeue(out result4))
			{
				_dispatchBuf.Add(result4);
			}
			if (_dispatchBuf.Count > 8)
			{
				_dispatchSeen.Clear();
				for (int num = _dispatchBuf.Count - 1; num >= 0; num--)
				{
					MsgType type = _dispatchBuf[num].Type;
					if (type == MsgType.PlayerState || type == MsgType.RegisterState || type == MsgType.BoxState || type == MsgType.PopState)
					{
						long item = (long)(((ulong)type << 32) | (uint)_dispatchBuf[num].ConnId);
						if (!_dispatchSeen.Add(item))
						{
							_dispatchBuf[num] = default(InMsg);
						}
					}
				}
			}
			for (int num2 = 0; num2 < _dispatchBuf.Count; num2++)
			{
				if (_dispatchBuf[num2].Type != 0)
				{
					try
					{
						Dispatch(_dispatchBuf[num2]);
					}
					catch (Exception arg)
					{
						CoopPlugin.Log.LogError((object)$"Dispatch {_dispatchBuf[num2].Type}: {arg}");
					}
					if (_net == null)
					{
						break;
					}
				}
			}
			if (_net == null)
			{
				return;
			}
			float deltaTime = Time.deltaTime;
			if (_errLogCooldown > 0f)
			{
				_errLogCooldown -= deltaTime;
			}
			FlushPendingCardWork();
			_dt = deltaTime;
			Guarded("avatars", _actAvatars);
			_syncActive = Role != CoopRole.None && _net.ConnectionCount > 0 && InGameLevel();
			Guarded("world", _actWorld);
			Guarded("cardshelves", _actCardShelves);
			Guarded("objmoves", _actObjMoves);
			Guarded("boxes", _actBoxes);
			Guarded("population", _actPopulation);
			Guarded("modules", _actModules);
			if (Role == CoopRole.Client)
			{
				Guarded("npc-puppets", _actNpcPuppets);
				Guarded("register-mirror", _actRegisterMirror);
				_npcSweepTimer += deltaTime;
				if (_npcSweepTimer >= 2f && InGameLevel())
				{
					_npcSweepTimer -= 2f;
					Guarded("npc-sweep", _actNpcSweep);
				}
			}
			_stateTimer += deltaTime;
			Guarded("state-send", _actStateSend);
			_diagTimer += deltaTime;
			if (_diagTimer >= 15f)
			{
				_diagTimer -= 15f;
				Transform val = (InGameLevel() ? ResolvePlayer() : null);
				string text2 = (((Object)(object)val != (Object)null) ? $"({val.position.x:F1},{val.position.y:F1},{val.position.z:F1})" : "n/a");
				string text3 = "";
				if (InGameLevel())
				{
					try
					{
						int num3 = NpcSync.CountLocalActiveNpcs();
						text3 = ((Role == CoopRole.Client) ? $" puppets={_npcs.PuppetCount} localNpcs={num3}(should be 0)" : $" liveNpcs={num3}");
					}
					catch
					{
					}
				}
				CoopPlugin.Log.LogInfo((object)$"diag: role={Role} conns={_net.ConnectionCount} sentStates={_diagSent} recvStates={_diagRecvStates} inGame={InGameLevel()} pos={text2}{text3}");
			}
			for (int num4 = _pendingKicks.Count - 1; num4 >= 0; num4--)
			{
				float num5 = _pendingKicks[num4].Value - deltaTime;
				if (num5 <= 0f)
				{
					int key = _pendingKicks[num4].Key;
					_pendingKicks.RemoveAt(num4);
					_net.Kick(key);
				}
				else
				{
					_pendingKicks[num4] = new KeyValuePair<int, float>(_pendingKicks[num4].Key, num5);
				}
			}
			_pingTimer += deltaTime;
			if (_pingTimer >= 2f)
			{
				_pingTimer = 0f;
				Broadcast(MsgType.Ping, null);
				foreach (int item2 in _net.ConnIds())
				{
					if (_net.SecondsSinceLastRecv(item2) > _net.TimeoutSeconds)
					{
						CoopPlugin.Log.LogWarning((object)("Connection " + item2 + " timed out"));
						_net.Kick(item2);
					}
				}
			}
			if (Role == CoopRole.Host)
			{
				HostTick(deltaTime);
			}
		}

		private void AutoTick(float dt)
		{
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			if ((_autoHostSlot < 0 && _autoJoinIp == null) || _autoPhase >= 99)
			{
				return;
			}
			_autoTimer += dt;
			if (_autoHostSlot >= 0)
			{
				if (_autoPhase == 0 && _autoTimer > 6f && !InGameLevel() && (Object)(object)CSingleton<CGameManager>.Instance != (Object)null)
				{
					CoopPlugin.Log.LogInfo((object)$"AUTO: loading slot {_autoHostSlot}...");
					SaveTransfer.ForceLoadSlot(_autoHostSlot);
					_autoPhase = 1;
					_autoTimer = 0f;
				}
				else if (_autoPhase == 1 && InGameLevel() && GameInstance.m_FinishedSavefileLoading)
				{
					_autoPhase = 2;
					_autoTimer = 0f;
				}
				else if (_autoPhase == 2 && _autoTimer > 3f)
				{
					CoopPlugin.Log.LogInfo((object)"AUTO: hosting now");
					StartHosting();
					_autoPhase = 99;
				}
			}
			else if (_autoJoinIp != null)
			{
				if (_autoPhase == 0 && _autoTimer > 10f && !InGameLevel() && (Object)(object)CSingleton<CGameManager>.Instance != (Object)null)
				{
					CoopPlugin.Log.LogInfo((object)("AUTO: joining " + _autoJoinIp + "..."));
					Join(_autoJoinIp);
					_autoPhase = 99;
				}
			}
			else if (_autoJoinSteamLobby != 0L && _autoPhase == 0 && _autoTimer > 10f && !InGameLevel() && (Object)(object)CSingleton<CGameManager>.Instance != (Object)null)
			{
				CoopPlugin.Log.LogInfo((object)$"AUTO: joining Steam lobby {_autoJoinSteamLobby}...");
				JoinSteam(new CSteamID(_autoJoinSteamLobby));
				_autoPhase = 99;
			}
		}

		private void OnLocalWorldChanges(List<WorldSync.Entry> changes)
		{
			if (Role == CoopRole.Host)
			{
				Broadcast(MsgType.ShelfDelta, delegate(BinaryWriter bw)
				{
					WorldSync.WriteEntries(bw, changes);
				});
			}
			else if (Role == CoopRole.Client)
			{
				Send(1, MsgType.ShelfRequest, delegate(BinaryWriter bw)
				{
					WorldSync.WriteEntries(bw, changes);
				});
			}
		}

		private void HostTick(float dt)
		{
			//IL_0525: Unknown result type (might be due to invalid IL or missing references)
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0536: Unknown result type (might be due to invalid IL or missing references)
			//IL_0537: Unknown result type (might be due to invalid IL or missing references)
			//IL_053a: Expected I4, but got Unknown
			if (_net.ConnectionCount == 0)
			{
				return;
			}
			if (InGameLevel())
			{
				Guarded("npc-collect", _actNpcCollect);
				Guarded("register-collect", _actRegisterCollect);
			}
			_priceTimer += dt;
			if (_priceTimer >= 3f)
			{
				_priceTimer -= 3f;
				try
				{
					List<float> prices = CPlayerData.m_SetItemPriceList;
					int num = 17;
					for (int i = 0; i < prices.Count; i++)
					{
						if (prices[i] != 0f)
						{
							num = num * 31 + i;
							num = num * 31 + prices[i].GetHashCode();
						}
					}
					if (num != _lastPriceHash)
					{
						_lastPriceHash = num;
						Broadcast(MsgType.PriceList, delegate(BinaryWriter bw)
						{
							int num5 = 0;
							for (int j = 0; j < prices.Count; j++)
							{
								if (prices[j] != 0f)
								{
									num5++;
								}
							}
							bw.Write(num5);
							for (int k = 0; k < prices.Count; k++)
							{
								if (prices[k] != 0f)
								{
									bw.Write(k);
									bw.Write(prices[k]);
								}
							}
						});
					}
				}
				catch (Exception ex)
				{
					CoopPlugin.Log.LogWarning((object)("price sync: " + ex.Message));
				}
			}
			_shopNameTimer += dt;
			if (_shopNameTimer >= 3f)
			{
				_shopNameTimer -= 3f;
				string name = CPlayerData.GetPlayerName();
				if (name != _lastShopNameSent)
				{
					_lastShopNameSent = name;
					Broadcast(MsgType.ShopName, delegate(BinaryWriter bw)
					{
						bw.Write(name);
					});
				}
			}
			_econTimer += dt;
			if (_econTimer >= 0.5f)
			{
				_econTimer -= 0.5f;
				double coin = CPlayerData.m_CoinAmountDouble;
				if (Math.Abs(coin - _lastCoinSent) > 0.0001)
				{
					_lastCoinSent = coin;
					float coinF = CPlayerData.m_CoinAmount;
					Broadcast(MsgType.CoinSet, delegate(BinaryWriter bw)
					{
						bw.Write(coin);
						bw.Write(coinF);
					});
				}
				int exp = CPlayerData.m_ShopExpPoint;
				int level = CPlayerData.m_ShopLevel;
				int fame = CPlayerData.m_FamePoint;
				long num2 = ((long)level << 40) ^ ((long)fame << 20) ^ (uint)exp;
				if (num2 != _lastProgressSent)
				{
					_lastProgressSent = num2;
					Broadcast(MsgType.ProgressSet, delegate(BinaryWriter bw)
					{
						bw.Write(exp);
						bw.Write(level);
						bw.Write(fame);
					});
				}
			}
			_lightSyncTimer += dt;
			if (_lightSyncTimer >= 5f)
			{
				_lightSyncTimer -= 5f;
				try
				{
					if ((Object)(object)_lightManager == (Object)null)
					{
						_lightManager = Object.FindObjectOfType<LightManager>();
					}
					if ((Object)(object)_lightManager != (Object)null && MiUpdateLightData != null && CPlayerData.m_LightTimeData != null)
					{
						MiUpdateLightData.Invoke(_lightManager, null);
						string lightJson = JsonUtility.ToJson((object)CPlayerData.m_LightTimeData);
						_lightHeal += 5f;
						if (lightJson != _lastLightJson || _lightHeal >= 15f)
						{
							_lastLightJson = lightJson;
							_lightHeal = 0f;
							Broadcast(MsgType.LightState, delegate(BinaryWriter bw)
							{
								bw.Write(lightJson);
							});
						}
					}
				}
				catch (Exception ex2)
				{
					CoopPlugin.Log.LogWarning((object)("light sync: " + ex2.Message));
				}
			}
			_cardResyncTimer += dt;
			if (_cardResyncTimer >= 12f && InGameLevel())
			{
				_cardResyncTimer -= 12f;
				try
				{
					List<CardShelfSync.Entry> full = _cardShelves.BuildFullState();
					if (full.Count > 0)
					{
						Broadcast(MsgType.CardShelfDelta, delegate(BinaryWriter bw)
						{
							CardShelfSync.WriteEntries(bw, full);
						});
					}
				}
				catch (Exception ex3)
				{
					CoopPlugin.Log.LogWarning((object)("card resync: " + ex3.Message));
				}
			}
			_licenseSyncTimer += dt;
			if (_licenseSyncTimer >= 10f && InGameLevel())
			{
				_licenseSyncTimer -= 10f;
				try
				{
					List<RestockData> restockDataList = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList;
					List<bool> isItemLicenseUnlocked = CPlayerData.m_IsItemLicenseUnlocked;
					List<RestockData> unlocked = new List<RestockData>();
					for (int num3 = 0; num3 < restockDataList.Count && num3 < isItemLicenseUnlocked.Count; num3++)
					{
						if (isItemLicenseUnlocked[num3] && restockDataList[num3] != null)
						{
							unlocked.Add(restockDataList[num3]);
						}
					}
					bool scanner = CPlayerData.m_IsScannerRestockUnlocked;
					int num4 = 17;
					foreach (RestockData item in unlocked)
					{
						num4 = num4 * 31 + ((item.itemType << 1) | item.isBigBox);
					}
					num4 = num4 * 31 + (scanner ? 1 : 0);
					_licenseHeal += 10f;
					if (num4 != _lastLicenseHash || _licenseHeal >= 60f)
					{
						_lastLicenseHash = num4;
						_licenseHeal = 0f;
						Broadcast(MsgType.LicenseState, delegate(BinaryWriter bw)
						{
							//IL_0036: Unknown result type (might be due to invalid IL or missing references)
							//IL_0040: Expected I4, but got Unknown
							bw.Write(scanner);
							bw.Write((ushort)unlocked.Count);
							foreach (RestockData item2 in unlocked)
							{
								bw.Write((int)item2.itemType);
								bw.Write(item2.isBigBox);
							}
						});
					}
				}
				catch (Exception ex4)
				{
					CoopPlugin.Log.LogWarning((object)("license sync: " + ex4.Message));
				}
			}
			_dayTimer += dt;
			if (!(_dayTimer >= 2f))
			{
				return;
			}
			_dayTimer -= 2f;
			int hour = 8;
			int min = 0;
			try
			{
				if ((Object)(object)_lightManager == (Object)null)
				{
					_lightManager = Object.FindObjectOfType<LightManager>();
				}
				if ((Object)(object)_lightManager != (Object)null)
				{
					if (FiTimeHour != null)
					{
						hour = (int)FiTimeHour.GetValue(_lightManager);
					}
					if (FiTimeMin != null)
					{
						min = (int)FiTimeMin.GetValue(_lightManager);
					}
				}
			}
			catch
			{
			}
			int day = CPlayerData.m_CurrentDay;
			Broadcast(MsgType.DayTime, delegate(BinaryWriter bw)
			{
				bw.Write(day);
				bw.Write(hour);
				bw.Write(min);
			});
		}

		private void Dispatch(InMsg msg)
		{
			//IL_09c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a39: Unknown result type (might be due to invalid IL or missing references)
			//IL_11d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_11db: Unknown result type (might be due to invalid IL or missing references)
			//IL_11e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_11e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_11f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_11f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_11fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_1202: Unknown result type (might be due to invalid IL or missing references)
			//IL_120f: Unknown result type (might be due to invalid IL or missing references)
			//IL_121c: Unknown result type (might be due to invalid IL or missing references)
			//IL_1229: Unknown result type (might be due to invalid IL or missing references)
			//IL_1236: Unknown result type (might be due to invalid IL or missing references)
			//IL_1243: Unknown result type (might be due to invalid IL or missing references)
			//IL_1252: Expected O, but got Unknown
			//IL_0e63: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e6d: Expected O, but got Unknown
			//IL_0e6d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e77: Expected O, but got Unknown
			//IL_1768: Unknown result type (might be due to invalid IL or missing references)
			//IL_1772: Expected O, but got Unknown
			//IL_0e7f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e89: Expected O, but got Unknown
			//IL_26e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_26ee: Expected O, but got Unknown
			//IL_26f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_2700: Expected O, but got Unknown
			//IL_2709: Unknown result type (might be due to invalid IL or missing references)
			//IL_2713: Expected O, but got Unknown
			//IL_271c: Unknown result type (might be due to invalid IL or missing references)
			//IL_2726: Expected O, but got Unknown
			//IL_10bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c3d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c47: Expected O, but got Unknown
			//IL_1504: Unknown result type (might be due to invalid IL or missing references)
			//IL_1506: Unknown result type (might be due to invalid IL or missing references)
			//IL_22d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_22df: Expected O, but got Unknown
			//IL_089d: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a7: Expected O, but got Unknown
			//IL_266c: Unknown result type (might be due to invalid IL or missing references)
			//IL_2674: Unknown result type (might be due to invalid IL or missing references)
			//IL_0932: Unknown result type (might be due to invalid IL or missing references)
			//IL_093c: Expected O, but got Unknown
			switch (msg.Type)
			{
			case MsgType.Hello:
			{
				if (Role != CoopRole.Host)
				{
					break;
				}
				using BinaryReader binaryReader26 = Msg.Reader(msg.Payload);
				string text7 = binaryReader26.ReadString();
				string text8 = binaryReader26.ReadString();
				string text9 = binaryReader26.ReadString();
				string text10 = binaryReader26.ReadString();
				string text11 = binaryReader26.ReadString();
				if (text7 != "1.0.6")
				{
					RejectConn(msg.ConnId, "version mismatch - host runs CardShopCoop 1.0.6, you have " + text7);
					break;
				}
				if (HostPassword.Length > 0 && text9 != HostPassword)
				{
					RejectConn(msg.ConnId, "wrong password");
					break;
				}
				if (text10 != ModParity.PluginHash())
				{
					RejectConn(msg.ConnId, "your mod set differs from the host's - both players need identical mods (same versions)");
					break;
				}
				string text12 = ModParity.EnumHash();
				if (text11 != "none" && text12 != "none" && text11 != text12)
				{
					try
					{
						byte[] data = File.ReadAllBytes(ModParity.EnumFilePath());
						byte[] gz = Msg.Gzip(data);
						Send(msg.ConnId, MsgType.EnumSync, delegate(BinaryWriter bw)
						{
							bw.Write(gz.Length);
							bw.Write(gz);
						});
					}
					catch (Exception ex3)
					{
						CoopPlugin.Log.LogWarning((object)("enum sync send: " + ex3.Message));
					}
					RejectConn(msg.ConnId, "your custom-card database differed - it has been synced from the host; RESTART your game, then join again");
				}
				else
				{
					PeerNames[msg.ConnId] = text8;
					_avatars.SetName(msg.ConnId, text8);
					StatusLine = "Hosting - " + text8 + " joined!";
					CoopPlugin.Log.LogInfo((object)(text8 + " joined, sending world..."));
					SendWorldTo(msg.ConnId);
					BroadcastRoster();
				}
				break;
			}
			case MsgType.Welcome:
				if (Role == CoopRole.Client)
				{
					using (BinaryReader binaryReader3 = Msg.Reader(msg.Payload))
					{
						binaryReader3.ReadString();
						string text3 = binaryReader3.ReadString();
						_saveExpected = binaryReader3.ReadInt32();
						_hostSlot = binaryReader3.ReadInt32();
						_bundleExpected = binaryReader3.ReadInt32();
						_selfId = binaryReader3.ReadByte();
						PeerNames[msg.ConnId] = text3;
						_avatars.SetName(msg.ConnId, text3);
						_saveBuf = new MemoryStream((_saveExpected > 0) ? _saveExpected : 1024);
						_bundleBuf = new MemoryStream((_bundleExpected > 0) ? _bundleExpected : 16);
						StatusLine = $"Downloading {text3}'s shop ({(_saveExpected + _bundleExpected) / 1024} KB)...";
						break;
					}
				}
				break;
			case MsgType.SaveChunk:
			{
				if (Role != CoopRole.Client || _saveBuf == null)
				{
					break;
				}
				using BinaryReader binaryReader14 = Msg.Reader(msg.Payload);
				binaryReader14.ReadInt32();
				int count2 = binaryReader14.ReadInt32();
				byte[] array2 = binaryReader14.ReadBytes(count2);
				_saveBuf.Write(array2, 0, array2.Length);
				if (_saveExpected > 0)
				{
					StatusLine = $"downloading shop... {Math.Min(100L, _saveBuf.Length * 100 / _saveExpected)}%";
				}
				break;
			}
			case MsgType.SaveDone:
			{
				if (Role != CoopRole.Client || _saveBuf == null || _worldRequested)
				{
					break;
				}
				byte[] array3 = _saveBuf.ToArray();
				_saveBuf = null;
				if (_saveExpected >= 0 && array3.Length != _saveExpected)
				{
					ErrorLine = $"World download looked corrupted ({array3.Length}/{_saveExpected} bytes) - try again.";
					Shutdown("bad download");
					break;
				}
				try
				{
					array3 = Msg.Gunzip(array3);
				}
				catch
				{
					ErrorLine = "World download could not be unpacked - try again.";
					Shutdown("bad download");
					break;
				}
				if (array3.Length < 1024 || array3[0] != 123)
				{
					ErrorLine = "World download looked corrupted - try again.";
					Shutdown("bad download");
				}
				else
				{
					_pendingSave = array3;
					StatusLine = "shop received - downloading mod data...";
				}
				break;
			}
			case MsgType.BundleChunk:
			{
				if (Role != CoopRole.Client || _bundleBuf == null)
				{
					break;
				}
				using BinaryReader binaryReader7 = Msg.Reader(msg.Payload);
				binaryReader7.ReadInt32();
				int count = binaryReader7.ReadInt32();
				byte[] array = binaryReader7.ReadBytes(count);
				_bundleBuf.Write(array, 0, array.Length);
				if (_bundleExpected > 0)
				{
					StatusLine = $"downloading mod data... {Math.Min(100L, _bundleBuf.Length * 100 / _bundleExpected)}%";
				}
				break;
			}
			case MsgType.BundleDone:
			{
				if (Role != CoopRole.Client || _worldRequested || _pendingSave == null)
				{
					break;
				}
				byte[] array4 = ((_bundleBuf != null) ? _bundleBuf.ToArray() : new byte[0]);
				_bundleBuf = null;
				_worldRequested = true;
				StatusLine = "World received - loading...";
				try
				{
					if (array4.Length != 0)
					{
						array4 = Msg.Gunzip(array4);
					}
					SidecarTransfer.ApplyBundle(array4, _hostSlot, SaveTransfer.CoopSlot);
				}
				catch (Exception ex2)
				{
					CoopPlugin.Log.LogWarning((object)("Sidecar apply failed (continuing): " + ex2.Message));
				}
				SaveTransfer.ApplyAndLoad(_pendingSave);
				_pendingSave = null;
				break;
			}
			case MsgType.ShelfDelta:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br6 = Msg.Reader(msg.Payload))
					{
						_world.ApplyRemote(WorldSync.ReadEntries(br6));
						break;
					}
				}
				break;
			case MsgType.ShelfRequest:
			{
				if (Role != CoopRole.Host || !InGameLevel())
				{
					break;
				}
				using BinaryReader br29 = Msg.Reader(msg.Payload);
				List<WorldSync.Entry> entries3 = WorldSync.ReadEntries(br29);
				_world.ApplyRemote(entries3);
				if (_net.ConnectionCount > 1)
				{
					Broadcast(MsgType.ShelfDelta, delegate(BinaryWriter bw)
					{
						WorldSync.WriteEntries(bw, entries3);
					});
				}
				break;
			}
			case MsgType.PriceList:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader22 = Msg.Reader(msg.Payload);
				int num33 = binaryReader22.ReadInt32();
				List<float> setItemPriceList2 = CPlayerData.m_SetItemPriceList;
				GamePatches.ApplyingRemotePrice = true;
				try
				{
					_incomingPriced.Clear();
					int num34 = 0;
					for (int num35 = 0; num35 < num33; num35++)
					{
						int num36 = binaryReader22.ReadInt32();
						float num37 = binaryReader22.ReadSingle();
						_incomingPriced.Add(num36);
						if (num36 >= 0 && num36 <= 500000)
						{
							while (setItemPriceList2.Count <= num36)
							{
								setItemPriceList2.Add(0f);
								num34++;
							}
							if (Math.Abs(setItemPriceList2[num36] - num37) > 0.0001f)
							{
								setItemPriceList2[num36] = num37;
								CEventManager.QueueEvent((CEvent)new CEventPlayer_ItemPriceChanged((EItemType)num36, num37));
							}
						}
					}
					if (num34 > 0)
					{
						CoopPlugin.Log.LogInfo((object)$"price apply: grew the price table by {num34} entries for modded items");
					}
					foreach (int item in _clientPriced)
					{
						if (!_incomingPriced.Contains(item) && item >= 0 && item < setItemPriceList2.Count && setItemPriceList2[item] != 0f)
						{
							setItemPriceList2[item] = 0f;
							CEventManager.QueueEvent((CEvent)new CEventPlayer_ItemPriceChanged((EItemType)item, 0f));
						}
					}
					HashSet<int> clientPriced = _clientPriced;
					_clientPriced = _incomingPriced;
					_incomingPriced = clientPriced;
					break;
				}
				finally
				{
					GamePatches.ApplyingRemotePrice = false;
				}
			}
			case MsgType.PlayerState:
			{
				using BinaryReader binaryReader21 = Msg.Reader(msg.Payload);
				Vector3 pos = new Vector3(binaryReader21.ReadSingle(), binaryReader21.ReadSingle(), binaryReader21.ReadSingle());
				float yaw = binaryReader21.ReadSingle();
				float speed = binaryReader21.ReadSingle();
				byte hold = binaryReader21.ReadByte();
				ReadHoldPayload(binaryReader21, hold, out var holdTypes, out var holdCards);
				_diagRecvStates++;
				_avatars.UpdateState(msg.ConnId, pos, yaw, speed, hold, holdTypes, holdCards);
				if (PeerNames.TryGetValue(msg.ConnId, out var value4))
				{
					_avatars.SetName(msg.ConnId, value4);
				}
				if (Role == CoopRole.Host && _net.ConnectionCount > 1)
				{
					byte[] frame = Msg.Build(MsgType.RelayState, delegate(BinaryWriter bw)
					{
						bw.Write((byte)msg.ConnId);
						bw.Write(pos.x);
						bw.Write(pos.y);
						bw.Write(pos.z);
						bw.Write(yaw);
						bw.Write(speed);
						bw.Write(hold);
						WriteHoldPayload(bw, hold, holdTypes, holdCards);
					});
					foreach (int item2 in _net.ConnIds())
					{
						if (item2 != msg.ConnId)
						{
							_net.SendTransient(item2, frame);
						}
					}
				}
				if (_gotStateFrom.Add(msg.ConnId))
				{
					string value5;
					string text6 = (PeerNames.TryGetValue(msg.ConnId, out value5) ? value5 : ("player " + msg.ConnId));
					CoopPlugin.Log.LogInfo((object)("Position link active with " + text6));
					if (Role == CoopRole.Host)
					{
						StatusLine = "Hosting - " + text6 + " is in your shop!";
					}
				}
				break;
			}
			case MsgType.CoinSet:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader19 = Msg.Reader(msg.Payload);
				double num28 = binaryReader19.ReadDouble();
				float num29 = binaryReader19.ReadSingle();
				if (!_loggedEconLink)
				{
					_loggedEconLink = true;
					CoopPlugin.Log.LogInfo((object)"Economy link active (host wallet mirrored)");
				}
				if (Math.Abs(CPlayerData.m_CoinAmountDouble - num28) > 0.0001)
				{
					CEventManager.QueueEvent((CEvent)new CEventPlayer_SetCoin(num29, num28));
				}
				break;
			}
			case MsgType.DayTime:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader17 = Msg.Reader(msg.Payload);
				int num18 = binaryReader17.ReadInt32();
				int num19 = binaryReader17.ReadInt32();
				int num20 = binaryReader17.ReadInt32();
				if (!_loggedTimeLink)
				{
					_loggedTimeLink = true;
					CoopPlugin.Log.LogInfo((object)$"Time link active (Day {num18} {num19:00}:{num20:00})");
				}
				HostTimeLine = $"Day {num18 + 1}  {num19:00}:{num20:00}";
				bool flag3 = num18 != CPlayerData.m_CurrentDay;
				CPlayerData.m_CurrentDay = num18;
				CPlayerData.m_IsShopOnceOpen = true;
				try
				{
					if ((Object)(object)_lightManager == (Object)null)
					{
						_lightManager = Object.FindObjectOfType<LightManager>();
					}
					if ((Object)(object)_lightManager != (Object)null)
					{
						if (flag3 && InGameLevel() && MiDayReset != null)
						{
							GamePatches.AllowNextDayStarted = true;
							((MonoBehaviour)_lightManager).StartCoroutine((IEnumerator)MiDayReset.Invoke(_lightManager, null));
							CoopPlugin.Log.LogInfo((object)$"Mirroring host day change -> Day {num18}");
						}
						else
						{
							FiTimeHour?.SetValue(_lightManager, num19);
							FiTimeMin?.SetValue(_lightManager, num20);
							FiTimeMinFloat?.SetValue(_lightManager, (float)num20);
							FiHasDayEnded?.SetValue(_lightManager, false);
						}
					}
					break;
				}
				catch
				{
					break;
				}
			}
			case MsgType.ProgressSet:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader13 = Msg.Reader(msg.Payload);
				int num13 = binaryReader13.ReadInt32();
				int num14 = binaryReader13.ReadInt32();
				int num15 = binaryReader13.ReadInt32();
				int shopLevel = CPlayerData.m_ShopLevel;
				CPlayerData.m_ShopLevel = num14;
				CEventManager.QueueEvent((CEvent)new CEventPlayer_SetShopExp(num13));
				CEventManager.QueueEvent((CEvent)new CEventPlayer_SetFame(num15));
				if (num14 > shopLevel)
				{
					CEventManager.QueueEvent((CEvent)new CEventPlayer_ShopLeveledUp(num14));
				}
				break;
			}
			case MsgType.Emote:
				_avatars.ShowEmote(msg.ConnId);
				RelayTagToOthers(msg.ConnId, 0);
				break;
			case MsgType.Activity:
			{
				int num10 = -1;
				try
				{
					using BinaryReader binaryReader9 = Msg.Reader(msg.Payload);
					binaryReader9.ReadByte();
					num10 = binaryReader9.ReadInt32();
				}
				catch
				{
				}
				_avatars.ShowTag(msg.ConnId, "opening a pack!", 3f);
				_avatars.ShowPackOpen(msg.ConnId, num10);
				RelayTagToOthers(msg.ConnId, 1, num10);
				break;
			}
			case MsgType.Roster:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader4 = Msg.Reader(msg.Payload);
				int num5 = binaryReader4.ReadByte();
				HashSet<int> seen = new HashSet<int>();
				for (int num6 = 0; num6 < num5; num6++)
				{
					int num7 = binaryReader4.ReadByte();
					string text4 = binaryReader4.ReadString();
					if (num7 != _selfId)
					{
						seen.Add(num7);
						_rosterNames[num7] = text4;
						if (_relayIds.Add(num7))
						{
							CoopPlugin.Log.LogInfo((object)("peer in shop: " + text4));
						}
						_avatars.SetName(1000 + num7, text4);
					}
				}
				_relayIds.RemoveWhere(delegate(int id)
				{
					if (seen.Contains(id))
					{
						return false;
					}
					_avatars.Remove(1000 + id);
					return true;
				});
				break;
			}
			case MsgType.RelayState:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader30 = Msg.Reader(msg.Payload);
				int num41 = binaryReader30.ReadByte();
				Vector3 pos2 = default(Vector3);
				((Vector3)(ref pos2))..ctor(binaryReader30.ReadSingle(), binaryReader30.ReadSingle(), binaryReader30.ReadSingle());
				float yaw2 = binaryReader30.ReadSingle();
				float speed2 = binaryReader30.ReadSingle();
				byte b3 = binaryReader30.ReadByte();
				ReadHoldPayload(binaryReader30, b3, out var types, out var cards);
				if (num41 != _selfId)
				{
					_avatars.UpdateState(1000 + num41, pos2, yaw2, speed2, b3, types, cards);
					if (_rosterNames.TryGetValue(num41, out var value7))
					{
						_avatars.SetName(1000 + num41, value7);
					}
				}
				break;
			}
			case MsgType.RelayTag:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader28 = Msg.Reader(msg.Payload);
				int num40 = binaryReader28.ReadByte();
				byte b2 = binaryReader28.ReadByte();
				int packIndex = -1;
				try
				{
					packIndex = binaryReader28.ReadInt32();
				}
				catch
				{
				}
				if (num40 != _selfId)
				{
					if (b2 == 0)
					{
						_avatars.ShowEmote(1000 + num40);
						break;
					}
					_avatars.ShowTag(1000 + num40, "opening a pack!", 3f);
					_avatars.ShowPackOpen(1000 + num40, packIndex);
				}
				break;
			}
			case MsgType.CardDelta:
			{
				using BinaryReader binaryReader27 = Msg.Reader(msg.Payload);
				bool isAdd = binaryReader27.ReadBoolean();
				int amount = binaryReader27.ReadInt32();
				CardData card2 = new CardData
				{
					expansionType = (ECardExpansionType)binaryReader27.ReadInt32(),
					monsterType = (EMonsterType)binaryReader27.ReadInt32(),
					borderType = (ECardBorderType)binaryReader27.ReadInt32(),
					isFoil = binaryReader27.ReadBoolean(),
					isDestiny = binaryReader27.ReadBoolean(),
					isChampionCard = binaryReader27.ReadBoolean(),
					isNew = binaryReader27.ReadBoolean(),
					cardGrade = binaryReader27.ReadInt32(),
					gradedCardIndex = binaryReader27.ReadInt32()
				};
				if (!InGameLevel())
				{
					_pendingCardDeltas.Add(new PendingCard
					{
						IsAdd = isAdd,
						Amount = amount,
						Card = card2
					});
				}
				else
				{
					ApplyCardDelta(isAdd, amount, card2);
				}
				break;
			}
			case MsgType.NpcState:
				if (Role == CoopRole.Client)
				{
					using (BinaryReader br24 = Msg.Reader(msg.Payload))
					{
						_npcs.ApplyBatch(br24, InGameLevel());
						break;
					}
				}
				break;
			case MsgType.CardShelfDelta:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br21 = Msg.Reader(msg.Payload))
					{
						_cardShelves.ApplyRemote(CardShelfSync.ReadEntries(br21));
						break;
					}
				}
				break;
			case MsgType.CardShelfRequest:
			{
				if (Role != CoopRole.Host || !InGameLevel())
				{
					break;
				}
				using BinaryReader br17 = Msg.Reader(msg.Payload);
				List<CardShelfSync.Entry> entries2 = CardShelfSync.ReadEntries(br17);
				_cardShelves.ApplyRemote(entries2);
				if (_net.ConnectionCount > 1)
				{
					Broadcast(MsgType.CardShelfDelta, delegate(BinaryWriter bw)
					{
						CardShelfSync.WriteEntries(bw, entries2);
					});
				}
				break;
			}
			case MsgType.BoxState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br13 = Msg.Reader(msg.Payload))
					{
						_boxes.ClientApply(BoxSync.ReadEntries(br13));
						break;
					}
				}
				break;
			case MsgType.PopState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br9 = Msg.Reader(msg.Payload))
					{
						_population.ClientApply(PopulationSync.Read(br9));
						break;
					}
				}
				break;
			case MsgType.FurnitureOrder:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader binaryReader10 = Msg.Reader(msg.Payload))
					{
						int num11 = binaryReader10.ReadInt32();
						Vector3 val2 = default(Vector3);
						((Vector3)(ref val2))..ctor(binaryReader10.ReadSingle(), binaryReader10.ReadSingle(), binaryReader10.ReadSingle());
						Quaternion val3 = default(Quaternion);
						((Quaternion)(ref val3))..ctor(binaryReader10.ReadSingle(), binaryReader10.ReadSingle(), binaryReader10.ReadSingle(), binaryReader10.ReadSingle());
						string value2;
						string arg = (PeerNames.TryGetValue(msg.ConnId, out value2) ? value2 : "player");
						CoopPlugin.Log.LogInfo((object)$"{arg} bought furniture: {(object)(EObjectType)num11}");
						ShelfManager.SpawnInteractableObjectInPackageBox((EObjectType)num11, val2, val3);
						break;
					}
				}
				break;
			case MsgType.BoxRequest:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br3 = Msg.Reader(msg.Payload))
					{
						_boxes.HostApplyRequest(BoxSync.ReadEntries(br3));
						break;
					}
				}
				break;
			case MsgType.OrderRequest:
			{
				if (Role != CoopRole.Host || !InGameLevel())
				{
					break;
				}
				using BinaryReader binaryReader2 = Msg.Reader(msg.Payload);
				int num = binaryReader2.ReadInt32();
				bool flag = binaryReader2.ReadBoolean();
				string rdName = binaryReader2.ReadString();
				int num2 = binaryReader2.ReadInt32();
				float cost = binaryReader2.ReadSingle();
				string value;
				string text2 = (PeerNames.TryGetValue(msg.ConnId, out value) ? value : "player");
				bool sizeDiffers;
				int num3 = ResolveRestockIndex(num, flag, rdName, out sizeDiffers);
				if (num3 >= 0)
				{
					CoopPlugin.Log.LogInfo((object)string.Format("{0} ordered {1} big={2} x{3} -> restock {4}{5}", text2, (object)(EItemType)num, flag, num2, num3, sizeDiffers ? " (size fallback)" : ""));
					RestockManager.SpawnPackageBoxItemMultipleFrame(num3, num2);
					if (sizeDiffers)
					{
						Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw)
						{
							bw.Write("'" + rdName + "' delivered in the host's box size (catalogs differ slightly)");
						});
					}
					break;
				}
				int num4 = 0;
				try
				{
					num4 = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList.Count;
				}
				catch
				{
				}
				CoopPlugin.Log.LogWarning((object)$"{text2} ordered unknown product type {num} '{rdName}' - refunding {cost:F0} (host catalog: {num4} products)");
				LogCatalogCandidates(rdName);
				if (cost > 0f && cost < 100000f)
				{
					CEventManager.QueueEvent((CEvent)new CEventPlayer_AddCoin(cost, false));
				}
				string reason = ((num4 <= 140) ? "the host's modded products haven't loaded yet (new save still in the tutorial?) - play past the host's tutorial and rejoin" : "match your content packs to order it");
				Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw)
				{
					bw.Write($"'{rdName}' isn't in the host's catalog - refunded ${cost:F0}. Note: {reason}");
				});
				break;
			}
			case MsgType.Toast:
				if (Role == CoopRole.Client)
				{
					using (BinaryReader binaryReader29 = Msg.Reader(msg.Payload))
					{
						RegisterLine = binaryReader29.ReadString();
						RegisterLineTimer = 8f;
						break;
					}
				}
				break;
			case MsgType.CatalogDigest:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br27 = Msg.Reader(msg.Payload))
					{
						CompareCatalogs(br27, msg.ConnId);
						break;
					}
				}
				break;
			case MsgType.BoxRemoved:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader binaryReader25 = Msg.Reader(msg.Payload))
					{
						int num38 = binaryReader25.ReadInt32();
						int num39 = binaryReader25.ReadInt32();
						string value6;
						string arg2 = (PeerNames.TryGetValue(msg.ConnId, out value6) ? value6 : "player");
						CoopPlugin.Log.LogInfo((object)$"{arg2} trashed box {num38} ({(object)(EItemType)num39})");
						_boxes.HostApplyRemoval(num38, num39);
						break;
					}
				}
				break;
			case MsgType.LicenseUnlock:
			{
				if (!InGameLevel())
				{
					break;
				}
				using BinaryReader binaryReader23 = Msg.Reader(msg.Payload);
				int itemType2 = binaryReader23.ReadInt32();
				bool isBig = binaryReader23.ReadBoolean();
				string rdName2 = binaryReader23.ReadString();
				bool flag5 = ApplyLicenseUnlock(itemType2, isBig, rdName2);
				if (Role != CoopRole.Host)
				{
					break;
				}
				if (flag5)
				{
					Broadcast(MsgType.LicenseUnlock, delegate(BinaryWriter bw)
					{
						bw.Write(itemType2);
						bw.Write(isBig);
						bw.Write(rdName2);
					});
					Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw)
					{
						bw.Write("license unlocked for everyone: " + rdName2);
					});
				}
				else
				{
					Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw)
					{
						bw.Write("'" + rdName2 + "' license couldn't unlock on the host (product missing) - match your content packs");
					});
				}
				break;
			}
			case MsgType.LicenseState:
			{
				if (Role != CoopRole.Client || !InGameLevel())
				{
					break;
				}
				using BinaryReader binaryReader20 = Msg.Reader(msg.Payload);
				bool scanner = binaryReader20.ReadBoolean();
				int num30 = binaryReader20.ReadUInt16();
				HashSet<long> wanted = new HashSet<long>();
				for (int num31 = 0; num31 < num30; num31++)
				{
					int num32 = binaryReader20.ReadInt32();
					bool flag4 = binaryReader20.ReadBoolean();
					wanted.Add(((long)num32 << 1) | (flag4 ? 1 : 0));
				}
				bool allowLock = Time.realtimeSinceStartupAsDouble - _lastLicenseBuyTime > 12.0;
				Guarded("license-apply", delegate
				{
					//IL_0049: Unknown result type (might be due to invalid IL or missing references)
					//IL_0094: Unknown result type (might be due to invalid IL or missing references)
					//IL_009a: Invalid comparison between Unknown and I4
					CPlayerData.m_IsScannerRestockUnlocked = CPlayerData.m_IsScannerRestockUnlocked || scanner;
					List<RestockData> restockDataList = CSingleton<InventoryBase>.Instance.m_StockItemData_SO.m_RestockDataList;
					List<bool> isItemLicenseUnlocked = CPlayerData.m_IsItemLicenseUnlocked;
					bool flag6 = false;
					for (int i = 0; i < restockDataList.Count && i < isItemLicenseUnlocked.Count; i++)
					{
						if (restockDataList[i] != null)
						{
							bool flag7 = wanted.Contains(((long)restockDataList[i].itemType << 1) | (restockDataList[i].isBigBox ? 1 : 0));
							if (flag7 && !isItemLicenseUnlocked[i])
							{
								GamePatches.ApplyingRemoteLicense = true;
								try
								{
									CPlayerData.SetUnlockItemLicense(i);
								}
								finally
								{
									GamePatches.ApplyingRemoteLicense = false;
								}
								flag6 = true;
								try
								{
									if ((int)restockDataList[i].itemType == 1)
									{
										TutorialManager.AddTaskValue((ETutorialTaskCondition)14, 1f);
									}
								}
								catch
								{
								}
							}
							else if (!flag7 && isItemLicenseUnlocked[i] && i != 0 && allowLock)
							{
								isItemLicenseUnlocked[i] = false;
							}
						}
					}
					if (flag6)
					{
						try
						{
							GameInstance.m_IsItemLicenseUnlocked = true;
						}
						catch
						{
						}
						RefreshLicensePanels();
					}
				});
				break;
			}
			case MsgType.StaffOp:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br19 = Msg.Reader(msg.Payload))
					{
						_staff.HostApplyOp(br19);
						break;
					}
				}
				break;
			case MsgType.StaffState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br15 = Msg.Reader(msg.Payload))
					{
						_staff.ClientApplyState(br15);
						break;
					}
				}
				break;
			case MsgType.ShopOp:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br14 = Msg.Reader(msg.Payload))
					{
						_shopState.HostApplyOp(br14);
						break;
					}
				}
				break;
			case MsgType.ShopState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br11 = Msg.Reader(msg.Payload))
					{
						_shopState.ClientApplyState(br11);
						break;
					}
				}
				break;
			case MsgType.SettingsOp:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br8 = Msg.Reader(msg.Payload))
					{
						_settings.HostApplyOp(br8);
						break;
					}
				}
				break;
			case MsgType.SettingsState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br5 = Msg.Reader(msg.Payload))
					{
						_settings.ClientApplyState(br5);
						break;
					}
				}
				break;
			case MsgType.MarketState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br4 = Msg.Reader(msg.Payload))
					{
						_market.ClientApplyState(br4);
						break;
					}
				}
				break;
			case MsgType.ReportState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br2 = Msg.Reader(msg.Payload))
					{
						_report.ClientApplyState(br2);
						break;
					}
				}
				break;
			case MsgType.ContainerOp:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br = Msg.Reader(msg.Payload))
					{
						_containers.HostApplyOp(br);
						break;
					}
				}
				break;
			case MsgType.ContainerState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br30 = Msg.Reader(msg.Payload))
					{
						_containers.ClientApplyState(br30);
						break;
					}
				}
				break;
			case MsgType.TournamentState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br28 = Msg.Reader(msg.Payload))
					{
						_tournament.ClientApplyState(br28);
						break;
					}
				}
				break;
			case MsgType.CardBoxOp:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br26 = Msg.Reader(msg.Payload))
					{
						_cardBoxes.HostApplyOp(br26);
						break;
					}
				}
				break;
			case MsgType.CardBoxState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br25 = Msg.Reader(msg.Payload))
					{
						_cardBoxes.ClientApplyState(br25);
						break;
					}
				}
				break;
			case MsgType.EnumSync:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader24 = Msg.Reader(msg.Payload);
				int count3 = binaryReader24.ReadInt32();
				byte[] hostBytes = Msg.Gunzip(binaryReader24.ReadBytes(count3));
				if (CoopPlugin.AutoSyncCardDatabase.Value)
				{
					StatusLine = ModParity.InstallEnumFile(hostBytes);
					CoopPlugin.Log.LogInfo((object)("enum sync: " + StatusLine));
				}
				else
				{
					StatusLine = "card databases differ - auto-sync is disabled; copy the host's enum_values.json (PrefabLoader folder) yourself";
				}
				break;
			}
			case MsgType.GradingOp:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br23 = Msg.Reader(msg.Payload))
					{
						_grading.HostApplyOp(br23);
						break;
					}
				}
				break;
			case MsgType.GradingState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br22 = Msg.Reader(msg.Payload))
					{
						_grading.ClientApplyState(br22);
						break;
					}
				}
				break;
			case MsgType.TradeOp:
				if (Role == CoopRole.Host && InGameLevel())
				{
					using (BinaryReader br20 = Msg.Reader(msg.Payload))
					{
						_trades.HostApplyOp(br20);
						break;
					}
				}
				break;
			case MsgType.TradeState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br18 = Msg.Reader(msg.Payload))
					{
						_trades.ClientApplyState(br18);
						break;
					}
				}
				break;
			case MsgType.TableState:
				if (Role == CoopRole.Client && InGameLevel())
				{
					using (BinaryReader br16 = Msg.Reader(msg.Payload))
					{
						_tables.ClientApplyState(br16);
						break;
					}
				}
				break;
			case MsgType.LightState:
			{
				if (Role != CoopRole.Client || !InGameLevel())
				{
					break;
				}
				using BinaryReader binaryReader18 = Msg.Reader(msg.Payload);
				LightTimeData val5 = JsonUtility.FromJson<LightTimeData>(binaryReader18.ReadString());
				if (val5 == null)
				{
					break;
				}
				try
				{
					if ((Object)(object)_lightManager == (Object)null)
					{
						_lightManager = Object.FindObjectOfType<LightManager>();
					}
					if (!((Object)(object)_lightManager == (Object)null))
					{
						int num21 = ((FiTimeOfDayIdx?.GetValue(_lightManager) is int num22) ? num22 : (-1));
						int num23 = ((FiTimeHour?.GetValue(_lightManager) is int num24) ? num24 : (-1));
						int num25 = ((FiTimeMin?.GetValue(_lightManager) is int num26) ? num26 : 0);
						int num27 = Math.Abs(val5.m_TimeHour * 60 + val5.m_TimeMin - (num23 * 60 + num25));
						if (num21 != val5.m_TImeOfDayIndex || num27 > 4)
						{
							CPlayerData.m_LightTimeData = val5;
							FiFinishLoading?.SetValue(_lightManager, false);
							MiLightInit?.Invoke(_lightManager, null);
							CoopPlugin.Log.LogInfo((object)$"lighting re-synced (phase {num21}->{val5.m_TImeOfDayIndex}, drift {num27}min)");
						}
					}
					break;
				}
				catch (Exception ex)
				{
					CoopPlugin.Log.LogWarning((object)("light apply: " + ex.Message));
					break;
				}
			}
			case MsgType.ShopName:
			{
				if (Role != CoopRole.Client)
				{
					break;
				}
				using BinaryReader binaryReader16 = Msg.Reader(msg.Payload);
				string text5 = binaryReader16.Read