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 PeerNames = new Dictionary(); 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 _mainThread = new ConcurrentQueue(); 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 _gotStateFrom = new HashSet(); 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 _dispatchBuf = new List(64); private readonly HashSet _dispatchSeen = new HashSet(); 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> _pendingKicks = new List>(); private readonly List _pendingCardDeltas = new List(); private readonly List> _pendingCardPrices = new List>(); private int _selfId = -1; private readonly HashSet _relayIds = new HashSet(); 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 _holdTypesBuf = new List(6); private readonly List _holdCardsBuf = new List(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 _rosterNames = new Dictionary(); private HashSet _clientPriced = new HashSet(); private HashSet _incomingPriced = new HashSet(); 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 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 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> all) { Broadcast(MsgType.PopState, delegate(BinaryWriter bw) { PopulationSync.Write(bw, all); }); }; _boxes.OnHostSnapshot = delegate(List list) { Broadcast(MsgType.BoxState, delegate(BinaryWriter bw) { BoxSync.WriteEntries(bw, list); }); }; _boxes.OnClientChanges = delegate(List 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 w) { Send(1, MsgType.GradingOp, w); }; _grading.BroadcastState = delegate(Action w) { Broadcast(MsgType.GradingState, w); }; _trades.SendOp = delegate(Action w) { Send(1, MsgType.TradeOp, w); }; _trades.BroadcastState = delegate(Action w) { Broadcast(MsgType.TradeState, w); }; _tables.BroadcastState = delegate(Action w) { Broadcast(MsgType.TableState, w); }; _staff.SendOp = delegate(Action w) { Send(1, MsgType.StaffOp, w); }; _staff.BroadcastState = delegate(Action w) { Broadcast(MsgType.StaffState, w); }; _shopState.SendOp = delegate(Action w) { Send(1, MsgType.ShopOp, w); }; _shopState.BroadcastState = delegate(Action w) { Broadcast(MsgType.ShopState, w); }; _settings.SendOp = delegate(Action w) { Send(1, MsgType.SettingsOp, w); }; _settings.BroadcastState = delegate(Action w) { Broadcast(MsgType.SettingsState, w); }; _market.BroadcastState = delegate(Action w) { Broadcast(MsgType.MarketState, w); }; _report.BroadcastState = delegate(Action w) { Broadcast(MsgType.ReportState, w); }; _containers.SendOp = delegate(Action w) { Send(1, MsgType.ContainerOp, w); }; _containers.BroadcastState = delegate(Action w) { Broadcast(MsgType.ContainerState, w); }; _tournament.BroadcastState = delegate(Action w) { Broadcast(MsgType.TournamentState, w); }; _cardBoxes.SendOp = delegate(Action w) { Send(1, MsgType.CardBoxOp, w); }; _cardBoxes.BroadcastState = delegate(Action 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((EventDelegate)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(connId, 1.5f)); } private static void ReadHoldPayload(BinaryReader br, byte hold, out List types, out List cards) { types = null; cards = null; int num = br.ReadByte(); if (num == 0) { return; } if (hold == 3) { cards = new List(num); for (int i = 0; i < num; i++) { cards.Add(Msg.ReadCard(br)); } } else { types = new List(num); for (int j = 0; j < num; j++) { types.Add(br.ReadInt32()); } } } private static void WriteHoldPayload(BinaryWriter bw, byte hold, List types, List 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 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> entries = new List>(PeerNames); Broadcast(MsgType.Roster, delegate(BinaryWriter bw) { bw.Write((byte)entries.Count); foreach (KeyValuePair 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((EventDelegate)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.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 restockDataList = CSingleton.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(); 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(); } if ((Object)(object)_cmSweep != (Object)null) { List 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 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 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(); } 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 { 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 { 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 restockDataList = CSingleton.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(); 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 restockDataList = CSingleton.Instance.m_StockItemData_SO.m_RestockDataList; List entries = new List(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 hashSet = new HashSet(); 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 restockDataList = CSingleton.Instance.m_StockItemData_SO.m_RestockDataList; int num2 = 0; int num3 = 0; List list = new List(); 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 restockDataList = CSingleton.Instance.m_StockItemData_SO.m_RestockDataList; List list = new List(); 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 write) { _net?.Send(connId, Msg.Build(type, write)); } private void Broadcast(MsgType type, Action write) { _net?.Broadcast(Msg.Build(type, write)); } private void BroadcastTransient(MsgType type, Action 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(_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.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.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.Instance != (Object)null) { CoopPlugin.Log.LogInfo((object)$"AUTO: joining Steam lobby {_autoJoinSteamLobby}..."); JoinSteam(new CSteamID(_autoJoinSteamLobby)); _autoPhase = 99; } } private void OnLocalWorldChanges(List 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 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(); } 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 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 restockDataList = CSingleton.Instance.m_StockItemData_SO.m_RestockDataList; List isItemLicenseUnlocked = CPlayerData.m_IsItemLicenseUnlocked; List unlocked = new List(); 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(); } 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 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 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 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(); } 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 seen = new HashSet(); 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 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.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 wanted = new HashSet(); 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 restockDataList = CSingleton.Instance.m_StockItemData_SO.m_RestockDataList; List 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(binaryReader18.ReadString()); if (val5 == null) { break; } try { if ((Object)(object)_lightManager == (Object)null) { _lightManager = Object.FindObjectOfType(); } 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.ReadString(); if (text5.Length > 0 && CPlayerData.GetPlayerName() != text5) { CPlayerData.PlayerName = text5; CoopPlugin.Log.LogInfo((object)("shop name synced: " + text5)); } break; } case MsgType.ItemPriceContrib: { if (Role != CoopRole.Host) { break; } using BinaryReader binaryReader15 = Msg.Reader(msg.Payload); int num16 = binaryReader15.ReadInt32(); float num17 = binaryReader15.ReadSingle(); List setItemPriceList = CPlayerData.m_SetItemPriceList; if (num16 >= 0 && num16 <= 500000) { while (setItemPriceList.Count <= num16) { setItemPriceList.Add(0f); } setItemPriceList[num16] = num17; GamePatches.ApplyingRemotePrice = true; try { CEventManager.QueueEvent((CEvent)new CEventPlayer_ItemPriceChanged((EItemType)num16, num17)); break; } finally { GamePatches.ApplyingRemotePrice = false; } } break; } case MsgType.ObjMoveDelta: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br12 = Msg.Reader(msg.Payload)) { _objMoves.ApplyRemote(ObjMoveSync.ReadEntries(br12)); break; } } break; case MsgType.ObjMoveRequest: { if (Role != CoopRole.Host || !InGameLevel()) { break; } using BinaryReader br10 = Msg.Reader(msg.Payload); List entries = ObjMoveSync.ReadEntries(br10); _objMoves.ApplyRemote(entries); if (_net.ConnectionCount > 1) { Broadcast(MsgType.ObjMoveDelta, delegate(BinaryWriter bw) { ObjMoveSync.WriteEntries(bw, entries); }); } break; } case MsgType.CardPriceSet: { using BinaryReader binaryReader12 = Msg.Reader(msg.Payload); CardData val4 = Msg.ReadCard(binaryReader12); float num12 = binaryReader12.ReadSingle(); if (!InGameLevel()) { _pendingCardPrices.Add(new KeyValuePair(val4, num12)); break; } GamePatches.ApplyingRemotePrice = true; try { CPlayerData.SetCardPrice(val4, num12); break; } finally { GamePatches.ApplyingRemotePrice = false; } } case MsgType.RegisterState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br7 = Msg.Reader(msg.Payload)) { _registerMirror.Apply(RegisterServe.ReadStates(br7)); break; } } break; case MsgType.ServeRequest: { if (Role != CoopRole.Host || !InGameLevel()) { break; } using BinaryReader binaryReader11 = Msg.Reader(msg.Payload); int counterIndex = binaryReader11.ReadInt32(); string value3; string serverName = (PeerNames.TryGetValue(msg.ConnId, out value3) ? value3 : "player"); byte[] scanEcho; string status = RegisterServe.Serve(counterIndex, serverName, out scanEcho); Send(msg.ConnId, MsgType.ServeStatus, delegate(BinaryWriter bw) { bw.Write(status); }); if (scanEcho != null) { Send(msg.ConnId, MsgType.ScanEcho, delegate(BinaryWriter bw) { bw.Write(scanEcho); }); } break; } case MsgType.ServeStatus: { if (Role != CoopRole.Client) { break; } using (BinaryReader binaryReader8 = Msg.Reader(msg.Payload)) { RegisterLine = binaryReader8.ReadString(); RegisterLineTimer = 3f; } if (RegisterLine == "sale complete!") { try { CSingleton.Instance.ResetCounter(); } catch { } Guarded("reset-totals", RegisterServe.ClientResetTotals); } break; } case MsgType.ScanEcho: { if (Role != CoopRole.Client || !InGameLevel()) { break; } using BinaryReader binaryReader6 = Msg.Reader(msg.Payload); int num9 = binaryReader6.ReadByte(); bool flag2 = binaryReader6.ReadBoolean(); double price = binaryReader6.ReadDouble(); double hostTotal = binaryReader6.ReadDouble(); try { ShelfManager val = Object.FindObjectOfType(); if (!((Object)(object)val == (Object)null) && num9 < val.m_CashierCounterList.Count) { InteractableCashierCounter counter = val.m_CashierCounterList[num9]; CardData card = (flag2 ? Msg.ReadCard(binaryReader6) : null); EItemType itemType = (EItemType)((!flag2) ? binaryReader6.ReadInt32() : 0); RegisterServe.ApplyScanEcho(counter, flag2, price, hostTotal, itemType, card); } break; } catch { break; } } case MsgType.EconContrib: { if (Role != CoopRole.Host) { break; } using BinaryReader binaryReader5 = Msg.Reader(msg.Payload); byte b = binaryReader5.ReadByte(); float num8 = binaryReader5.ReadSingle(); switch (b) { case 1: CEventManager.QueueEvent((CEvent)new CEventPlayer_AddCoin(num8, false)); break; case 2: CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(num8, false)); break; case 3: CEventManager.QueueEvent((CEvent)new CEventPlayer_AddShopExp((int)num8, false)); break; case 4: CEventManager.QueueEvent((CEvent)new CEventPlayer_AddFame((int)num8, false)); break; } break; } case MsgType.Ping: Send(msg.ConnId, MsgType.Pong, null); break; case MsgType.Bye: { string text = "the host ended the session"; if (msg.Payload.Length != 0) { try { using BinaryReader binaryReader = Msg.Reader(msg.Payload); text = binaryReader.ReadString(); } catch { } } if (Role == CoopRole.Client) { ErrorLine = text; Shutdown("rejected: " + text); } else { _net.Kick(msg.ConnId); } break; } case MsgType.Pong: break; } } private void SendWorldTo(int connId) { int hostSlot; byte[] rawSave; byte[] rawBundle; try { hostSlot = CSingleton.Instance.m_CurrentSaveLoadSlotSelectedIndex; rawSave = SaveTransfer.BuildHostPayload(); try { rawBundle = SidecarTransfer.BuildBundle(hostSlot); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Sidecar bundle failed (sending base save only): " + ex.Message)); rawBundle = new byte[0]; } } catch (Exception ex2) { ErrorLine = "Could not snapshot the shop: " + ex2.Message; CoopPlugin.Log.LogError((object)ex2); return; } ICoopTransport net = _net; Thread thread = new Thread((ThreadStart)delegate { try { byte[] payload = Msg.Gzip(rawSave); byte[] bundle = ((rawBundle.Length != 0) ? Msg.Gzip(rawBundle) : rawBundle); CoopPlugin.Log.LogInfo((object)$"transfer: save {payload.Length / 1024} KB, mod data {bundle.Length / 1024} KB (compressed)"); net.Send(connId, Msg.Build(MsgType.Welcome, delegate(BinaryWriter bw) { bw.Write("1.0.6"); bw.Write(CoopPlugin.PlayerName.Value); bw.Write(payload.Length); bw.Write(hostSlot); bw.Write(bundle.Length); bw.Write((byte)connId); })); for (int num = 0; num < payload.Length; num += 131072) { int len = Math.Min(131072, payload.Length - num); int o = num; net.Send(connId, Msg.Build(MsgType.SaveChunk, delegate(BinaryWriter bw) { bw.Write(o); bw.Write(len); bw.Write(payload, o, len); })); } net.Send(connId, Msg.Build(MsgType.SaveDone, delegate(BinaryWriter bw) { bw.Write(payload.Length); })); for (int num2 = 0; num2 < bundle.Length; num2 += 131072) { int len2 = Math.Min(131072, bundle.Length - num2); int o2 = num2; net.Send(connId, Msg.Build(MsgType.BundleChunk, delegate(BinaryWriter bw) { bw.Write(o2); bw.Write(len2); bw.Write(bundle, o2, len2); })); } net.Send(connId, Msg.Build(MsgType.BundleDone, delegate(BinaryWriter bw) { bw.Write(bundle.Length); })); } catch (Exception ex3) { CoopPlugin.Log.LogError((object)("World send failed: " + ex3.Message)); } }); thread.IsBackground = true; thread.Name = "CoopWorldSend"; thread.Start(); } private void OnGUI() { _ui.Draw(this, _net); } } [BepInPlugin("com.zwhit.cardshopcoop", "CardShopCoop", "1.0.6")] public class CoopPlugin : BaseUnityPlugin { public const string Guid = "com.zwhit.cardshopcoop"; public const string Name = "CardShopCoop"; public const string Version = "1.0.6"; public static ManualLogSource Log; public static ConfigEntry Port; public static ConfigEntry LastJoinIP; public static ConfigEntry PlayerName; public static ConfigEntry SendRateHz; public static ConfigEntry AvatarsEnabled; public static ConfigEntry UiToggleKey; public static ConfigEntry EmoteKey; public static ConfigEntry ServeKey; public static ConfigEntry ClientWorldSlot; public static ConfigEntry AutoSyncCardDatabase; public static ConfigEntry ServeReach; private void Awake() { //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Invalid comparison between Unknown and I4 //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Expected O, but got Unknown //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; FileLog.Init(Paths.GameRootPath); ((BaseUnityPlugin)this).Logger.LogEvent += delegate(object _, LogEventArgs e) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) FileLog.Write($"{e.Level,-7} {e.Data}"); }; Port = ((BaseUnityPlugin)this).Config.Bind("Network", "Port", 27886, "TCP port used for hosting. Both PCs' firewalls must allow the game on this port."); LastJoinIP = ((BaseUnityPlugin)this).Config.Bind("Network", "LastJoinIP", "192.168.1.100", "IP address of the host PC (remembered after a successful join)."); PlayerName = ((BaseUnityPlugin)this).Config.Bind("Player", "Name", Environment.UserName, "Name shown above your head on the other player's screen."); SendRateHz = ((BaseUnityPlugin)this).Config.Bind("Network", "SendRateHz", 15f, "How many position updates per second to send (8-20 is sensible)."); if (Mathf.Approximately(SendRateHz.Value, 12f)) { SendRateHz.Value = 15f; } AvatarsEnabled = ((BaseUnityPlugin)this).Config.Bind("Player", "AvatarsEnabled", true, "Show the other player as a walking character in your shop."); UiToggleKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "UiToggleKey", (KeyCode)283, "Toggles the co-op window. (F3 is reserved for future co-op options.)"); if ((int)UiToggleKey.Value == 292) { UiToggleKey.Value = (KeyCode)283; } EmoteKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "EmoteKey", (KeyCode)103, "Sends a wave emote that pops above your avatar."); ServeKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "ServeKey", (KeyCode)118, "When JOINING: stand at the register and press this to serve the customer (scan items, take payment, give change)."); ClientWorldSlot = ((BaseUnityPlugin)this).Config.Bind("Network", "ClientWorldSlot", 7, "Save slot the co-op world uses when JOINING someone (your own slots 0-3 are never touched). On a PC dedicated to co-op you can set 0 for maximum mod-data fidelity."); AutoSyncCardDatabase = ((BaseUnityPlugin)this).Config.Bind("Network", "AutoSyncCardDatabase", true, "When your modded-card ID registry (EPL enum_values.json) differs from the host's, automatically install the host's copy (yours is backed up beside it) so you only need to restart and rejoin. Set false to handle the file yourself."); ServeReach = ((BaseUnityPlugin)this).Config.Bind("Player", "ServeReach", 1.6f, "How close (meters, to the counter's center) a JOINER must stand to serve the register or a trade customer. The counter itself is ~1m wide, so values below ~1.2 make it unreachable."); GamePatches.ApplyAll(new Harmony("com.zwhit.cardshopcoop")); GameObject val = new GameObject("CardShopCoop"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent(); Log.LogInfo((object)string.Format("{0} {1} loaded. Press {2} in-game to open the co-op window.", "CardShopCoop", "1.0.6", UiToggleKey.Value)); } } } namespace CardShopCoop.Util { public static class FileLog { private static StreamWriter _writer; private static readonly object Lock = new object(); private static int _lastFlushTick; public static string Path { get; private set; } public static void Init(string gameRoot) { try { int id = Process.GetCurrentProcess().Id; Path = System.IO.Path.Combine(gameRoot, "BepInEx", $"CardShopCoop_{id}.log"); _writer = new StreamWriter(Path, append: false); AppDomain.CurrentDomain.ProcessExit += delegate { Flush(); }; Write("log started " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Flush(); } catch { _writer = null; } } public static void Write(string line) { if (_writer == null) { return; } lock (Lock) { try { _writer.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] {line}"); int tickCount = Environment.TickCount; if (tickCount - _lastFlushTick > 1000) { _writer.Flush(); _lastFlushTick = tickCount; } } catch { } } } private static void Flush() { lock (Lock) { try { _writer?.Flush(); } catch { } } } } public static class ModParity { private static string _plugins; private static string _enum; public static string PluginHash() { if (_plugins != null) { return _plugins; } try { List list = new List(); foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { list.Add(pluginInfo.Key + "=" + pluginInfo.Value.Metadata.Version); } list.Sort(StringComparer.Ordinal); _plugins = Short(Sha1(string.Join(";", list))); } catch { _plugins = "err"; } return _plugins; } public static string EnumFilePath() { return Path.Combine(Application.persistentDataPath, "PrefabLoader", "enum_values.json"); } public static string EnumHash() { if (_enum != null) { return _enum; } try { string path = EnumFilePath(); _enum = (File.Exists(path) ? Short(Sha1Bytes(File.ReadAllBytes(path))) : "none"); } catch { _enum = "none"; } return _enum; } public static string InstallEnumFile(byte[] hostBytes) { string text = EnumFilePath(); try { if (File.Exists(text)) { if (SameBytes(File.ReadAllBytes(text), hostBytes)) { return "card database already synced - RESTART the game, then join again"; } string destFileName = text + ".coopbak-" + DateTime.Now.ToString("yyyyMMdd-HHmmss"); File.Copy(text, destFileName, overwrite: true); PruneBackups(text); } else { Directory.CreateDirectory(Path.GetDirectoryName(text)); } File.WriteAllBytes(text, hostBytes); return "card database synced from host (your old file was backed up) - RESTART the game, then join again"; } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("enum sync failed: " + ex.Message)); return "could not update the card database automatically - copy the host's enum_values.json manually (see mod page)"; } } private static bool SameBytes(byte[] a, byte[] b) { if (a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { return false; } } return true; } private static void PruneBackups(string basePath) { try { string[] files = Directory.GetFiles(Path.GetDirectoryName(basePath), Path.GetFileName(basePath) + ".coopbak-*"); Array.Sort(files, (IComparer?)StringComparer.Ordinal); for (int i = 0; i < files.Length - 3; i++) { File.Delete(files[i]); } } catch { } } private static string Sha1(string s) { return Sha1Bytes(Encoding.UTF8.GetBytes(s)); } private static string Sha1Bytes(byte[] data) { using SHA1 sHA = SHA1.Create(); return BitConverter.ToString(sHA.ComputeHash(data)).Replace("-", ""); } private static string Short(string hex) { if (hex.Length <= 16) { return hex; } return hex.Substring(0, 16); } } } namespace CardShopCoop.UI { public class CoopUI { public bool Visible = true; public static bool TextFieldFocused; private Rect _win = new Rect(24f, 96f, 380f, 10f); private string _ipField; private string _nameField; private string _lanIps; private string _lanIpsOther; private bool _revealIp; private bool _browserOpen; private string _searchField = ""; private int _page; private bool _publicLobby; private string _lobbyNameField = ""; private string _hostPwField = ""; private string _joinPwField = ""; private CSteamID _pwPromptLobby = CSteamID.Nil; private const int PageSize = 6; private GUIStyle _warnStyle; private GUIStyle _centerStyle; private GUIStyle _bigStyle; private GUIStyle _redStyle; private GUIStyle _wrapStyle; private KeyCode _hintKeySeen; private string _hintText; private string _errorSeen; private string _errorText; private string _hostTimeSeen; private string _hostTimeText; private string _promptSeen; private string _promptText; private string _registerSeen; private string _registerText; private static int IpRank(string ip) { if (ip.StartsWith("192.168.")) { return 0; } if (ip.StartsWith("10.")) { return 1; } if (ip.StartsWith("172.")) { return 2; } return 3; } public void Draw(CoopCore core, ICoopTransport net) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Expected O, but got Unknown //IL_03f9: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Expected O, but got Unknown //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) if (_ipField == null) { _ipField = CoopPlugin.LastJoinIP.Value; } if (_nameField == null) { _nameField = CoopPlugin.PlayerName.Value; } if (!Visible) { if (_hintText == null || _hintKeySeen != CoopPlugin.UiToggleKey.Value) { _hintKeySeen = CoopPlugin.UiToggleKey.Value; _hintText = $"CardShopCoop: {_hintKeySeen} for co-op"; } GUI.Label(new Rect(8f, (float)Screen.height - 22f, 400f, 20f), _hintText); if (core.ErrorLine.Length > 0) { if (_warnStyle == null) { _warnStyle = new GUIStyle(GUI.skin.label) { richText = true, fontStyle = (FontStyle)1 }; } if (core.ErrorLine != _errorSeen) { _errorSeen = core.ErrorLine; _errorText = "CO-OP: " + core.ErrorLine + ""; } GUI.Label(new Rect(8f, (float)Screen.height - 46f, 900f, 22f), _errorText, _warnStyle); } } if (CoopCore.Role == CoopRole.Client && core.HostTimeLine.Length > 0) { if (_centerStyle == null) { _centerStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, richText = true }; } if (core.HostTimeLine != _hostTimeSeen) { _hostTimeSeen = core.HostTimeLine; _hostTimeText = "" + core.HostTimeLine + " - co-op"; } GUI.Label(new Rect((float)Screen.width / 2f - 150f, 4f, 300f, 20f), _hostTimeText, _centerStyle); } if (core.RegisterLine.Length > 0 || core.PromptLine.Length > 0) { if (_bigStyle == null) { _bigStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)1, richText = true, fontStyle = (FontStyle)1, wordWrap = true }; } if (core.PromptLine.Length > 0) { if (core.PromptLine != _promptSeen) { _promptSeen = core.PromptLine; _promptText = "" + core.PromptLine + ""; } GUI.Label(new Rect((float)Screen.width / 2f - 300f, (float)Screen.height * 0.58f, 600f, 30f), _promptText, _bigStyle); } if (core.RegisterLine.Length > 0) { if (core.RegisterLine != _registerSeen) { _registerSeen = core.RegisterLine; _registerText = "" + core.RegisterLine + ""; } GUI.Label(new Rect((float)Screen.width / 2f - 350f, (float)Screen.height * 0.63f, 700f, 90f), _registerText, _bigStyle); } } if (Visible) { _win = GUILayout.Window(867530, _win, (WindowFunction)delegate { WindowFn(core, net); }, "CardShopCoop 1.0.6", Array.Empty()); } } private void WindowFn(CoopCore core, ICoopTransport net) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0691: Unknown result type (might be due to invalid IL or missing references) //IL_06ba: Unknown result type (might be due to invalid IL or missing references) //IL_06bf: Unknown result type (might be due to invalid IL or missing references) //IL_066f: Unknown result type (might be due to invalid IL or missing references) //IL_0674: Unknown result type (might be due to invalid IL or missing references) //IL_067b: Unknown result type (might be due to invalid IL or missing references) //IL_0687: Expected O, but got Unknown //IL_0730: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label(core.StatusLine, Array.Empty()); if (core.ErrorLine.Length > 0) { if (_redStyle == null) { _redStyle = new GUIStyle(GUI.skin.label) { wordWrap = true }; _redStyle.normal.textColor = new Color(1f, 0.45f, 0.4f); } GUILayout.Label(core.ErrorLine, _redStyle, Array.Empty()); } switch (CoopCore.Role) { case CoopRole.None: { if (_browserOpen) { DrawBrowser(core); break; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Your name:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(72f) }); GUI.SetNextControlName("coop_name"); string text = GUILayout.TextField(_nameField, 16, Array.Empty()); if (text != _nameField) { _nameField = text; if (text.Trim().Length > 0) { CoopPlugin.PlayerName.Value = text.Trim(); } } GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.Label("Host (load your shop first):", Array.Empty()); _publicLobby = GUILayout.Toggle(_publicLobby, " public lobby (shows in the browser)", Array.Empty()); if (_publicLobby) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Lobby name:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUI.SetNextControlName("coop_lobbyname"); _lobbyNameField = GUILayout.TextField(_lobbyNameField, 28, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Password:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUI.SetNextControlName("coop_hostpw"); _hostPwField = GUILayout.TextField(_hostPwField, 20, Array.Empty()); GUILayout.Label("(blank = open)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Host via Steam", Array.Empty())) { core.StartHostingSteam(_publicLobby, _lobbyNameField, _publicLobby ? _hostPwField : ""); } if (GUILayout.Button("Host via LAN", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { core.StartHosting(); } GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.Label("Join (stay on the main menu):", Array.Empty()); if (GUILayout.Button("Browse public lobbies", Array.Empty())) { _browserOpen = true; _page = 0; _pwPromptLobby = CSteamID.Nil; core.Lobby.RefreshList(); } GUILayout.Label("Steam friends: just accept the host's invite.", Array.Empty()); if (core.ErrorLine == "wrong password" && core.LastFailedLobby != CSteamID.Nil) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Password:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUI.SetNextControlName("coop_joinpw"); _joinPwField = GUILayout.TextField(_joinPwField, 20, Array.Empty()); if (GUILayout.Button("Retry", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { core.JoinSteam(core.LastFailedLobby, _joinPwField); } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); GUI.SetNextControlName("coop_ip"); _ipField = GUILayout.TextField(_ipField, 24, Array.Empty()); if (GUILayout.Button("Join LAN", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { core.Join(_ipField); } GUILayout.EndHorizontal(); GUILayout.Label($"LAN port {CoopPlugin.Port.Value} - all players need this mod + the same mods.", Array.Empty()); break; } case CoopRole.Host: if (core.IsSteamSession) { GUILayout.Label("Hosting through Steam - no IPs needed.", Array.Empty()); if (GUILayout.Button("Invite friend (Steam overlay)", Array.Empty())) { core.OpenSteamInvite(); } GUILayout.Label((net == null || net.ConnectionCount == 0) ? "Waiting for your invite to be accepted..." : PlayersLine(core), Array.Empty()); if (GUILayout.Button("Wave (" + ((object)CoopPlugin.EmoteKey.Value/*cast due to .constrained prefix*/).ToString() + ")", Array.Empty())) { core.SendEmote(); } if (GUILayout.Button("Stop hosting", Array.Empty())) { core.Disconnect(); } break; } if (_lanIps == null) { List list = LocalIPv4s(); list.Sort((string a, string b) => IpRank(a).CompareTo(IpRank(b))); _lanIps = ((list.Count > 0) ? list[0] : "(no LAN address found)"); _lanIpsOther = ((list.Count > 1) ? string.Join(" ", list.GetRange(1, list.Count - 1)) : ""); } GUILayout.Label("Give this to the other PC:", Array.Empty()); if (!_revealIp) { if (GUILayout.Button("click to show IP (hidden for streams)", Array.Empty())) { _revealIp = true; } } else { GUILayout.Label($"{_lanIps} (port {CoopPlugin.Port.Value})", Array.Empty()); if (_lanIpsOther.Length > 0) { GUILayout.Label("(other adapters, usually wrong: " + _lanIpsOther + ")", Array.Empty()); } } GUILayout.Label((net == null || net.ConnectionCount == 0) ? "Waiting for a player..." : PlayersLine(core), Array.Empty()); if (GUILayout.Button("Wave (" + ((object)CoopPlugin.EmoteKey.Value/*cast due to .constrained prefix*/).ToString() + ")", Array.Empty())) { core.SendEmote(); } if (GUILayout.Button("Stop hosting", Array.Empty())) { core.Disconnect(); } break; case CoopRole.Client: GUILayout.Label(PlayersLine(core), Array.Empty()); if (_wrapStyle == null) { _wrapStyle = new GUIStyle(GUI.skin.label) { wordWrap = true, richText = true }; } GUILayout.Label($"You're playing in the host's shop. At the register, click the customer's items to scan them, then click to take payment and give change ({CoopPlugin.ServeKey.Value} also works). Your own saves are protected.", _wrapStyle, Array.Empty()); if (GUILayout.Button("Wave (" + ((object)CoopPlugin.EmoteKey.Value/*cast due to .constrained prefix*/).ToString() + ")", Array.Empty())) { core.SendEmote(); } if (GUILayout.Button("Leave session", Array.Empty())) { core.Disconnect(); } break; } TextFieldFocused = GUI.GetNameOfFocusedControl()?.StartsWith("coop_") ?? false; GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f)); } private void DrawBrowser(CoopCore core) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Public lobbies", Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button(core.Lobby.ListRefreshing ? "..." : "Refresh", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { core.Lobby.RefreshList(); } if (GUILayout.Button("Back", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) })) { _browserOpen = false; _pwPromptLobby = CSteamID.Nil; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Search:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) }); GUI.SetNextControlName("coop_search"); string text = GUILayout.TextField(_searchField, 24, Array.Empty()); if (text != _searchField) { _searchField = text; _page = 0; } GUILayout.EndHorizontal(); List list = new List(); foreach (SteamLobby.LobbyRow lobby in core.Lobby.Lobbies) { if (_searchField.Length == 0 || (lobby.Name ?? "").IndexOf(_searchField, StringComparison.OrdinalIgnoreCase) >= 0) { list.Add(lobby); } } int num = Mathf.Max(1, (list.Count + 6 - 1) / 6); _page = Mathf.Clamp(_page, 0, num - 1); if (list.Count == 0) { GUILayout.Label(core.Lobby.ListRefreshing ? "Searching..." : "No lobbies found - hit Refresh, or host one!", Array.Empty()); } for (int i = _page * 6; i < list.Count && i < (_page + 1) * 6; i++) { SteamLobby.LobbyRow lobbyRow = list[i]; bool flag = lobbyRow.Ver == "1.0.6"; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(string.Format("{0}{1} ({2}/{3})", lobbyRow.HasPw ? "[pw] " : "", lobbyRow.Name, lobbyRow.Players, lobbyRow.Max) + (flag ? "" : (" v" + lobbyRow.Ver + "")), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.enabled = flag; if (GUILayout.Button("Join", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) })) { if (lobbyRow.HasPw) { _pwPromptLobby = lobbyRow.Id; _joinPwField = ""; } else { core.JoinSteam(lobbyRow.Id); _browserOpen = false; } } GUI.enabled = true; GUILayout.EndHorizontal(); if (_pwPromptLobby == lobbyRow.Id) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Password:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUI.SetNextControlName("coop_joinpw"); _joinPwField = GUILayout.TextField(_joinPwField, 20, Array.Empty()); if (GUILayout.Button("Go", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) })) { core.JoinSteam(lobbyRow.Id, _joinPwField); _browserOpen = false; _pwPromptLobby = CSteamID.Nil; } GUILayout.EndHorizontal(); } } GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = _page > 0; if (GUILayout.Button("< Prev", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _page--; } GUI.enabled = _page < num - 1; if (GUILayout.Button("Next >", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _page++; } GUI.enabled = true; GUILayout.Label($"page {_page + 1}/{num} - {list.Count} lobbies", Array.Empty()); GUILayout.EndHorizontal(); } private static string PlayersLine(CoopCore core) { if (core.PeerNames.Count == 0) { return "Linked."; } List values = new List(core.PeerNames.Values); return "Playing with: " + string.Join(", ", values); } private static List LocalIPv4s() { List list = new List(); try { NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { if (networkInterface.OperationalStatus != OperationalStatus.Up || networkInterface.NetworkInterfaceType == NetworkInterfaceType.Loopback) { continue; } foreach (UnicastIPAddressInformation unicastAddress in networkInterface.GetIPProperties().UnicastAddresses) { if (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork) { string text = unicastAddress.Address.ToString(); if (!text.StartsWith("169.254")) { list.Add(text); } } } } } catch { } if (list.Count == 0) { list.Add("(no LAN address found)"); } return list; } } } namespace CardShopCoop.Sync { public class AvatarManager { private struct Snapshot { public Vector3 Pos; public float Yaw; public float RecvTime; } private class RemoteAvatar { public GameObject Go; public Animator Anim; public bool HasMoveSpeed; public bool HasHoldingBox; public TMP_Text NameTag; public TMP_Text EmoteTag; public GameObject HoldProp; public Material HoldPropMat; public string Name = "Player"; public Vector3 TargetPos; public Vector3 Velocity; public float LastStateTime; public float TargetYaw; public float NetSpeed; public byte HoldState; public List HoldTypes; public List HoldCards; public readonly Snapshot[] Snaps = new Snapshot[4]; public int SnapHead = -1; public int SnapCount; public string HeldSig = ""; public string CardSig = ""; public string PendingBoxSig = ""; public string PendingCardSig = ""; public string PendingItemSig = ""; public readonly List HeldItems = new List(); public readonly List HeldCards3d = new List(); public GameObject BinderProp; public Item PackProp; public float PackTimer; public GameObject BoxProp; public Item BoxProdItem; public string BoxSig = ""; public float EmoteTimer; public bool EverPositioned; public bool HasState; public bool HoldingBoxPose; public bool HoldingBoxPoseSet; } private const int SnapBufferSize = 4; private const float InterpDelay = 2f / 15f; private const float MaxExtrapolation = 0.25f; private static readonly int MoveSpeedHash = Animator.StringToHash("MoveSpeed"); private static readonly int IsHoldingBoxHash = Animator.StringToHash("IsHoldingBox"); private static RestockManager _restock; private readonly Dictionary _avatars = new Dictionary(); private bool _loggedAnimParams; public static Transform ViewCamera; private static TMP_FontAsset _tagFont; public void SetName(int connId, string name) { if (string.IsNullOrEmpty(name)) { return; } if (_avatars.TryGetValue(connId, out var value)) { if (!(value.Name == name)) { value.Name = name; if ((Object)(object)value.NameTag != (Object)null) { value.NameTag.text = name; } if ((Object)(object)value.Go != (Object)null) { ((Object)value.Go).name = "CoopAvatar_" + name; } } } else { _avatars[connId] = new RemoteAvatar { Name = name }; } } public void UpdateState(int connId, Vector3 pos, float yaw, float speed, byte holdState, List holdTypes = null, List holdCards = null) { //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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Expected I4, but got Unknown //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Expected I4, but got Unknown if (!_avatars.TryGetValue(connId, out var value)) { value = new RemoteAvatar(); _avatars[connId] = value; } float time = Time.time; float num = time - value.LastStateTime; if (value.HasState && num > 0.01f && num < 1f) { Vector3 val = (pos - value.TargetPos) / num; val.y = 0f; value.Velocity = Vector3.ClampMagnitude(val, 6f); } else { value.Velocity = Vector3.zero; } value.LastStateTime = time; value.TargetPos = pos; value.TargetYaw = yaw; value.NetSpeed = speed; value.HoldState = holdState; value.HoldTypes = holdTypes; value.HoldCards = holdCards; value.HasState = true; value.SnapHead = (value.SnapHead + 1) % 4; value.Snaps[value.SnapHead] = new Snapshot { Pos = pos, Yaw = yaw, RecvTime = time }; if (value.SnapCount < 4) { value.SnapCount++; } value.PendingBoxSig = ((holdState != 1) ? "" : ((holdTypes != null && holdTypes.Count >= 2) ? (holdTypes[0] + ":" + holdTypes[1]) : "0:0")); if (holdState == 3 && holdCards != null && holdCards.Count > 0) { StringBuilder stringBuilder = new StringBuilder(); foreach (CardData holdCard in holdCards) { stringBuilder.Append((int)holdCard.monsterType).Append('/').Append((int)holdCard.expansionType) .Append('/') .Append(holdCard.isFoil ? 1 : 0) .Append(';'); } value.PendingCardSig = stringBuilder.ToString(); } else { value.PendingCardSig = ""; } value.PendingItemSig = ((holdState == 2 && holdTypes != null && holdTypes.Count > 0) ? string.Join(",", holdTypes) : ""); if (!value.EverPositioned && (Object)(object)value.Go != (Object)null) { value.Go.transform.position = pos; value.EverPositioned = true; } } public void ShowEmote(int connId) { ShowTag(connId, "\\o/ hi!", 2.5f); } public void ShowTag(int connId, string text, float seconds) { if (_avatars.TryGetValue(connId, out var value) && (Object)(object)value.EmoteTag != (Object)null) { value.EmoteTag.text = text; value.EmoteTimer = seconds; } } public void Remove(int connId) { if (_avatars.TryGetValue(connId, out var value)) { ReleaseHeld(value); DestroyBody(value); _avatars.Remove(connId); } } public void Clear() { foreach (RemoteAvatar value in _avatars.Values) { ReleaseHeld(value); DestroyBody(value); } _avatars.Clear(); } private static void DestroyBody(RemoteAvatar av) { if ((Object)(object)av.HoldPropMat != (Object)null) { Object.Destroy((Object)(object)av.HoldPropMat); av.HoldPropMat = null; } if ((Object)(object)av.Go != (Object)null) { Object.Destroy((Object)(object)av.Go); } } private static void ReleaseItems(RemoteAvatar av) { foreach (Item heldItem in av.HeldItems) { if ((Object)(object)heldItem != (Object)null) { try { ItemSpawnManager.DisableItem(heldItem); } catch { } } } av.HeldItems.Clear(); av.HeldSig = ""; } private static void ReleaseHeld(RemoteAvatar av) { ReleaseItems(av); ReleaseCards(av); if ((Object)(object)av.PackProp != (Object)null) { try { ItemSpawnManager.DisableItem(av.PackProp); } catch { } av.PackProp = null; } if ((Object)(object)av.BinderProp != (Object)null) { Object.Destroy((Object)(object)av.BinderProp); av.BinderProp = null; } ReleaseBoxProp(av); } private static void ReleaseBoxProp(RemoteAvatar av) { if ((Object)(object)av.BoxProdItem != (Object)null) { try { ItemSpawnManager.DisableItem(av.BoxProdItem); } catch { } av.BoxProdItem = null; } if ((Object)(object)av.BoxProp != (Object)null) { Object.Destroy((Object)(object)av.BoxProp); av.BoxProp = null; } av.BoxSig = ""; } private static void TrySpawnBoxProp(RemoteAvatar av, bool isBig, int itemType) { //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_restock == (Object)null) { _restock = Object.FindObjectOfType(); } RestockManager restock = _restock; InteractablePackagingBox_Item val = ((!isBig) ? restock?.m_PackageBoxSmallPrefab : restock?.m_PackageBoxPrefab); if ((Object)(object)val == (Object)null) { return; } GameObject val2 = new GameObject("CoopBoxHolder_tmp"); val2.SetActive(false); GameObject val3 = Object.Instantiate(((Component)val).gameObject, val2.transform); MonoBehaviour[] componentsInChildren = val3.GetComponentsInChildren(true); foreach (MonoBehaviour val4 in componentsInChildren) { if ((Object)(object)val4 != (Object)null) { Object.DestroyImmediate((Object)(object)val4); } } Rigidbody[] componentsInChildren2 = val3.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } Collider[] componentsInChildren3 = val3.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } Transform val5 = null; try { val5 = (((Object)(object)av.Anim != (Object)null) ? av.Anim.GetBoneTransform((HumanBodyBones)8) : null); } catch { } Transform transform = av.Go.transform; val3.transform.SetParent(((Object)(object)val5 != (Object)null) ? val5 : transform, false); val3.transform.rotation = transform.rotation; ((Object)val3).name = "CoopBoxProp"; val3.SetActive(true); Object.Destroy((Object)(object)val2); Vector3 val6 = (((Object)(object)val5 != (Object)null) ? (val5.position - transform.up * 0.05f) : (transform.position + transform.up * 1.16f)) + transform.forward * 0.42f; Renderer[] componentsInChildren4 = val3.GetComponentsInChildren(); if (componentsInChildren4.Length != 0) { Bounds bounds = componentsInChildren4[0].bounds; for (int j = 1; j < componentsInChildren4.Length; j++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren4[j].bounds); } Transform transform2 = val3.transform; transform2.position += val6 - ((Bounds)(ref bounds)).center; } else { val3.transform.position = val6; } av.BoxProp = val3; if (itemType <= 0) { return; } ItemMeshData itemMeshData = InventoryBase.GetItemMeshData((EItemType)itemType); if (itemMeshData != null) { Item item = ItemSpawnManager.GetItem(val3.transform); item.SetMesh(itemMeshData.mesh, itemMeshData.material, (EItemType)itemType, itemMeshData.meshSecondary, itemMeshData.materialSecondary, itemMeshData.materialList); ((Component)item).transform.position = val6 + transform.up * (isBig ? 0.3f : 0.22f); ((Component)item).transform.rotation = transform.rotation; ((Component)item).gameObject.SetActive(true); if ((Object)(object)item.m_Rigidbody != (Object)null) { item.m_Rigidbody.isKinematic = true; } if ((Object)(object)item.m_Collider != (Object)null) { ((Collider)item.m_Collider).enabled = false; } av.BoxProdItem = item; } } catch (Exception ex) { CoopPlugin.Log.LogInfo((object)("box prop unavailable (using cube): " + ex.Message)); } } private static void ReleaseCards(RemoteAvatar av) { foreach (InteractableCard3d item in av.HeldCards3d) { if ((Object)(object)item != (Object)null) { try { ((InteractableObject)item).OnDestroyed(); } catch { } } } av.HeldCards3d.Clear(); av.CardSig = ""; } public void ShowPackOpen(int connId, int packIndex) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if (!_avatars.TryGetValue(connId, out var value) || (Object)(object)value.Go == (Object)null) { return; } value.PackTimer = 4f; try { if ((Object)(object)value.Anim != (Object)null) { value.Anim.SetTrigger("GrabItem"); } } catch { } if (!((Object)(object)value.PackProp == (Object)null) || packIndex < 0) { return; } try { ItemMeshData itemMeshData = InventoryBase.GetItemMeshData((EItemType)packIndex); if (itemMeshData != null) { Item item = ItemSpawnManager.GetItem(value.Go.transform); item.SetMesh(itemMeshData.mesh, itemMeshData.material, (EItemType)packIndex, itemMeshData.meshSecondary, itemMeshData.materialSecondary, itemMeshData.materialList); ((Component)item).transform.localPosition = new Vector3(0f, 1.15f, 0.4f); ((Component)item).transform.localRotation = Quaternion.Euler(35f, 0f, 0f); ((Component)item).gameObject.SetActive(true); if ((Object)(object)item.m_Rigidbody != (Object)null) { item.m_Rigidbody.isKinematic = true; } if ((Object)(object)item.m_Collider != (Object)null) { ((Collider)item.m_Collider).enabled = false; } value.PackProp = item; } else { CoopPlugin.Log.LogInfo((object)$"pack-open visual: no mesh for pack index {packIndex}"); } } catch { } } public void Tick(float dt) { //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Unknown result type (might be due to invalid IL or missing references) //IL_062c: Unknown result type (might be due to invalid IL or missing references) //IL_064c: Unknown result type (might be due to invalid IL or missing references) //IL_06f2: Unknown result type (might be due to invalid IL or missing references) //IL_06f8: Unknown result type (might be due to invalid IL or missing references) //IL_06fd: Unknown result type (might be due to invalid IL or missing references) //IL_0702: Unknown result type (might be due to invalid IL or missing references) //IL_0730: Unknown result type (might be due to invalid IL or missing references) //IL_0736: Unknown result type (might be due to invalid IL or missing references) //IL_073b: Unknown result type (might be due to invalid IL or missing references) //IL_0740: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) if (!CoopPlugin.AvatarsEnabled.Value || !((Object)(object)CSingleton.Instance != (Object)null) || !CSingleton.Instance.m_IsGameLevel) { return; } object obj; if (!((Object)(object)ViewCamera != (Object)null)) { Camera main = Camera.main; obj = ((main != null) ? ((Component)main).transform : null); } else { obj = ViewCamera; } Transform val = (Transform)obj; foreach (RemoteAvatar value in _avatars.Values) { if ((Object)(object)value.Go == (Object)null) { if ((Object)(object)value.HoldPropMat != (Object)null) { Object.Destroy((Object)(object)value.HoldPropMat); value.HoldPropMat = null; value.HoldProp = null; value.PackProp = null; value.BinderProp = null; value.BoxProp = null; value.BoxProdItem = null; value.HeldItems.Clear(); value.HeldCards3d.Clear(); value.HeldSig = ""; value.CardSig = ""; value.BoxSig = ""; } if (value.HasState) { TrySpawn(value); } continue; } Transform transform = value.Go.transform; Vector3 pos; float yaw; if (value.SnapCount > 0) { SampleSnapshots(value, Time.time - 2f / 15f, dt, out pos, out yaw); } else { pos = value.TargetPos; yaw = value.TargetYaw; } Vector3 val2 = transform.position - pos; bool flag = ((Vector3)(ref val2)).sqrMagnitude > 25f; float num = 1f - Mathf.Exp(-14f * dt); transform.position = (flag ? pos : Vector3.Lerp(transform.position, pos, num)); Quaternion val3 = Quaternion.Euler(0f, yaw, 0f); transform.rotation = (flag ? val3 : Quaternion.Slerp(transform.rotation, val3, num)); if ((Object)(object)value.Anim != (Object)null) { if (value.HasMoveSpeed) { float num2 = value.Anim.GetFloat(MoveSpeedHash); value.Anim.SetFloat(MoveSpeedHash, Mathf.Lerp(num2, value.NetSpeed, 1f - Mathf.Exp(-8f * dt))); } if (value.HasHoldingBox) { bool flag2 = value.HoldState != 0; if (!value.HoldingBoxPoseSet || value.HoldingBoxPose != flag2) { value.Anim.SetBool(IsHoldingBoxHash, flag2); value.HoldingBoxPose = flag2; value.HoldingBoxPoseSet = true; } } } bool flag3 = value.HoldState == 1; if (value.PendingBoxSig != value.BoxSig) { ReleaseBoxProp(value); value.BoxSig = value.PendingBoxSig; if (flag3) { bool isBig = value.HoldTypes != null && value.HoldTypes.Count >= 1 && value.HoldTypes[0] == 1; int itemType = ((value.HoldTypes != null && value.HoldTypes.Count >= 2) ? value.HoldTypes[1] : 0); TrySpawnBoxProp(value, isBig, itemType); } } bool flag4 = flag3 && (Object)(object)value.BoxProp == (Object)null; if ((Object)(object)value.HoldProp != (Object)null && value.HoldProp.activeSelf != flag4) { value.HoldProp.SetActive(flag4); value.HoldProp.transform.localScale = new Vector3(0.34f, 0.27f, 0.34f); } if (value.PendingCardSig != value.CardSig) { ReleaseCards(value); value.CardSig = value.PendingCardSig; if (value.CardSig.Length > 0 && value.HoldCards != null) { for (int i = 0; i < value.HoldCards.Count; i++) { try { Card3dUIGroup cardUI = CSingleton.Instance.GetCardUI(); InteractableCard3d component = ((Component)ShelfManager.SpawnInteractableObject((EObjectType)9)).GetComponent(); cardUI.m_CardUI.SetCardUI(value.HoldCards[i]); ((Component)component).transform.SetParent(value.Go.transform, false); float num3 = (float)i - (float)(value.HoldCards.Count - 1) * 0.5f; ((Component)component).transform.localPosition = new Vector3(num3 * 0.08f, 1.15f, 0.38f); ((Component)component).transform.localRotation = Quaternion.Euler(30f, num3 * -9f, 0f); ((Component)cardUI).transform.position = ((Component)component).transform.position; ((Component)cardUI).transform.rotation = ((Component)component).transform.rotation; component.SetCardUIFollow(cardUI); component.SetEnableCollision(false); value.HeldCards3d.Add(component); } catch { } } } } if ((Object)(object)value.PackProp != (Object)null) { value.PackTimer -= dt; if (value.PackTimer <= 0f) { try { ItemSpawnManager.DisableItem(value.PackProp); } catch { } value.PackProp = null; } } bool flag5 = value.HoldState == 4; if (flag5 && (Object)(object)value.BinderProp == (Object)null) { TrySpawnBinder(value); } if ((Object)(object)value.BinderProp != (Object)null && value.BinderProp.activeSelf != flag5) { value.BinderProp.SetActive(flag5); } if (value.PendingItemSig != value.HeldSig) { ReleaseItems(value); value.HeldSig = value.PendingItemSig; if (value.HeldSig.Length > 0 && value.HoldTypes != null) { for (int j = 0; j < value.HoldTypes.Count; j++) { try { ItemMeshData itemMeshData = InventoryBase.GetItemMeshData((EItemType)value.HoldTypes[j]); if (itemMeshData != null) { Item item = ItemSpawnManager.GetItem(value.Go.transform); item.SetMesh(itemMeshData.mesh, itemMeshData.material, (EItemType)value.HoldTypes[j], itemMeshData.meshSecondary, itemMeshData.materialSecondary, itemMeshData.materialList); ((Component)item).transform.localPosition = new Vector3(0f, 1.04f + 0.018f * (float)j, 0.36f + 0.055f * (float)j); ((Component)item).transform.localRotation = Quaternion.Euler(14f, 0f, 0f); ((Component)item).gameObject.SetActive(true); if ((Object)(object)item.m_Rigidbody != (Object)null) { item.m_Rigidbody.isKinematic = true; } if ((Object)(object)item.m_Collider != (Object)null) { ((Collider)item.m_Collider).enabled = false; } value.HeldItems.Add(item); } } catch { } } } } if ((Object)(object)val != (Object)null) { if ((Object)(object)value.NameTag != (Object)null) { value.NameTag.transform.rotation = Quaternion.LookRotation(value.NameTag.transform.position - val.position); } if ((Object)(object)value.EmoteTag != (Object)null) { value.EmoteTag.transform.rotation = Quaternion.LookRotation(value.EmoteTag.transform.position - val.position); } } if (value.EmoteTimer > 0f) { value.EmoteTimer -= dt; if (value.EmoteTimer <= 0f && (Object)(object)value.EmoteTag != (Object)null) { value.EmoteTag.text = ""; } } } } private static void SampleSnapshots(RemoteAvatar av, float renderTime, float dt, out Vector3 pos, out float yaw) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) Snapshot snapshot = av.Snaps[av.SnapHead]; if (renderTime >= snapshot.RecvTime) { av.Velocity *= Mathf.Exp(-3f * dt); float num = Mathf.Min(renderTime - snapshot.RecvTime, 0.25f); pos = snapshot.Pos + av.Velocity * num; yaw = snapshot.Yaw; return; } int num2 = (av.SnapHead - av.SnapCount + 1 + 4) % 4; Snapshot snapshot2 = av.Snaps[num2]; for (int i = 1; i < av.SnapCount; i++) { Snapshot snapshot3 = av.Snaps[(num2 + i) % 4]; if (renderTime <= snapshot3.RecvTime) { float num3 = snapshot3.RecvTime - snapshot2.RecvTime; float num4 = ((num3 > 0.0001f) ? ((renderTime - snapshot2.RecvTime) / num3) : 1f); pos = Vector3.Lerp(snapshot2.Pos, snapshot3.Pos, num4); yaw = Mathf.LerpAngle(snapshot2.Yaw, snapshot3.Yaw, num4); return; } snapshot2 = snapshot3; } pos = snapshot2.Pos; yaw = snapshot2.Yaw; } private void TrySpawn(RemoteAvatar av) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Unknown result type (might be due to invalid IL or missing references) //IL_04ef: Unknown result type (might be due to invalid IL or missing references) //IL_055b: Unknown result type (might be due to invalid IL or missing references) //IL_058b: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Unknown result type (might be due to invalid IL or missing references) CustomerManager instance = CSingleton.Instance; if ((Object)(object)instance == (Object)null) { return; } int num = 17; string name = av.Name; foreach (char c in name) { num = num * 31 + c; } bool flag = (num & 1) == 1; Customer val = (flag ? instance.m_CustomerFemalePrefab : instance.m_CustomerPrefab); if ((Object)(object)val == (Object)null) { val = (((Object)(object)instance.m_CustomerPrefab != (Object)null) ? instance.m_CustomerPrefab : instance.m_CustomerFemalePrefab); } if ((Object)(object)val == (Object)null) { return; } GameObject val2 = new GameObject("CoopAvatarHolder_tmp"); val2.SetActive(false); GameObject val3 = Object.Instantiate(((Component)val).gameObject, val2.transform); val3.transform.SetParent((Transform)null, false); val3.transform.position = av.TargetPos; val3.SetActive(true); Object.Destroy((Object)(object)val2); Customer component = val3.GetComponent(); try { if ((Object)(object)component != (Object)null) { component.RandomizeCharacterMesh(); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Avatar dressing failed (spawning undressed): " + ex.Message)); } if ((Object)(object)component != (Object)null) { try { if ((Object)(object)component.m_ShoppingBagTransform != (Object)null) { ((Component)component.m_ShoppingBagTransform).gameObject.SetActive(false); } if ((Object)(object)component.m_CustomerCash != (Object)null) { ((Component)component.m_CustomerCash).gameObject.SetActive(false); } if ((Object)(object)component.m_GameCardFanOut != (Object)null) { component.m_GameCardFanOut.SetActive(false); } if ((Object)(object)component.m_GameCardSingle != (Object)null) { component.m_GameCardSingle.SetActive(false); } if ((Object)(object)component.m_CleanFX != (Object)null) { component.m_CleanFX.SetActive(false); } if ((Object)(object)component.m_ExclaimationMesh != (Object)null) { component.m_ExclaimationMesh.SetActive(false); } if ((Object)(object)component.m_InteractCollider != (Object)null) { component.m_InteractCollider.SetActive(false); } if ((Object)(object)component.m_SmellyFX != (Object)null) { component.m_SmellyFX.SetActive(false); } } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("Avatar prop hiding partial: " + ex2.Message)); } } MonoBehaviour[] componentsInChildren = val3.GetComponentsInChildren(true); foreach (MonoBehaviour val4 in componentsInChildren) { if (!((Object)(object)val4 == (Object)null)) { switch (((object)val4).GetType().Name) { case "CopyPose": case "BlendshapeManager": case "ScaleCharacter": case "TransformBone": case "MipBiasAdjust": continue; } Object.DestroyImmediate((Object)(object)val4); } } Component[] componentsInChildren2 = val3.GetComponentsInChildren(true); foreach (Component val5 in componentsInChildren2) { if (!((Object)(object)val5 == (Object)null)) { switch (((object)val5).GetType().Name) { case "NavMeshAgent": case "NavMeshObstacle": case "Seeker": case "FunnelModifier": Object.DestroyImmediate((Object)(object)val5); break; } } } Collider[] componentsInChildren3 = val3.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } Rigidbody[] componentsInChildren4 = val3.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren4.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren4[i]); } ((Object)val3).name = "CoopAvatar_" + av.Name; av.Go = val3; av.Anim = val3.GetComponentInChildren(true); av.EverPositioned = true; av.HoldingBoxPoseSet = false; if ((Object)(object)av.Anim != (Object)null) { AnimatorControllerParameter[] parameters = av.Anim.parameters; foreach (AnimatorControllerParameter obj in parameters) { if (obj.name == "MoveSpeed") { av.HasMoveSpeed = true; } if (obj.name == "IsHoldingBox") { av.HasHoldingBox = true; } } if (!_loggedAnimParams) { _loggedAnimParams = true; StringBuilder stringBuilder = new StringBuilder("Avatar animator params: "); parameters = av.Anim.parameters; foreach (AnimatorControllerParameter val6 in parameters) { stringBuilder.Append(val6.name).Append(' '); } CoopPlugin.Log.LogInfo((object)stringBuilder.ToString()); } } GameObject val7 = GameObject.CreatePrimitive((PrimitiveType)3); Object.DestroyImmediate((Object)(object)val7.GetComponent()); ((Object)val7).name = "CoopHoldProp"; val7.transform.SetParent(val3.transform, false); val7.transform.localPosition = new Vector3(0f, 1.05f, 0.45f); val7.transform.localRotation = Quaternion.identity; MeshRenderer component2 = val7.GetComponent(); if ((Object)(object)component2 != (Object)null) { av.HoldPropMat = ((Renderer)component2).material; av.HoldPropMat.color = new Color(0.72f, 0.55f, 0.35f); } val7.SetActive(false); av.HoldProp = val7; av.NameTag = MakeTag(val3.transform, av.Name, 2.25f, Color.white); av.EmoteTag = MakeTag(val3.transform, "", 2.55f, new Color(1f, 0.85f, 0.2f)); CoopPlugin.Log.LogInfo((object)("Spawned co-op avatar for '" + av.Name + "' (" + (flag ? "female" : "male") + " model)")); } private static void TrySpawnBinder(RemoteAvatar av) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) try { CollectionBinderFlipAnimCtrl val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { return; } GameObject val2 = new GameObject("CoopBinderHolder_tmp"); val2.SetActive(false); GameObject val3 = Object.Instantiate(((Component)val).gameObject, val2.transform); MonoBehaviour[] componentsInChildren = val3.GetComponentsInChildren(true); foreach (MonoBehaviour val4 in componentsInChildren) { if ((Object)(object)val4 != (Object)null) { Object.DestroyImmediate((Object)(object)val4); } } Collider[] componentsInChildren2 = val3.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } val3.transform.SetParent(av.Go.transform, false); val3.transform.localPosition = new Vector3(0f, 1.1f, 0.38f); val3.transform.localRotation = Quaternion.Euler(35f, 0f, 0f); val3.transform.localScale = Vector3.one * 0.8f; ((Object)val3).name = "CoopBinder"; val3.SetActive(true); Object.Destroy((Object)(object)val2); av.BinderProp = val3; } catch (Exception ex) { CoopPlugin.Log.LogInfo((object)("binder prop unavailable: " + ex.Message)); } } private static TMP_Text MakeTag(Transform parent, string text, float height, Color color) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CoopTag"); val.transform.SetParent(parent, false); val.transform.localPosition = new Vector3(0f, height, 0f); TextMeshPro val2 = val.AddComponent(); ((TMP_Text)val2).text = text; ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).fontSize = 1.8f; ((Graphic)val2).color = color; ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)0; ((TMP_Text)val2).rectTransform.sizeDelta = new Vector2(4f, 1f); if ((Object)(object)_tagFont == (Object)null) { _tagFont = TMP_Settings.defaultFontAsset; if ((Object)(object)_tagFont == (Object)null) { TMP_Text val3 = Object.FindObjectOfType(true); if ((Object)(object)val3 != (Object)null) { _tagFont = val3.font; } } } if ((Object)(object)_tagFont != (Object)null) { ((TMP_Text)val2).font = _tagFont; } return (TMP_Text)(object)val2; } } public class BoxSync { public struct Entry { public int Type; public int Count; public bool IsBig; public bool IsOpen; public bool Carried; public Vector3 Pos; public float Yaw; } public static Func IsLocallyCarried = (InteractablePackagingBox_Item _) => false; private static readonly MethodInfo MiSetOpenClose = AccessTools.Method(typeof(InteractablePackagingBox_Item), "SetOpenCloseBox", (Type[])null, (Type[])null); private static readonly FieldInfo FiAmountToSpawn = AccessTools.Field(typeof(InteractablePackagingBox_Item), "m_ItemAmountToSpawn"); private static readonly FieldInfo FiStoredList = AccessTools.Field(typeof(ShelfCompartment), "m_StoredItemList"); private readonly List _lastApplied = new List(); private readonly HashSet _carriedLastTick = new HashSet(); private readonly HashSet _remoteCarried = new HashSet(); private readonly Dictionary _recentlyReleased = new Dictionary(); private readonly Dictionary _locallyTouched = new Dictionary(); private readonly HashSet _hostCarriedLastTick = new HashSet(); private readonly Dictionary _hostRecentlyReleased = new Dictionary(); private float _timer; private int _lastHostHash; private float _hostHeal; private readonly List _reportBuf = new List(); private RestockManager _rm; public Action> OnHostSnapshot; public Action> OnClientChanges; public Action OnLocalRemoved; public static Action LocalBoxDestroyed; public static bool ApplyingRemote; public void Reset() { _lastApplied.Clear(); _carriedLastTick.Clear(); _remoteCarried.Clear(); _recentlyReleased.Clear(); _locallyTouched.Clear(); _hostCarriedLastTick.Clear(); _hostRecentlyReleased.Clear(); _timer = -0.6f; _lastHostHash = 0; _hostHeal = 0f; _rm = null; } private RestockManager Rm() { if ((Object)(object)_rm == (Object)null) { _rm = Object.FindObjectOfType(); } return _rm; } private static List LiveBoxes() { return RestockManager.GetItemPackagingBoxList(); } private static Entry Snapshot(InteractablePackagingBox_Item box) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected I4, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) return new Entry { Type = (int)box.m_ItemCompartment.GetItemType(), Count = box.m_ItemCompartment.GetItemCount(), IsBig = box.m_IsBigBox, IsOpen = ((InteractablePackagingBox)box).IsBoxOpened(), Carried = IsLocallyCarried(box), Pos = ((Component)box).transform.position, Yaw = ((Component)box).transform.eulerAngles.y }; } private static bool Differs(Entry a, Entry b) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (a.Type == b.Type && a.Count == b.Count && a.IsBig == b.IsBig && a.IsOpen == b.IsOpen) { Vector3 val = a.Pos - b.Pos; if (!(((Vector3)(ref val)).sqrMagnitude > 0.01f)) { return Mathf.Abs(Mathf.DeltaAngle(a.Yaw, b.Yaw)) > 3f; } } return true; } public void HostTick(float dt, bool active) { //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) if (!active || (Object)(object)Rm() == (Object)null) { return; } bool flag = false; try { List list = LiveBoxes(); for (int i = 0; i < list.Count; i++) { if ((Object)(object)list[i] == (Object)null) { continue; } if (IsLocallyCarried(list[i])) { if (_hostCarriedLastTick.Add(i)) { flag = true; } } else if (_hostCarriedLastTick.Remove(i)) { flag = true; _hostRecentlyReleased[i] = Time.realtimeSinceStartupAsDouble; } } } catch { } _timer += dt; if (!flag && _timer < 1.5f) { return; } if (_timer >= 1.5f) { _timer -= 1.5f; } if (flag) { _lastHostHash = 0; } try { List list2 = LiveBoxes(); List list3 = new List(Mathf.Min(list2.Count, 250)); for (int j = 0; j < list2.Count; j++) { if (list3.Count >= 250) { break; } if (!((Object)(object)list2[j] == (Object)null)) { Entry item = Snapshot(list2[j]); if (_remoteCarried.Contains(j)) { item.Carried = true; } list3.Add(item); } } int num = 17; for (int k = 0; k < list3.Count; k++) { Entry entry = list3[k]; num = num * 31 + entry.Type; num = num * 31 + entry.Count; num = num * 31 + (int)((entry.IsBig ? 1u : 0u) | (uint)(entry.IsOpen ? 2 : 0) | (uint)(entry.Carried ? 4 : 0)); num = num * 31 + (int)(entry.Pos.x * 8f); num = num * 31 + (int)(entry.Pos.z * 8f); } _hostHeal += 1.5f; if (num != _lastHostHash || !(_hostHeal < 10f)) { _lastHostHash = num; _hostHeal = 0f; OnHostSnapshot?.Invoke(list3); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync host: " + ex.Message)); } } public void HostApplyRequest(List entries) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 List list = LiveBoxes(); for (int i = 0; i < entries.Count && i < list.Count; i++) { InteractablePackagingBox_Item val = list[i]; if (!((Object)(object)val == (Object)null) && (int)val.m_ItemCompartment.GetItemType() == entries[i].Type && !IsLocallyCarried(val) && (!_hostRecentlyReleased.TryGetValue(i, out var value) || !(Time.realtimeSinceStartupAsDouble - value < 6.0))) { if (entries[i].Carried) { _remoteCarried.Add(i); } else { _remoteCarried.Remove(i); } ApplyToBox(val, entries[i]); } } _timer = 99f; _lastHostHash = 0; } public void HostApplyRemoval(int index, int type) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 List list = LiveBoxes(); if (index < 0 || index >= list.Count || (Object)(object)list[index] == (Object)null || (int)list[index].m_ItemCompartment.GetItemType() != type || IsLocallyCarried(list[index])) { return; } ApplyingRemote = true; try { ((InteractableObject)list[index]).OnDestroyed(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync removal: " + ex.Message)); } finally { ApplyingRemote = false; } _remoteCarried.Clear(); } public void HostNotifyLocalDestroyed() { _remoteCarried.Clear(); } public void NotifyLocalDestroyed(InteractablePackagingBox_Item box) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected I4, but got Unknown int num = LiveBoxes().IndexOf(box); if (num >= 0) { int arg = 0; try { arg = (int)box.m_ItemCompartment.GetItemType(); } catch { } if (num < _lastApplied.Count) { _lastApplied.RemoveAt(num); } _carriedLastTick.Clear(); _locallyTouched.Clear(); _recentlyReleased.Clear(); OnLocalRemoved?.Invoke(num, arg); } } public void ClientApply(List hostList) { ApplyingRemote = true; try { ClientApplyInner(hostList); } finally { ApplyingRemote = false; } } private void ClientApplyInner(List hostList) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Invalid comparison between Unknown and I4 List list = LiveBoxes(); for (int num = list.Count - 1; num >= hostList.Count; num--) { try { if ((Object)(object)list[num] != (Object)null) { ((InteractableObject)list[num]).OnDestroyed(); } } catch { } } for (int i = 0; i < hostList.Count; i++) { Entry want = hostList[i]; InteractablePackagingBox_Item val = ((i < list.Count) ? list[i] : null); if ((Object)(object)val != (Object)null && ((int)val.m_ItemCompartment.GetItemType() != want.Type || val.m_IsBigBox != want.IsBig)) { try { ((InteractableObject)val).OnDestroyed(); } catch { } val = null; list = LiveBoxes(); } if ((Object)(object)val == (Object)null) { try { val = RestockManager.SpawnPackageBoxItem((EItemType)want.Type, want.Count, want.IsBig); list = LiveBoxes(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync spawn: " + ex.Message)); continue; } } if (!IsLocallyCarried(val)) { double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if ((!want.Carried || !_recentlyReleased.TryGetValue(i, out var value) || !(realtimeSinceStartupAsDouble - value < 6.0)) && (!_locallyTouched.TryGetValue(i, out var value2) || !(realtimeSinceStartupAsDouble - value2 < 6.0))) { ApplyToBox(val, want, !want.Carried); } } } _lastApplied.Clear(); _lastApplied.AddRange(hostList); } public void ClientTick(float dt, bool active) { if (!active || (Object)(object)Rm() == (Object)null || _lastApplied.Count == 0) { return; } bool flag = false; try { List list = LiveBoxes(); int num = Mathf.Min(list.Count, _lastApplied.Count); for (int i = 0; i < num; i++) { if ((Object)(object)list[i] == (Object)null) { continue; } if (IsLocallyCarried(list[i])) { if (_carriedLastTick.Add(i)) { flag = true; } } else if (_carriedLastTick.Remove(i)) { flag = true; _recentlyReleased[i] = Time.realtimeSinceStartupAsDouble; } } } catch { } _timer += dt; if (!flag && _timer < 1.5f) { return; } if (_timer >= 1.5f) { _timer -= 1.5f; } try { List list2 = LiveBoxes(); bool flag2 = flag; _reportBuf.Clear(); List reportBuf = _reportBuf; double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; for (int j = 0; j < _lastApplied.Count && j < list2.Count && (Object)(object)list2[j] != (Object)null; j++) { if (IsLocallyCarried(list2[j])) { Entry item = _lastApplied[j]; item.Carried = true; reportBuf.Add(item); continue; } if (_lastApplied[j].Carried && (!_recentlyReleased.TryGetValue(j, out var value) || !(realtimeSinceStartupAsDouble - value < 6.0))) { reportBuf.Add(_lastApplied[j]); continue; } Entry entry = Snapshot(list2[j]); if (Differs(entry, _lastApplied[j])) { flag2 = true; _locallyTouched[j] = realtimeSinceStartupAsDouble; } reportBuf.Add(entry); } if (flag2) { OnClientChanges?.Invoke(reportBuf); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync client: " + ex.Message)); } } private static void ApplyToBox(InteractablePackagingBox_Item box, Entry want, bool applyPosition = true) { //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) try { if (want.Carried) { if (((Component)box).gameObject.activeSelf) { try { box.m_ItemCompartment.SetPriceTagVisibility(false); } catch { } ((Component)box).gameObject.SetActive(false); } return; } if (!((Component)box).gameObject.activeSelf) { ((Component)box).gameObject.SetActive(true); try { box.m_ItemCompartment.SetPriceTagVisibility(true); } catch { } } if (((InteractablePackagingBox)box).IsBoxOpened() != want.IsOpen && MiSetOpenClose != null) { try { MiSetOpenClose.Invoke(box, null); } catch { } } ShelfCompartment itemCompartment = box.m_ItemCompartment; if (itemCompartment.GetItemCount() != want.Count) { if (((InteractablePackagingBox)box).IsBoxOpened()) { if (FiStoredList?.GetValue(itemCompartment) is List { Count: >0 } list) { foreach (Item item in new List(list)) { if (!((Object)(object)item == (Object)null)) { itemCompartment.RemoveItem(item); ItemSpawnManager.DisableItem(item); } } list.Clear(); } if (want.Count > 0) { itemCompartment.SpawnItem(want.Count, true); } else { itemCompartment.PreSpawnItemUpdate(0); } } else { itemCompartment.PreSpawnItemUpdate(want.Count); FiAmountToSpawn?.SetValue(box, want.Count); } } if (applyPosition) { Transform transform = ((Component)box).transform; Vector3 val = transform.position - want.Pos; if (((Vector3)(ref val)).sqrMagnitude > 0.01f || Mathf.Abs(Mathf.DeltaAngle(transform.eulerAngles.y, want.Yaw)) > 3f) { transform.SetPositionAndRotation(want.Pos, Quaternion.Euler(0f, want.Yaw, 0f)); ObjMoveSync.SyncTagGroup(transform); } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync apply: " + ex.Message)); } } public static void WriteEntries(BinaryWriter bw, List entries) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)Mathf.Min(entries.Count, 250)); for (int i = 0; i < entries.Count && i < 250; i++) { Entry entry = entries[i]; bw.Write(entry.Type); bw.Write((ushort)Mathf.Clamp(entry.Count, 0, 65535)); bw.Write((byte)((entry.IsBig ? 1u : 0u) | (uint)(entry.IsOpen ? 2 : 0) | (uint)(entry.Carried ? 4 : 0))); bw.Write(entry.Pos.x); bw.Write(entry.Pos.y); bw.Write(entry.Pos.z); bw.Write(entry.Yaw); } } public static List ReadEntries(BinaryReader br) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) int num = br.ReadByte(); List list = new List(num); for (int i = 0; i < num; i++) { Entry item = new Entry { Type = br.ReadInt32(), Count = br.ReadUInt16() }; byte b = br.ReadByte(); item.IsBig = (b & 1) != 0; item.IsOpen = (b & 2) != 0; item.Carried = (b & 4) != 0; item.Pos = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); item.Yaw = br.ReadSingle(); list.Add(item); } return list; } } public class CardBoxSync { public struct Entry { public List Cards; public bool Carried; public Vector3 Pos; public float Yaw; } private const int MaxBoxes = 16; private const int MaxCards = 16; public static CardBoxSync Instance; public static Func IsLocallyCarried = (InteractablePackagingBox_Card _) => false; public Action> SendOp; public Action> BroadcastState; public static bool ApplyingRemote; private const byte OpReport = 0; private const byte OpCollect = 1; private const byte OpRemoved = 2; private static readonly FieldInfo FiStoredCards = AccessTools.Field(typeof(InteractablePackagingBox_Card), "m_StoredCardList"); private readonly List _lastApplied = new List(); private readonly HashSet _carriedLastTick = new HashSet(); private readonly HashSet _remoteCarried = new HashSet(); private readonly Dictionary _recentlyReleased = new Dictionary(); private readonly Dictionary _locallyTouched = new Dictionary(); private readonly Dictionary _recentlyCollected = new Dictionary(); private float _timer; private int _lastHostHash; private float _hostHeal; private RestockManager _rm; private Transform _spawnAnchor; private static readonly List EmptyCards = new List(); public CardBoxSync() { Instance = this; } public void Reset() { _lastApplied.Clear(); _carriedLastTick.Clear(); _remoteCarried.Clear(); _recentlyReleased.Clear(); _locallyTouched.Clear(); _recentlyCollected.Clear(); _timer = -8.4f; _lastHostHash = 0; _hostHeal = 0f; _rm = null; } public void ForceResend() { _lastHostHash = 0; _hostHeal = 999f; } private RestockManager Rm() { if ((Object)(object)_rm == (Object)null) { _rm = Object.FindObjectOfType(); } return _rm; } private static List LiveBoxes() { return RestockManager.GetCardPackagingBoxList(); } private static bool InGameLevel() { CGameManager instance = CSingleton.Instance; if ((Object)(object)instance != (Object)null) { return instance.m_IsGameLevel; } return false; } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown Try(h, typeof(InteractablePackagingBox_Card), "OnPressOpenBox", new HarmonyMethod(typeof(CardBoxSync), "OpenBoxPrefix", (Type[])null)); Try(h, typeof(InteractablePackagingBox_Card), "OnDestroyed", new HarmonyMethod(typeof(CardBoxSync), "DestroyedPrefix", (Type[])null)); } public static bool OpenBoxPrefix(InteractablePackagingBox_Card __instance) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } try { Instance?.ClientCollect(__instance); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync collect: " + ex.Message)); } return false; } public static bool DestroyedPrefix(InteractablePackagingBox_Card __instance) { if (!ApplyingRemote) { Instance?.OnLocalDestroyed(__instance); } return true; } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } public void HostTick(float dt, bool inGame) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) if (!inGame || (Object)(object)Rm() == (Object)null) { return; } _timer += dt; if (_timer < 1.5f) { return; } _timer -= 1.5f; try { List list = LiveBoxes(); List list2 = new List(Mathf.Min(list.Count, 16)); for (int i = 0; i < list.Count; i++) { if (list2.Count >= 16) { break; } if (!((Object)(object)list[i] == (Object)null)) { Entry item = Snapshot(list[i]); if (_remoteCarried.Contains(i)) { item.Carried = true; } list2.Add(item); } } int num = 17; for (int j = 0; j < list2.Count; j++) { Entry entry = list2[j]; num = num * 31 + HashCards(entry.Cards); num = num * 31 + (entry.Carried ? 1 : 0); num = num * 31 + (int)(entry.Pos.x * 8f); num = num * 31 + (int)(entry.Pos.z * 8f); } _hostHeal += 1.5f; if (num != _lastHostHash || !(_hostHeal < 10f)) { _lastHostHash = num; _hostHeal = 0f; List snap = list2; BroadcastState?.Invoke(delegate(BinaryWriter bw) { WriteEntries(bw, snap); }); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync host: " + ex.Message)); } } public void HostApplyOp(BinaryReader br) { if (CoopCore.Role == CoopRole.Host) { byte b = br.ReadByte(); switch (b) { case 0: HostApplyReport(br); break; case 1: HostApplyCollect(br); break; case 2: HostApplyRemoved(br); break; default: CoopPlugin.Log.LogWarning((object)$"CardBoxSync: unknown op {b}"); break; } } } private void HostApplyReport(BinaryReader br) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min((int)br.ReadByte(), 16); List list = LiveBoxes(); Vector3 pos = default(Vector3); for (int i = 0; i < num; i++) { int num2 = br.ReadByte(); bool flag = br.ReadBoolean(); ((Vector3)(ref pos))..ctor(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); float yaw = br.ReadSingle(); if (i >= list.Count || (Object)(object)list[i] == (Object)null) { continue; } InteractablePackagingBox_Card val = list[i]; if (SafeCards(val).Count == num2 && !IsLocallyCarried(val)) { if (flag) { _remoteCarried.Add(i); } else { _remoteCarried.Remove(i); } ApplyToBox(val, new Entry { Cards = null, Carried = flag, Pos = pos, Yaw = yaw }); } } } private void HostApplyCollect(BinaryReader br) { byte index = br.ReadByte(); int cardCount = br.ReadByte(); int cardsHash = br.ReadInt32(); InteractablePackagingBox_Card val = FindBox(index, cardCount, cardsHash); if ((Object)(object)val == (Object)null) { CoopPlugin.Log.LogWarning((object)"CardBoxSync: collect for unknown/mismatched box - ignored"); } else { if (IsLocallyCarried(val)) { return; } try { List list = SafeCards(val); for (int i = 0; i < list.Count; i++) { if (list[i] != null) { CPlayerData.AddCard(list[i], 1); if (list[i].cardGrade == 10) { CPlayerData.m_GameReportDataCollectPermanent.gemMintCardObtained++; } } } AchievementManager.OnCheckGemMintCardCount(CPlayerData.m_GameReportDataCollectPermanent.gemMintCardObtained); AchievementManager.OnCheckCollectedGradedCardSet(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync collect apply: " + ex.Message)); } ApplyingRemote = true; try { ((InteractableObject)val).OnDestroyed(); } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("CardBoxSync collect despawn: " + ex2.Message)); } finally { ApplyingRemote = false; } _remoteCarried.Clear(); ForceResend(); } } private void HostApplyRemoved(BinaryReader br) { byte index = br.ReadByte(); int cardCount = br.ReadByte(); int cardsHash = br.ReadInt32(); InteractablePackagingBox_Card val = FindBox(index, cardCount, cardsHash); if ((Object)(object)val == (Object)null || IsLocallyCarried(val)) { return; } ApplyingRemote = true; try { ((InteractableObject)val).OnDestroyed(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync removal: " + ex.Message)); } finally { ApplyingRemote = false; } _remoteCarried.Clear(); ForceResend(); } private static InteractablePackagingBox_Card FindBox(int index, int cardCount, int cardsHash) { List list = LiveBoxes(); if (index >= 0 && index < list.Count && (Object)(object)list[index] != (Object)null) { List list2 = SafeCards(list[index]); if (list2.Count == cardCount && HashCards(list2) == cardsHash) { return list[index]; } } for (int i = 0; i < list.Count; i++) { if (!((Object)(object)list[i] == (Object)null)) { List list3 = SafeCards(list[i]); if (list3.Count == cardCount && HashCards(list3) == cardsHash) { return list[i]; } } } return null; } public void ClientApplyState(BinaryReader br) { List hostList = ReadEntries(br); ApplyingRemote = true; try { ClientApplyInner(hostList); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(List hostList) { //IL_01fc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Rm() == (Object)null) { return; } List list = LiveBoxes(); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; for (int i = 0; i < hostList.Count; i++) { if ((i >= list.Count || !((Object)(object)list[i] != (Object)null) || !SameCards(SafeCards(list[i]), hostList[i].Cards)) && _recentlyCollected.TryGetValue(HashCards(hostList[i].Cards), out var value) && realtimeSinceStartupAsDouble - value < 6.0) { return; } } if (_recentlyCollected.Count > 0) { List list2 = null; foreach (KeyValuePair item in _recentlyCollected) { if (realtimeSinceStartupAsDouble - item.Value > 12.0) { (list2 ?? (list2 = new List())).Add(item.Key); } } if (list2 != null) { foreach (int item2 in list2) { _recentlyCollected.Remove(item2); } } } for (int num = list.Count - 1; num >= hostList.Count; num--) { try { if ((Object)(object)list[num] != (Object)null) { ((InteractableObject)list[num]).OnDestroyed(); } } catch { } } for (int j = 0; j < hostList.Count; j++) { Entry want = hostList[j]; InteractablePackagingBox_Card val = ((j < list.Count) ? list[j] : null); if ((Object)(object)val != (Object)null && !SameCards(SafeCards(val), want.Cards)) { try { ((InteractableObject)val).OnDestroyed(); } catch { } val = null; list = LiveBoxes(); } if ((Object)(object)val == (Object)null) { try { val = RestockManager.SpawnPackageBoxCard(new List(want.Cards), SpawnAnchor(want.Pos, want.Yaw)); list = LiveBoxes(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync spawn: " + ex.Message)); continue; } } if (!IsLocallyCarried(val) && (!want.Carried || !_recentlyReleased.TryGetValue(j, out var value2) || !(realtimeSinceStartupAsDouble - value2 < 6.0)) && (!_locallyTouched.TryGetValue(j, out var value3) || !(realtimeSinceStartupAsDouble - value3 < 6.0))) { ApplyToBox(val, want); } } _lastApplied.Clear(); _lastApplied.AddRange(hostList); } public void ClientTick(float dt, bool inGame) { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) if (!inGame || (Object)(object)Rm() == (Object)null || _lastApplied.Count == 0) { return; } _timer += dt; if (_timer < 1.5f) { return; } _timer -= 1.5f; try { List list = LiveBoxes(); bool flag = false; List list2 = new List(_lastApplied.Count); for (int i = 0; i < _lastApplied.Count && i < list.Count && (Object)(object)list[i] != (Object)null; i++) { if (IsLocallyCarried(list[i])) { Entry item = _lastApplied[i]; item.Carried = true; if (_carriedLastTick.Add(i)) { flag = true; } list2.Add(item); continue; } if (_carriedLastTick.Remove(i)) { flag = true; _recentlyReleased[i] = Time.realtimeSinceStartupAsDouble; } Entry item2 = Snapshot(list[i]); Entry entry = _lastApplied[i]; Vector3 val = item2.Pos - entry.Pos; if (((Vector3)(ref val)).sqrMagnitude > 0.01f || Mathf.Abs(Mathf.DeltaAngle(item2.Yaw, entry.Yaw)) > 3f) { flag = true; _locallyTouched[i] = Time.realtimeSinceStartupAsDouble; } item2.Cards = entry.Cards; list2.Add(item2); } if (!flag || SendOp == null) { return; } SendOp(delegate(BinaryWriter bw) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)0); bw.Write((byte)Mathf.Min(list2.Count, 16)); for (int j = 0; j < list2.Count && j < 16; j++) { Entry entry2 = list2[j]; bw.Write((byte)Mathf.Min((entry2.Cards != null) ? entry2.Cards.Count : 0, 16)); bw.Write(entry2.Carried); bw.Write(entry2.Pos.x); bw.Write(entry2.Pos.y); bw.Write(entry2.Pos.z); bw.Write(entry2.Yaw); } }); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync client: " + ex.Message)); } } private void ClientCollect(InteractablePackagingBox_Card box) { int idx = LiveBoxes().IndexOf(box); if (idx < 0) { return; } List list = SafeCards(box); if (SendOp == null) { CoopPlugin.Log.LogWarning((object)"CardBoxSync: no host link, open ignored"); return; } int hash = HashCards(list); int count = list.Count; SendOp(delegate(BinaryWriter bw) { bw.Write((byte)1); bw.Write((byte)Mathf.Clamp(idx, 0, 255)); bw.Write((byte)Mathf.Min(count, 16)); bw.Write(hash); }); _recentlyCollected[hash] = Time.realtimeSinceStartupAsDouble; if (idx < _lastApplied.Count) { _lastApplied.RemoveAt(idx); } _carriedLastTick.Clear(); _locallyTouched.Clear(); _recentlyReleased.Clear(); try { CSingleton.Instance.OnExitHoldBoxMode(); } catch { } try { ((InteractablePackagingBox)box).m_BoxAnim.Play("Open"); } catch { } try { SoundManager.PlayAudio("SFX_BoxOpen", 0.5f, 1f); } catch { } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "graded cards collected - check the binder"; CoopCore.Instance.RegisterLineTimer = 4f; } try { ((MonoBehaviour)box).StartCoroutine(CollectDespawn(box)); } catch { DespawnNow(box); } } private static IEnumerator CollectDespawn(InteractablePackagingBox_Card box) { yield return (object)new WaitForSeconds(0.85f); DespawnNow(box); } private static void DespawnNow(InteractablePackagingBox_Card box) { if ((Object)(object)box == (Object)null) { return; } ApplyingRemote = true; try { ((Component)box).gameObject.SetActive(false); ((InteractableObject)box).OnDestroyed(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync local despawn: " + ex.Message)); } finally { ApplyingRemote = false; } } private void OnLocalDestroyed(InteractablePackagingBox_Card box) { if (!InGameLevel()) { return; } if (CoopCore.Role == CoopRole.Host) { _remoteCarried.Clear(); } else { if (CoopCore.Role != CoopRole.Client) { return; } int num = LiveBoxes().IndexOf(box); if (num >= 0) { List list = SafeCards(box); int hash = HashCards(list); int count = list.Count; if (num < _lastApplied.Count) { _lastApplied.RemoveAt(num); } _carriedLastTick.Clear(); _locallyTouched.Clear(); _recentlyReleased.Clear(); _recentlyCollected[hash] = Time.realtimeSinceStartupAsDouble; int sendIdx = num; SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)2); bw.Write((byte)Mathf.Clamp(sendIdx, 0, 255)); bw.Write((byte)Mathf.Min(count, 16)); bw.Write(hash); }); } } } private static Entry Snapshot(InteractablePackagingBox_Card box) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) return new Entry { Cards = SafeCards(box), Carried = IsLocallyCarried(box), Pos = ((Component)box).transform.position, Yaw = ((Component)box).transform.eulerAngles.y }; } private static List SafeCards(InteractablePackagingBox_Card box) { try { return box.GetCardDataList() ?? EmptyCards; } catch { return EmptyCards; } } private static void ApplyToBox(InteractablePackagingBox_Card box, Entry want) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) try { if (want.Carried) { SetBoxVisible(box, visible: false); return; } SetBoxVisible(box, visible: true); Transform transform = ((Component)box).transform; Vector3 val = transform.position - want.Pos; if (((Vector3)(ref val)).sqrMagnitude > 0.01f || Mathf.Abs(Mathf.DeltaAngle(transform.eulerAngles.y, want.Yaw)) > 3f) { transform.SetPositionAndRotation(want.Pos, Quaternion.Euler(0f, want.Yaw, 0f)); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardBoxSync apply: " + ex.Message)); } } private static void SetBoxVisible(InteractablePackagingBox_Card box, bool visible) { if (((Component)box).gameObject.activeSelf == visible) { return; } ((Component)box).gameObject.SetActive(visible); try { if (!(FiStoredCards?.GetValue(box) is List list)) { return; } for (int i = 0; i < list.Count; i++) { InteractableCard3d val = list[i]; if ((Object)(object)val != (Object)null && (Object)(object)val.m_Card3dUI != (Object)null) { ((Component)val.m_Card3dUI).gameObject.SetActive(visible); } } } catch { } } private Transform SpawnAnchor(Vector3 pos, float yaw) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_spawnAnchor == (Object)null) { _spawnAnchor = new GameObject("CoopCardBoxSpawnAnchor").transform; } _spawnAnchor.SetPositionAndRotation(pos, Quaternion.Euler(0f, yaw, 0f)); return _spawnAnchor; } private static void WriteEntries(BinaryWriter bw, List entries) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)Mathf.Min(entries.Count, 16)); for (int i = 0; i < entries.Count && i < 16; i++) { Entry entry = entries[i]; int num = ((entry.Cards != null) ? Mathf.Min(entry.Cards.Count, 16) : 0); bw.Write((byte)num); for (int j = 0; j < num; j++) { Msg.WriteCard(bw, (CardData)(((object)entry.Cards[j]) ?? ((object)new CardData()))); } bw.Write(entry.Pos.x); bw.Write(entry.Pos.y); bw.Write(entry.Pos.z); bw.Write(entry.Yaw); bw.Write(entry.Carried); } } private static List ReadEntries(BinaryReader br) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min((int)br.ReadByte(), 16); List list = new List(num); for (int i = 0; i < num; i++) { int num2 = Mathf.Min((int)br.ReadByte(), 16); List list2 = new List(num2); for (int j = 0; j < num2; j++) { list2.Add(Msg.ReadCard(br)); } Entry item = new Entry { Cards = list2, Pos = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()), Yaw = br.ReadSingle(), Carried = br.ReadBoolean() }; list.Add(item); } return list; } private static int HashCards(List cards) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected I4, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected I4, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected I4, but got Unknown int num = 17; if (cards == null) { return num; } num = num * 31 + cards.Count; for (int i = 0; i < cards.Count && i < 16; i++) { CardData val = cards[i]; if (val != null) { num = num * 31 + val.monsterType; num = num * 31 + val.expansionType; num = num * 31 + val.borderType; num = num * 31 + (int)((val.isFoil ? 1u : 0u) | (uint)(val.isDestiny ? 2 : 0) | (uint)(val.isChampionCard ? 4 : 0)); num = num * 31 + val.cardGrade; } } return num; } private static bool SameCards(List a, List b) { int num = a?.Count ?? 0; int num2 = b?.Count ?? 0; if (num != num2) { return false; } return HashCards(a) == HashCards(b); } } public class CardShelfSync { public struct Entry { public int Key; public bool Occupied; public CardData Card; } private struct SlotState { public bool Occupied; public int Monster; public int Expansion; public int Border; public int Grade; public int GradedIdx; public bool Foil; public bool Destiny; public bool Champion; public bool Matches(CardData c) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between I4 and Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between I4 and Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between I4 and Unknown if (Occupied && c != null && Monster == (int)c.monsterType && Expansion == (int)c.expansionType && Border == (int)c.borderType && Grade == c.cardGrade && GradedIdx == c.gradedCardIndex && Foil == c.isFoil && Destiny == c.isDestiny) { return Champion == c.isChampionCard; } return false; } public static SlotState From(CardData c) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected I4, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected I4, but got Unknown if (c == null) { return default(SlotState); } return new SlotState { Occupied = true, Monster = (int)c.monsterType, Expansion = (int)c.expansionType, Border = (int)c.borderType, Grade = c.cardGrade, GradedIdx = c.gradedCardIndex, Foil = c.isFoil, Destiny = c.isDestiny, Champion = c.isChampionCard }; } } private readonly Dictionary _last = new Dictionary(); private readonly Dictionary _locallyChanged = new Dictionary(); private float _timer; private ShelfManager _sm; public Action> OnLocalChanges; public bool IsClientRole; public void Reset() { _last.Clear(); _locallyChanged.Clear(); _timer = 0.1f; _sm = null; } public void InvalidateBaseline() { _last.Clear(); } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } public void Tick(float dt, bool active) { if (!active) { return; } _timer += dt; if (_timer < 0.9f) { return; } _timer -= 0.9f; List changes = null; try { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } Walk(val.m_CardShelfList, 2, ref changes); Walk(val.m_CardItemCombiShelfList, 3, ref changes); Walk(val.m_TournamentPrizeShelfList, 14, ref changes); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardShelfSync snapshot: " + ex.Message)); return; } if (changes != null && changes.Count > 0) { OnLocalChanges?.Invoke(changes); } } private void Walk(List shelves, int kind, ref List changes) where T : CardShelf { for (int i = 0; i < shelves.Count; i++) { T val = shelves[i]; if ((Object)(object)val == (Object)null || !((Component)(object)val).gameObject.activeInHierarchy) { continue; } List cardCompartmentList = ((CardShelf)val).GetCardCompartmentList(); for (int j = 0; j < cardCompartmentList.Count; j++) { InteractableCardCompartment val2 = cardCompartmentList[j]; if ((Object)(object)val2 == (Object)null) { continue; } int key = (kind << 24) | ((i & 0xFFFF) << 8) | (j & 0xFF); if (!TryReadSlot(val2, out var card)) { continue; } bool flag = card != null; if (_last.TryGetValue(key, out var value)) { if (value.Occupied == flag && (!flag || value.Matches(card))) { continue; } } else if (IsClientRole) { _last[key] = SlotState.From(card); continue; } if (changes == null) { changes = new List(); } if (changes.Count >= 128) { return; } _last[key] = SlotState.From(card); if (IsClientRole) { _locallyChanged[key] = Time.realtimeSinceStartupAsDouble; } changes.Add(new Entry { Key = key, Occupied = flag, Card = card }); } } } private static void ClearSlot(InteractableCardCompartment comp) { comp.DisableAllCard(); try { comp.m_StoredCardList.Clear(); for (int i = 0; i < comp.m_InteractablePriceTagList.Count; i++) { comp.m_InteractablePriceTagList[i].SetPriceChecked(false); } comp.SetPriceTagCardData((CardData)null); comp.SetPriceTagVisibility(false); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("CardShelfSync tag clear: " + ex.Message)); } } private static bool TryReadSlot(InteractableCardCompartment comp, out CardData card) { card = null; if (comp.m_StoredCardList.Count == 0) { return true; } InteractableCard3d val = comp.m_StoredCardList[0]; if ((Object)(object)val == (Object)null || (Object)(object)val.m_Card3dUI == (Object)null || (Object)(object)val.m_Card3dUI.m_CardUI == (Object)null) { return false; } card = val.m_Card3dUI.m_CardUI.GetCardData(); return card != null; } public void ApplyRemote(List entries) { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } foreach (Entry entry in entries) { try { if (!IsClientRole || !_locallyChanged.TryGetValue(entry.Key, out var value) || !(Time.realtimeSinceStartupAsDouble - value < 6.0)) { InteractableCardCompartment val2 = Resolve(val, entry.Key); if (!((Object)(object)val2 == (Object)null) && (!entry.Occupied || val2.m_StoredCardList.Count <= 0 || !_last.TryGetValue(entry.Key, out var value2) || !value2.Matches(entry.Card) || TryReadSlot(val2, out var _))) { ApplySlot(val2, entry); _last[entry.Key] = SlotState.From(entry.Occupied ? entry.Card : null); } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"CardShelfSync apply {entry.Key:X}: {ex.Message}"); } } } private static InteractableCardCompartment Resolve(ShelfManager sm, int key) { int num = key >> 24; int num2 = (key >> 8) & 0xFFFF; int num3 = key & 0xFF; CardShelf val = null; if (num == 2 && num2 < sm.m_CardShelfList.Count) { val = sm.m_CardShelfList[num2]; } else if (num == 3 && num2 < sm.m_CardItemCombiShelfList.Count) { val = (CardShelf)(object)sm.m_CardItemCombiShelfList[num2]; } else if (num == 14 && num2 < sm.m_TournamentPrizeShelfList.Count) { val = (CardShelf)(object)sm.m_TournamentPrizeShelfList[num2]; } if ((Object)(object)val == (Object)null) { return null; } List cardCompartmentList = val.GetCardCompartmentList(); if (num3 >= cardCompartmentList.Count) { return null; } return cardCompartmentList[num3]; } private static void ApplySlot(InteractableCardCompartment comp, Entry e) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) bool flag = comp.m_StoredCardList.Count > 0; if (!e.Occupied) { if (flag) { ClearSlot(comp); } return; } if (flag) { if (TryReadSlot(comp, out var card) && card != null && SlotState.From(card).Matches(e.Card)) { return; } ClearSlot(comp); } Card3dUIGroup cardUI = CSingleton.Instance.GetCardUI(); InteractableCard3d component = ((Component)ShelfManager.SpawnInteractableObject((EObjectType)9)).GetComponent(); cardUI.m_IgnoreCulling = true; cardUI.m_CardUI.SetFoilCullListVisibility(true); cardUI.SetSimplifyCardDistanceCull(false); cardUI.m_CardUI.ResetFarDistanceCull(); cardUI.m_CardUI.SetCardUI(e.Card); ((Component)cardUI).transform.position = ((Component)component).transform.position; ((Component)cardUI).transform.rotation = ((Component)component).transform.rotation; component.SetCardUIFollow(cardUI); component.SetEnableCollision(false); comp.SetCardOnShelf(component); cardUI.m_IgnoreCulling = false; } public List BuildFullState() { List list = new List(); ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return list; } Collect(val.m_CardShelfList, 2, list); Collect(val.m_CardItemCombiShelfList, 3, list); Collect(val.m_TournamentPrizeShelfList, 14, list); return list; } private static void Collect(List shelves, int kind, List into) where T : CardShelf { for (int i = 0; i < shelves.Count; i++) { T val = shelves[i]; if ((Object)(object)val == (Object)null || !((Component)(object)val).gameObject.activeInHierarchy) { continue; } List cardCompartmentList = ((CardShelf)val).GetCardCompartmentList(); for (int j = 0; j < cardCompartmentList.Count; j++) { InteractableCardCompartment val2 = cardCompartmentList[j]; if (!((Object)(object)val2 == (Object)null) && TryReadSlot(val2, out var card)) { into.Add(new Entry { Key = ((kind << 24) | ((i & 0xFFFF) << 8) | (j & 0xFF)), Occupied = (card != null), Card = card }); } } } } public static void WriteEntries(BinaryWriter bw, List entries) { bw.Write((ushort)entries.Count); foreach (Entry entry in entries) { bw.Write(entry.Key); bw.Write(entry.Occupied); if (entry.Occupied) { Msg.WriteCard(bw, entry.Card); } } } public static List ReadEntries(BinaryReader br) { int num = br.ReadUInt16(); List list = new List(num); for (int i = 0; i < num; i++) { Entry item = new Entry { Key = br.ReadInt32(), Occupied = br.ReadBoolean() }; if (item.Occupied) { item.Card = Msg.ReadCard(br); } list.Add(item); } return list; } } public class ContainerSync { private class PackMirror { public int StoredCount; public List StoredTypes = new List(); public bool Processing; public float Timer; public int OpenedCount; public List Output = new List(); } public static ContainerSync Instance; private const int KindCardStorage = 9; private const int KindCleanser = 10; private const int KindPackOpener = 11; private const int KindBoxStorage = 12; private const int KindDonation = 13; private const byte OpContentSet = 1; private const byte OpPackInsert = 2; private const byte OpPackTurnOn = 3; private const byte OpPackCollect = 4; private const byte OpBoxTake = 5; private const byte OpBoxStore = 6; private const byte OpCleanserToggle = 7; private const byte OpCleanserRefill = 8; private const byte OpWorkerTakeFlag = 9; private const float TickInterval = 1f; private const float HealInterval = 15f; private const double TouchedGuard = 6.0; public Action> SendOp; public Action> BroadcastState; public static bool ApplyingRemote; private static readonly FieldInfo FiPoIsProcessing = AccessTools.Field(typeof(InteractableAutoPackOpener), "m_IsProcessing"); private static readonly FieldInfo FiPoOpenTimer = AccessTools.Field(typeof(InteractableAutoPackOpener), "m_PackOpenTimer"); private static readonly FieldInfo FiPoOpenedCount = AccessTools.Field(typeof(InteractableAutoPackOpener), "m_PackOpenedCount"); private static readonly FieldInfo FiPoUI = AccessTools.Field(typeof(InteractableAutoPackOpener), "m_AutoCardOpenerUI"); private static readonly FieldInfo FiEbCount = AccessTools.Field(typeof(InteractableEmptyBoxStorage), "m_StoredBoxCount"); private static readonly FieldInfo FiEbMax = AccessTools.Field(typeof(InteractableEmptyBoxStorage), "m_MaxStoredBoxCount"); private static readonly MethodInfo MiEbEval = AccessTools.Method(typeof(InteractableEmptyBoxStorage), "EvaluateStoredBoxStackHeight", (Type[])null, (Type[])null); private static readonly FieldInfo FiClTurnedOn = AccessTools.Field(typeof(InteractableAutoCleanser), "m_IsTurnedOn"); private static readonly FieldInfo FiClNeedRefill = AccessTools.Field(typeof(InteractableAutoCleanser), "m_IsNeedRefill"); private static readonly FieldInfo FiClCooldown = AccessTools.Field(typeof(InteractableAutoCleanser), "m_IsSprayOnCooldown"); private static readonly FieldInfo FiClTimer = AccessTools.Field(typeof(InteractableAutoCleanser), "m_Timer"); private ShelfManager _sm; private float _timer; private float _heal; private readonly Dictionary _lastHash = new Dictionary(); private readonly List _dirty = new List(); private readonly Dictionary _touched = new Dictionary(); private readonly Dictionary _packMirrors = new Dictionary(); private readonly Func _hashCardStorage; private readonly Func _hashDonation; private readonly Func _hashPackOpener; private readonly Func _hashBoxStorage; private readonly Func _hashCleanser; public ContainerSync() { Instance = this; _hashCardStorage = delegate(object obj) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown InteractableCardStorageShelf val = (InteractableCardStorageShelf)obj; return HashCards(val.GetCompactCardDataAmountList()) * 31 + (val.CanWorkerTake() ? 1 : 0); }; _hashDonation = (object obj) => HashCards(((InteractableBulkDonationBox)obj).GetCompactCardDataAmountList()); _hashPackOpener = delegate(object obj) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected I4, but got Unknown InteractableAutoPackOpener val = (InteractableAutoPackOpener)obj; int num = 17; List storedItemList = val.GetStoredItemList(); num = num * 31 + (storedItemList?.Count ?? 0); if (storedItemList != null) { for (int i = 0; i < storedItemList.Count; i++) { if ((Object)(object)storedItemList[i] != (Object)null) { num = num * 31 + storedItemList[i].GetItemType(); } } } num = num * 31 + (val.GetIsProcessing() ? 1 : 0); num = num * 31 + (int)((FiPoOpenTimer?.GetValue(val) as float?).GetValueOrDefault() * 2f); num = num * 31 + val.GetPackOpenedCount(); return num * 31 + HashCards(val.GetCompactCardDataAmountList()); }; _hashBoxStorage = (object obj) => ((InteractableEmptyBoxStorage)obj).GetBoxStoredCount(); _hashCleanser = delegate(object obj) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown InteractableAutoCleanser val = (InteractableAutoCleanser)obj; int num = 17; num = num * 31 + (val.IsTurnedOn() ? 1 : 0); num = num * 31 + (val.IsNeedRefill() ? 2 : 0); List storedItemList = val.GetStoredItemList(); num = num * 31 + (storedItemList?.Count ?? 0); if (storedItemList != null) { for (int i = 0; i < storedItemList.Count; i++) { if ((Object)(object)storedItemList[i] != (Object)null) { num = num * 31 + (int)(storedItemList[i].GetContentFill() * 100f); } } } return num; }; } public void Reset() { _sm = null; _timer = -5.3f; _heal = 0f; _lastHash.Clear(); _dirty.Clear(); _touched.Clear(); _packMirrors.Clear(); } public void ForceResend() { _lastHash.Clear(); _heal = 0f; } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } private int IndexOf(int kind, object obj) { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return -1; } IList list = PopulationSync.GetList(val, kind); if (list == null) { return -1; } int num = list.IndexOf(obj); if (num >= 250) { return -1; } return num; } private T Get(int kind, int idx) where T : class { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return null; } IList list = PopulationSync.GetList(val, kind); if (list == null || idx < 0 || idx >= list.Count) { return null; } return list[idx] as T; } private void Touch(int kind, int idx) { _touched[(kind << 8) | idx] = Time.realtimeSinceStartupAsDouble; } private bool IsTouched(int kind, int idx) { if (_touched.TryGetValue((kind << 8) | idx, out var value)) { return Time.realtimeSinceStartupAsDouble - value < 6.0; } return false; } public void HostTick(float dt, bool inGame) { if (!inGame) { return; } _timer += dt; if (_timer < 1f) { return; } _timer -= 1f; if (_timer > 1f) { _timer = 1f; } try { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } _heal += 1f; if (_heal >= 15f) { _heal = 0f; _lastHash.Clear(); } _dirty.Clear(); CollectKind(val, 9, _hashCardStorage); CollectKind(val, 13, _hashDonation); CollectKind(val, 11, _hashPackOpener); CollectKind(val, 12, _hashBoxStorage); CollectKind(val, 10, _hashCleanser); if (_dirty.Count == 0) { return; } List dirty = new List(_dirty); BroadcastState?.Invoke(delegate(BinaryWriter bw) { bw.Write((ushort)dirty.Count); for (int i = 0; i < dirty.Count; i++) { WriteRecord(bw, dirty[i] >> 8, dirty[i] & 0xFF); } }); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ContainerSync host: " + ex.Message)); } } private void CollectKind(ShelfManager sm, int kind, Func hashFn) { IList list = PopulationSync.GetList(sm, kind); if (list == null) { return; } for (int i = 0; i < list.Count && i < 250; i++) { if (list[i] != null) { int num; try { num = hashFn(list[i]); } catch { continue; } int num2 = (kind << 8) | i; if (!_lastHash.TryGetValue(num2, out var value) || value != num) { _lastHash[num2] = num; _dirty.Add(num2); } } } } private void WriteRecord(BinaryWriter bw, int kind, int idx) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)kind); bw.Write((byte)idx); switch (kind) { case 9: { InteractableCardStorageShelf val5 = Get(kind, idx); bw.Write((Object)(object)val5 == (Object)null || val5.CanWorkerTake()); WriteCards(bw, (val5 != null) ? val5.GetCompactCardDataAmountList() : null); break; } case 13: { InteractableBulkDonationBox val4 = Get(kind, idx); WriteCards(bw, (val4 != null) ? val4.GetCompactCardDataAmountList() : null); break; } case 11: { InteractableAutoPackOpener val2 = Get(kind, idx); List list2 = ((val2 != null) ? val2.GetStoredItemList() : null); int num2 = Mathf.Min(list2?.Count ?? 0, 250); bw.Write((byte)num2); for (int j = 0; j < num2; j++) { bw.Write(((Object)(object)list2[j] != (Object)null) ? ((int)list2[j].GetItemType()) : 0); } bw.Write((Object)(object)val2 != (Object)null && val2.GetIsProcessing()); bw.Write(((Object)(object)val2 != (Object)null) ? (FiPoOpenTimer?.GetValue(val2) as float?).GetValueOrDefault() : 0f); bw.Write(((Object)(object)val2 != (Object)null) ? val2.GetPackOpenedCount() : 0); WriteCards(bw, (val2 != null) ? val2.GetCompactCardDataAmountList() : null); break; } case 12: { InteractableEmptyBoxStorage val3 = Get(kind, idx); bw.Write(((Object)(object)val3 != (Object)null) ? val3.GetBoxStoredCount() : 0); break; } case 10: { InteractableAutoCleanser val = Get(kind, idx); byte b = 0; if ((Object)(object)val != (Object)null && val.IsTurnedOn()) { b |= 1; } if ((Object)(object)val == (Object)null || val.IsNeedRefill()) { b |= 2; } bw.Write(b); List list = ((val != null) ? val.GetStoredItemList() : null); int num = Mathf.Min(list?.Count ?? 0, 32); bw.Write((byte)num); for (int i = 0; i < num; i++) { bw.Write(((Object)(object)list[i] != (Object)null) ? list[i].GetContentFill() : 0f); } break; } } } public void HostApplyOp(BinaryReader br) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) byte b = br.ReadByte(); try { switch (b) { case 1: { int num = br.ReadByte(); int idx3 = br.ReadByte(); bool canWorkerTake = br.ReadBoolean(); List cards = ReadCards(br); ApplyContent(num, idx3, cards, num == 9, canWorkerTake); break; } case 9: { int idx4 = br.ReadByte(); bool canWorkerTake2 = br.ReadBoolean(); InteractableCardStorageShelf val4 = Get(9, idx4); if (!((Object)(object)val4 == (Object)null)) { ApplyingRemote = true; try { val4.SetCanWorkerTake(canWorkerTake2); val4.OnCardStorageShelfSettingDone(); break; } finally { ApplyingRemote = false; } } break; } case 2: { int idx8 = br.ReadByte(); EItemType itemType = (EItemType)br.ReadInt32(); InteractableAutoPackOpener val7 = Get(11, idx8); if ((Object)(object)val7 == (Object)null) { break; } Item val8 = SpawnItem(itemType, val7.m_PosInside); if (!((Object)(object)val8 == (Object)null)) { ApplyingRemote = true; try { val7.AddItem(val8, true, false); break; } finally { ApplyingRemote = false; } } break; } case 3: { int idx2 = br.ReadByte(); InteractableAutoPackOpener val3 = Get(11, idx2); if ((Object)(object)val3 != (Object)null && !val3.GetIsProcessing() && val3.GetStoredItemList().Count > 0) { ((InteractableObject)val3).OnMouseButtonUp(); } break; } case 4: { int idx9 = br.ReadByte(); List revealed = ReadCards(br); HostApplyPackCollect(idx9, revealed); break; } case 5: { int idx7 = br.ReadByte(); Vector3 reqPos = default(Vector3); ((Vector3)(ref reqPos))..ctor(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); HostApplyBoxTake(idx7, reqPos); break; } case 6: { int idx6 = br.ReadByte(); InteractableEmptyBoxStorage val6 = Get(12, idx6); if (!((Object)(object)val6 == (Object)null)) { int num2 = (FiEbMax?.GetValue(val6) as int?) ?? 200; if (val6.GetBoxStoredCount() < num2) { FiEbCount?.SetValue(val6, val6.GetBoxStoredCount() + 1); MiEbEval?.Invoke(val6, null); } } break; } case 7: { int idx5 = br.ReadByte(); bool flag = br.ReadBoolean(); InteractableAutoCleanser val5 = Get(10, idx5); if (!((Object)(object)val5 == (Object)null)) { FiClTurnedOn?.SetValue(val5, flag); if (!flag) { FiClCooldown?.SetValue(val5, true); FiClTimer?.SetValue(val5, 0f); } } break; } case 8: { int idx = br.ReadByte(); float contentFill = br.ReadSingle(); InteractableAutoCleanser val = Get(10, idx); if ((Object)(object)val == (Object)null || !val.HasEnoughSlot()) { break; } Item val2 = SpawnItem((EItemType)23, val.m_PosList[0], contentFill); if (!((Object)(object)val2 == (Object)null)) { ApplyingRemote = true; try { val.AddItem(val2, true); break; } finally { ApplyingRemote = false; } } break; } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"ContainerSync op {b}: {ex.Message}"); } } private void HostApplyPackCollect(int idx, List revealed) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) InteractableAutoPackOpener val = Get(11, idx); if ((Object)(object)val == (Object)null) { return; } List compactCardDataAmountList = val.GetCompactCardDataAmountList(); if (compactCardDataAmountList == null || compactCardDataAmountList.Count == 0) { return; } for (int i = 0; i < compactCardDataAmountList.Count; i++) { CompactCardDataAmount val2 = compactCardDataAmountList[i]; if (val2 == null) { continue; } int num = val2.amount - AmountFor(revealed, val2); if (num > 0) { CardData cardData = CPlayerData.GetCardData(val2.cardSaveIndex, val2.expansionType, val2.isDestiny); if (cardData != null) { CPlayerData.AddCard(cardData, num); } } } int packOpenedCount = val.GetPackOpenedCount(); CPlayerData.m_GameReportDataCollect.cardPackOpened += packOpenedCount; CPlayerData.m_GameReportDataCollectPermanent.cardPackOpened += packOpenedCount; AchievementManager.OnCardPackOpened(CPlayerData.m_GameReportDataCollectPermanent.cardPackOpened); compactCardDataAmountList.Clear(); FiPoOpenedCount?.SetValue(val, 0); FiPoIsProcessing?.SetValue(val, false); val.m_CurrentState = 0; object? obj = FiPoUI?.GetValue(val); AutoCardOpenerUI val3 = (AutoCardOpenerUI)((obj is AutoCardOpenerUI) ? obj : null); if (val3 != null) { val3.SetUIState(0); val3.UpdatePackCountText(0, val.m_MaxPackCount); } } private void HostApplyBoxTake(int idx, Vector3 reqPos) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) InteractableEmptyBoxStorage val = Get(12, idx); if ((Object)(object)val == (Object)null || val.GetBoxStoredCount() <= 0) { return; } InteractablePackagingBox_Item val2 = RestockManager.SpawnPackageBoxItem((EItemType)(-1), 0, true); if (!((Object)(object)val2 == (Object)null)) { Transform emptyBoxSpawnLoc = val.m_EmptyBoxSpawnLoc; ((Component)val2).transform.position = (((Object)(object)emptyBoxSpawnLoc != (Object)null) ? emptyBoxSpawnLoc.position : reqPos); if ((Object)(object)emptyBoxSpawnLoc != (Object)null) { ((Component)val2).transform.rotation = emptyBoxSpawnLoc.rotation; } val2.ForceSetOpenCloseInstant(true); val2.SetOpenCloseBox(false, false); FiEbCount?.SetValue(val, val.GetBoxStoredCount() - 1); MiEbEval?.Invoke(val, null); } } public void ClientApplyState(BinaryReader br) { int num = br.ReadUInt16(); for (int i = 0; i < num; i++) { int num2 = br.ReadByte(); int num3 = br.ReadByte(); try { switch (num2) { default: return; case 9: { bool canWorkerTake = br.ReadBoolean(); List cards = ReadCards(br); InteractableCardStorageShelf val4 = Get(num2, num3); if (!((Object)(object)val4 == (Object)null) && !val4.IsEditingBulkBox() && !IsTouched(num2, num3)) { ApplyContent(num2, num3, cards, hasFlag: true, canWorkerTake); } break; } case 13: { List cards2 = ReadCards(br); InteractableBulkDonationBox val5 = Get(num2, num3); if (!((Object)(object)val5 == (Object)null) && !val5.IsEditingBulkBox() && !IsTouched(num2, num3)) { ApplyContent(num2, num3, cards2, hasFlag: false, canWorkerTake: false); } break; } case 11: { int num6 = br.ReadByte(); List list2 = new List(num6); for (int k = 0; k < num6; k++) { list2.Add(br.ReadInt32()); } bool processing = br.ReadBoolean(); float timer = br.ReadSingle(); int openedCount = br.ReadInt32(); List output = ReadCards(br); InteractableAutoPackOpener val3 = Get(num2, num3); if (!((Object)(object)val3 == (Object)null)) { if (!_packMirrors.TryGetValue(num3, out var value)) { value = (_packMirrors[num3] = new PackMirror()); } value.StoredCount = num6; value.StoredTypes = list2; value.Processing = processing; value.Timer = timer; value.OpenedCount = openedCount; value.Output = output; ApplyPackMirrorToMachine(val3, value); } break; } case 12: { int num5 = br.ReadInt32(); InteractableEmptyBoxStorage val2 = Get(num2, num3); if (!((Object)(object)val2 == (Object)null) && !IsTouched(num2, num3)) { FiEbCount?.SetValue(val2, num5); MiEbEval?.Invoke(val2, null); } break; } case 10: { byte b = br.ReadByte(); int num4 = br.ReadByte(); List list = new List(num4); for (int j = 0; j < num4; j++) { list.Add(br.ReadSingle()); } InteractableAutoCleanser val = Get(num2, num3); if (!((Object)(object)val == (Object)null) && !IsTouched(num2, num3)) { ApplyCleanserState(val, (b & 1) != 0, (b & 2) != 0, list); } break; } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"ContainerSync apply kind {num2}: {ex.Message}"); break; } } } private void ApplyContent(int kind, int idx, List cards, bool hasFlag, bool canWorkerTake) { ApplyingRemote = true; try { switch (kind) { case 9: { InteractableCardStorageShelf val2 = Get(kind, idx); if (!((Object)(object)val2 == (Object)null)) { val2.SetCompactCardDataAmountList(cards); if (hasFlag) { val2.SetCanWorkerTake(canWorkerTake); val2.OnCardStorageShelfSettingDone(); } } break; } case 13: { InteractableBulkDonationBox val = Get(kind, idx); if (!((Object)(object)val == (Object)null)) { val.SetCompactCardDataAmountList(cards); val.UpdateFillPercent(Mathf.Clamp01((float)val.GetTotalCardAmount() / (float)val.GetBoxTotalCardCountMax())); } break; } } } finally { ApplyingRemote = false; } } private void ApplyPackMirrorToMachine(InteractableAutoPackOpener p, PackMirror m) { ApplyingRemote = true; try { List storedItemList = p.GetStoredItemList(); if (storedItemList != null && storedItemList.Count > 0) { for (int num = storedItemList.Count - 1; num >= 0; num--) { Item val = storedItemList[num]; storedItemList.RemoveAt(num); if ((Object)(object)val != (Object)null) { try { ItemSpawnManager.DisableItem(val); } catch { } } } } FiPoIsProcessing?.SetValue(p, m.Processing); p.m_CurrentState = (m.Processing ? ((m.StoredCount > 0) ? 1 : 2) : 0); object? obj2 = FiPoUI?.GetValue(p); AutoCardOpenerUI val2 = (AutoCardOpenerUI)((obj2 is AutoCardOpenerUI) ? obj2 : null); if (val2 != null) { if (m.Processing && m.StoredCount > 0) { val2.SetUIState(1); val2.UpdateProcessingFillBar(1f - (float)m.StoredCount / (float)p.m_MaxPackCount); val2.UpdateProcessingTimeLeftText(p.m_PackOpenTime * (float)m.StoredCount - m.Timer); } else if (m.Processing) { val2.SetUIState(2); } else { val2.SetUIState(0); val2.UpdatePackCountText(m.StoredCount, p.m_MaxPackCount); } } } finally { ApplyingRemote = false; } } private void ApplyCleanserState(InteractableAutoCleanser c, bool on, bool needRefill, List fills) { ApplyingRemote = true; try { int num = 12; while (c.GetItemCount() > fills.Count && num-- > 0) { Item lastItem = c.GetLastItem(); if ((Object)(object)lastItem == (Object)null) { break; } c.RemoveItem(lastItem); try { ItemSpawnManager.DisableItem(lastItem); } catch { } } num = 12; while (c.GetItemCount() < fills.Count && num-- > 0 && c.HasEnoughSlot()) { Item val = SpawnItem((EItemType)23, c.m_PosList[0], 1f); if ((Object)(object)val == (Object)null) { break; } c.AddItem(val, true); } List storedItemList = c.GetStoredItemList(); if (storedItemList != null) { for (int i = 0; i < storedItemList.Count && i < fills.Count; i++) { if ((Object)(object)storedItemList[i] != (Object)null) { storedItemList[i].SetContentFill(fills[i]); } } } FiClTurnedOn?.SetValue(c, on); FiClNeedRefill?.SetValue(c, needRefill); } finally { ApplyingRemote = false; } } private void ClientForwardContent(int kind, object container, List cards, bool canWorkerTake) { int idx = IndexOf(kind, container); if (idx >= 0) { Touch(kind, idx); SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)1); bw.Write((byte)kind); bw.Write((byte)idx); bw.Write(canWorkerTake); WriteCards(bw, cards); }); } } private void ClientPackOpenerClick(InteractableAutoPackOpener p) { int idx = IndexOf(11, p); if (idx < 0) { return; } _packMirrors.TryGetValue(idx, out var value); SoundManager.PlayAudio("SFX_ButtonLightTap", 0.6f, 0.5f); if (value == null || !value.Processing) { if (value != null && value.StoredCount > 0) { SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)3); bw.Write((byte)idx); }); } else { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)38); } } else if (value.Output.Count > 0 && value.StoredCount <= 0) { List revealed = new List(value.Output); CPlayerData.m_GameReportDataCollect.cardPackOpened += value.OpenedCount; CPlayerData.m_GameReportDataCollectPermanent.cardPackOpened += value.OpenedCount; AchievementManager.OnCardPackOpened(CPlayerData.m_GameReportDataCollectPermanent.cardPackOpened); try { CSingleton.Instance.m_ShowCardObtainedPage.ShowCardObtained(revealed); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ContainerSync reveal: " + ex.Message)); } SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)4); bw.Write((byte)idx); WriteCards(bw, revealed); }); value.Output.Clear(); value.Processing = false; value.OpenedCount = 0; value.StoredCount = 0; ApplyPackMirrorToMachine(p, value); SoundManager.PlayAudio("SFX_PercStarJingle3", 0.6f, 1f); SoundManager.PlayAudio("SFX_Gift", 0.6f, 1f); } else { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)39); } } public static void ApplyPatches(Harmony h) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Expected O, but got Unknown //IL_016c: Expected O, but got Unknown //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Expected O, but got Unknown //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Expected O, but got Unknown Try(h, typeof(InteractableCardStorageShelf), "SetCompactCardDataAmountList", null, new HarmonyMethod(typeof(ContainerSync), "StorageContentPostfix", (Type[])null)); Try(h, typeof(InteractableBulkDonationBox), "SetCompactCardDataAmountList", null, new HarmonyMethod(typeof(ContainerSync), "DonationContentPostfix", (Type[])null)); Try(h, typeof(InteractableCardStorageShelf), "SetCanWorkerTake", null, new HarmonyMethod(typeof(ContainerSync), "WorkerTakePostfix", (Type[])null)); Try(h, typeof(InteractableAutoPackOpener), "OnMouseButtonUp", new HarmonyMethod(typeof(ContainerSync), "PackOpenerClickPrefix", (Type[])null)); Try(h, typeof(InteractableAutoPackOpener), "AddItem", new HarmonyMethod(typeof(ContainerSync), "PackOpenerAddItemPrefix", (Type[])null)); Try(h, typeof(InteractableAutoPackOpener), "TakeItemToHand", new HarmonyMethod(typeof(ContainerSync), "TakeItemBlockPrefix", (Type[])null)); Try(h, typeof(InteractableEmptyBoxStorage), "TakeBox", new HarmonyMethod(typeof(ContainerSync), "TakeBoxPrefix", (Type[])null)); Try(h, typeof(InteractableEmptyBoxStorage), "StoreBox", new HarmonyMethod(typeof(ContainerSync), "StoreBoxPrefix", (Type[])null), new HarmonyMethod(typeof(ContainerSync), "StoreBoxPostfix", (Type[])null)); Try(h, typeof(InteractableAutoCleanser), "OnMouseButtonUp", null, new HarmonyMethod(typeof(ContainerSync), "CleanserTogglePostfix", (Type[])null)); Try(h, typeof(InteractableAutoCleanser), "AddItem", new HarmonyMethod(typeof(ContainerSync), "CleanserAddItemPrefix", (Type[])null)); Try(h, typeof(InteractableAutoCleanser), "TakeItemToHand", new HarmonyMethod(typeof(ContainerSync), "TakeItemBlockPrefix", (Type[])null)); } public static void StorageContentPostfix(InteractableCardStorageShelf __instance) { if (CoopCore.Role == CoopRole.Client && !ApplyingRemote) { Instance?.ClientForwardContent(9, __instance, __instance.GetCompactCardDataAmountList(), __instance.CanWorkerTake()); } } public static void DonationContentPostfix(InteractableBulkDonationBox __instance) { if (CoopCore.Role == CoopRole.Client && !ApplyingRemote) { Instance?.ClientForwardContent(13, __instance, __instance.GetCompactCardDataAmountList(), canWorkerTake: false); } } public static void WorkerTakePostfix(InteractableCardStorageShelf __instance, bool canWorkerTake) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return; } ContainerSync instance = Instance; if (instance == null) { return; } int idx = instance.IndexOf(9, __instance); if (idx >= 0) { instance.Touch(9, idx); instance.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)9); bw.Write((byte)idx); bw.Write(canWorkerTake); }); } } public static bool PackOpenerClickPrefix(InteractableAutoPackOpener __instance) { if (CoopCore.Role != CoopRole.Client) { return true; } try { Instance?.ClientPackOpenerClick(__instance); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ContainerSync click: " + ex.Message)); } return false; } public static bool PackOpenerAddItemPrefix(InteractableAutoPackOpener __instance, Item item) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected I4, but got Unknown if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } ContainerSync instance = Instance; if (instance == null) { return true; } int idx = instance.IndexOf(11, __instance); if (idx >= 0) { int itemType = 0; try { itemType = (int)item.GetItemType(); } catch { } instance.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)2); bw.Write((byte)idx); bw.Write(itemType); }); } try { ItemSpawnManager.DisableItem(item); } catch { } return false; } public static bool TakeItemBlockPrefix(ref Item __result) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } __result = null; return false; } public static bool TakeBoxPrefix(InteractableEmptyBoxStorage __instance) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } ContainerSync instance = Instance; if (instance == null) { return false; } int idx = instance.IndexOf(12, __instance); if (idx >= 0 && __instance.GetBoxStoredCount() > 0) { Transform emptyBoxSpawnLoc = __instance.m_EmptyBoxSpawnLoc; Vector3 pos = (((Object)(object)emptyBoxSpawnLoc != (Object)null) ? emptyBoxSpawnLoc.position : ((Component)__instance).transform.position); instance.Touch(12, idx); instance.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)5); bw.Write((byte)idx); bw.Write(pos.x); bw.Write(pos.y); bw.Write(pos.z); }); } return false; } public static void StoreBoxPrefix(InteractableEmptyBoxStorage __instance, out int __state) { __state = __instance.GetBoxStoredCount(); } public static void StoreBoxPostfix(InteractableEmptyBoxStorage __instance, int __state) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote || __instance.GetBoxStoredCount() <= __state) { return; } ContainerSync instance = Instance; if (instance == null) { return; } int idx = instance.IndexOf(12, __instance); if (idx >= 0) { instance.Touch(12, idx); instance.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)6); bw.Write((byte)idx); }); } } public static void CleanserTogglePostfix(InteractableAutoCleanser __instance) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return; } ContainerSync instance = Instance; if (instance == null) { return; } int idx = instance.IndexOf(10, __instance); if (idx >= 0) { bool on = __instance.IsTurnedOn(); instance.Touch(10, idx); instance.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)7); bw.Write((byte)idx); bw.Write(on); }); } } public static bool CleanserAddItemPrefix(InteractableAutoCleanser __instance, Item item) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } ContainerSync instance = Instance; if (instance == null) { return true; } int idx = instance.IndexOf(10, __instance); if (idx >= 0) { float fill = 1f; try { fill = item.GetContentFill(); } catch { } instance.Touch(10, idx); instance.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)8); bw.Write((byte)idx); bw.Write(fill); }); } try { ItemSpawnManager.DisableItem(item); } catch { } return false; } private static Item SpawnItem(EItemType itemType, Transform parent, float contentFill = -1f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) try { ItemMeshData itemMeshData = InventoryBase.GetItemMeshData(itemType); Item item = ItemSpawnManager.GetItem(parent); item.SetMesh(itemMeshData.mesh, itemMeshData.material, itemType, itemMeshData.meshSecondary, itemMeshData.materialSecondary, (List)null); ((Component)item).transform.localPosition = Vector3.zero; ((Component)item).transform.localRotation = Quaternion.identity; if (contentFill >= 0f) { item.SetContentFill(contentFill); } ((Component)item).gameObject.SetActive(true); return item; } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"ContainerSync spawn {itemType}: {ex.Message}"); return null; } } private static int AmountFor(List list, CompactCardDataAmount id) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (list == null) { return 0; } for (int i = 0; i < list.Count; i++) { CompactCardDataAmount val = list[i]; if (val != null && val.cardSaveIndex == id.cardSaveIndex && val.expansionType == id.expansionType && val.isDestiny == id.isDestiny) { return val.amount; } } return 0; } private static int HashCards(List list) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected I4, but got Unknown int num = 17; if (list == null) { return num; } for (int i = 0; i < list.Count; i++) { CompactCardDataAmount val = list[i]; if (val != null) { num = num * 31 + val.cardSaveIndex; num = num * 31 + val.expansionType; num = num * 31 + (val.isDestiny ? 1 : 0); num = num * 31 + val.amount; num = num * 31 + val.gradedCardIndex; } } return num; } private static void WriteCards(BinaryWriter bw, List list) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected I4, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min(list?.Count ?? 0, 2000); bw.Write((ushort)num); for (int i = 0; i < num; i++) { CompactCardDataAmount val = (CompactCardDataAmount)(((object)list[i]) ?? ((object)new CompactCardDataAmount())); bw.Write(val.cardSaveIndex); bw.Write((int)val.expansionType); bw.Write(val.isDestiny); bw.Write(val.amount); bw.Write(val.gradedCardIndex); } } private static List ReadCards(BinaryReader br) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) int num = br.ReadUInt16(); List list = new List(num); for (int i = 0; i < num; i++) { CompactCardDataAmount val = new CompactCardDataAmount(); val.cardSaveIndex = br.ReadInt32(); val.expansionType = (ECardExpansionType)br.ReadInt32(); val.isDestiny = br.ReadBoolean(); val.amount = br.ReadInt32(); val.gradedCardIndex = br.ReadInt32(); list.Add(val); } return list; } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } } public class GradingSync { public static GradingSync Instance; public Action> SendOp; public Action> BroadcastState; public static bool ApplyingRemote; private const int MaxSets = 8; private const int MaxSlots = 8; private const float DeliveryFee = 10f; private static readonly FieldInfo FiShowingAlpha = AccessTools.Field(typeof(GradedCardSubmitSelectScreen), "m_IsShowingCanvasGrpAlpha"); private static readonly FieldInfo FiHidingAlpha = AccessTools.Field(typeof(GradedCardSubmitSelectScreen), "m_IsHidingCanvasGrpAlpha"); private float _timer; private int _lastHash; private float _heal; private GradeCardWebsiteUIScreen _website; public GradingSync() { Instance = this; } public void Reset() { _timer = -6.8f; _lastHash = 0; _heal = 0f; _website = null; } public void ForceResend() { _lastHash = 0; _heal = 999f; } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown Try(h, typeof(GradedCardSubmitSelectScreen), "OnPressSubmitButton", new HarmonyMethod(typeof(GradingSync), "SubmitPrefix", (Type[])null)); Try(h, typeof(RestockManager), "OnDayStarted", new HarmonyMethod(typeof(GradingSync), "MatureBlockPrefix", (Type[])null)); } public static bool MatureBlockPrefix() { return CoopCore.Role != CoopRole.Client; } public static bool SubmitPrefix(GradedCardSubmitSelectScreen __instance) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } try { ClientSubmit(__instance); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("GradingSync submit: " + ex.Message)); } return false; } private static void ClientSubmit(GradedCardSubmitSelectScreen screen) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Expected O, but got Unknown //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Expected O, but got Unknown object obj = FiShowingAlpha?.GetValue(screen); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return; } obj = FiHidingAlpha?.GetValue(screen); bool flag2 = default(bool); int num2; if (obj is bool) { flag2 = (bool)obj; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { return; } GradeCardSubmitSet currentGradeCardSubmitSet = CPlayerData.m_CurrentGradeCardSubmitSet; if (currentGradeCardSubmitSet == null || currentGradeCardSubmitSet.m_CardDataList == null) { return; } List picked = new List(); for (int i = 0; i < currentGradeCardSubmitSet.m_CardDataList.Count; i++) { CardData val = currentGradeCardSubmitSet.m_CardDataList[i]; if (val != null && (int)val.monsterType != 0) { picked.Add(val); } } if (picked.Count == 0) { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)36); return; } int serviceLevel = currentGradeCardSubmitSet.m_ServiceLevel; GradeCardServiceData gradeCardServiceData = CSingleton.Instance.m_MonsterData_SO.GetGradeCardServiceData(serviceLevel); float total = 10f + gradeCardServiceData.m_CostPerCard * (float)picked.Count; if (CPlayerData.m_CoinAmountDouble < (double)total) { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)0); return; } if (CPlayerData.m_GradeCardInProgressList != null && CPlayerData.m_GradeCardInProgressList.Count >= 4) { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)4); return; } GradingSync instance = Instance; if (instance == null || instance.SendOp == null) { CoopPlugin.Log.LogWarning((object)"GradingSync: no host link, submission cancelled"); return; } instance.SendOp(delegate(BinaryWriter bw) { bw.Write((byte)Mathf.Clamp(serviceLevel, 0, 3)); bw.Write((byte)Mathf.Min(picked.Count, 8)); for (int j = 0; j < picked.Count && j < 8; j++) { Msg.WriteCard(bw, picked[j]); } bw.Write(total); }); SoundManager.PlayAudio("SFX_CustomerBuy", 0.6f, 1f); GradeCardSubmitSet val2 = new GradeCardSubmitSet { m_ServiceLevel = serviceLevel, m_CardDataList = new List(8) }; for (int num3 = 0; num3 < 8; num3++) { val2.m_CardDataList.Add(new CardData()); } CPlayerData.m_CurrentGradeCardSubmitSet = val2; ((UIScreenBase)screen).CloseScreen(); try { GradeCardWebsiteUIScreen gradeCardWebsiteUIScreen = screen.m_GradeCardWebsiteUIScreen; if (gradeCardWebsiteUIScreen != null) { gradeCardWebsiteUIScreen.UpdateSubmissionProgressPanelUI(); } } catch { } try { InteractionPlayerController instance2 = CSingleton.Instance; if (instance2 != null) { CollectionBinderFlipAnimCtrl collectionBinderFlipAnimCtrl = instance2.m_CollectionBinderFlipAnimCtrl; if (collectionBinderFlipAnimCtrl != null) { collectionBinderFlipAnimCtrl.SetCanUpdateSort(true); } } } catch { } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "cards sent for grading - they mature on the host's days"; CoopCore.Instance.RegisterLineTimer = 4f; } } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } public void HostTick(float dt, bool inGame) { if (!inGame) { return; } _timer += dt; if (_timer < 1.5f) { return; } _timer -= 1.5f; try { List list = CPlayerData.m_GradeCardInProgressList; if (list == null) { return; } int num = ComputeHash(list); _heal += 1.5f; if (num != _lastHash || !(_heal < 15f)) { _lastHash = num; _heal = 0f; BroadcastState?.Invoke(delegate(BinaryWriter bw) { WriteState(bw, list); }); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("GradingSync host: " + ex.Message)); } } public void HostApplyOp(BinaryReader br) { //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected O, but got Unknown //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Expected O, but got Unknown int num = Mathf.Clamp((int)br.ReadByte(), 0, 3); int num2 = Mathf.Min((int)br.ReadByte(), 8); List list = new List(num2); for (int i = 0; i < num2; i++) { list.Add(Msg.ReadCard(br)); } float num3 = br.ReadSingle(); if (CoopCore.Role != CoopRole.Host || list.Count == 0) { return; } try { GradeCardServiceData gradeCardServiceData = CSingleton.Instance.m_MonsterData_SO.GetGradeCardServiceData(num); float num4 = 10f + gradeCardServiceData.m_CostPerCard * (float)list.Count; if (Mathf.Abs(num4 - num3) > 0.01f) { CoopPlugin.Log.LogWarning((object)$"GradingSync: fee mismatch (client {num3}, host {num4}) - using host value"); } if (CPlayerData.m_GradeCardInProgressList == null || CPlayerData.m_GradeCardInProgressList.Count >= 4 || CPlayerData.m_CoinAmountDouble < (double)num4) { CoopPlugin.Log.LogWarning((object)"GradingSync: submission rejected (slots/wallet), returning cards to binder"); for (int j = 0; j < list.Count; j++) { CPlayerData.AddCard(list[j], 1); } return; } for (int k = 0; k < list.Count; k++) { if (list[k].cardGrade > 0) { try { CPlayerData.RemoveGradedCard(list[k], true); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("GradingSync degrade: " + ex.Message)); } } } PriceChangeManager.AddTransaction(0f - num4, (ETransactionType)11, num, 0, (CardData)null); CPlayerData.m_GameReportDataCollect.supplyCost -= num4; CPlayerData.m_GameReportDataCollectPermanent.supplyCost -= num4; CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(num4, false)); GradeCardSubmitSet val = new GradeCardSubmitSet { m_ServiceLevel = num, m_DayPassed = 0, m_MinutePassed = 0f, m_CardDataList = new List(8) }; val.m_CardDataList.AddRange(list); while (val.m_CardDataList.Count < 8) { val.m_CardDataList.Add(new CardData()); } CPlayerData.m_GradeCardInProgressList.Add(val); ForceResend(); } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("GradingSync op: " + ex2.Message)); } } public void ClientApplyState(BinaryReader br) { ApplyingRemote = true; try { ClientApplyInner(br); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("GradingSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(BinaryReader br) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown int num = Mathf.Min((int)br.ReadByte(), 8); List list = new List(num); for (int i = 0; i < num; i++) { GradeCardSubmitSet val = new GradeCardSubmitSet { m_ServiceLevel = br.ReadByte(), m_DayPassed = br.ReadByte(), m_MinutePassed = br.ReadSingle(), m_CardDataList = new List(8) }; int num2 = Mathf.Min((int)br.ReadByte(), 8); for (int j = 0; j < num2; j++) { val.m_CardDataList.Add(Msg.ReadCard(br)); } while (val.m_CardDataList.Count < 8) { val.m_CardDataList.Add(new CardData()); } list.Add(val); } CPlayerData.m_GradeCardInProgressList = list; try { if ((Object)(object)_website == (Object)null) { _website = Object.FindObjectOfType(); } if ((Object)(object)_website != (Object)null && ((Component)_website).gameObject.activeInHierarchy) { _website.UpdateSubmissionProgressPanelUI(); } } catch { } } private static void WriteState(BinaryWriter bw, List list) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min(list.Count, 8); bw.Write((byte)num); for (int i = 0; i < num; i++) { GradeCardSubmitSet val = list[i]; bw.Write((byte)Mathf.Clamp(val?.m_ServiceLevel ?? 0, 0, 255)); bw.Write((byte)Mathf.Clamp(val?.m_DayPassed ?? 0, 0, 255)); bw.Write(val?.m_MinutePassed ?? 0f); List list2 = val?.m_CardDataList; int num2 = ((list2 != null) ? Mathf.Min(list2.Count, 8) : 0); bw.Write((byte)num2); for (int j = 0; j < num2; j++) { Msg.WriteCard(bw, (CardData)(((object)list2[j]) ?? ((object)new CardData()))); } } } private static int ComputeHash(List list) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected I4, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected I4, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected I4, but got Unknown int num = 17; num = num * 31 + list.Count; for (int i = 0; i < list.Count && i < 8; i++) { GradeCardSubmitSet val = list[i]; if (val == null) { continue; } num = num * 31 + val.m_ServiceLevel; num = num * 31 + val.m_DayPassed; num = num * 31 + (int)(val.m_MinutePassed / 60f); List cardDataList = val.m_CardDataList; if (cardDataList == null) { continue; } for (int j = 0; j < cardDataList.Count && j < 8; j++) { CardData val2 = cardDataList[j]; if (val2 != null) { num = num * 31 + val2.monsterType; num = num * 31 + val2.expansionType; num = num * 31 + val2.borderType; num = num * 31 + (int)((val2.isFoil ? 1u : 0u) | (uint)(val2.isDestiny ? 2 : 0) | (uint)(val2.isChampionCard ? 4 : 0)); num = num * 31 + val2.cardGrade; } } } return num; } } public class MarketSync { public static bool ApplyingRemote; public Action> BroadcastState; private const float Interval = 2f; private const float HealEvery = 20f; private float _timer; private int _lastHash; private float _heal; private int _lastAppliedGen; private static int s_rollGen; public void Reset() { _timer = -3.4f; _lastHash = 0; _heal = 0f; _lastAppliedGen = int.MinValue; } public void ForceResend() { _lastHash = 0; _heal = 20f; } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_003f: Expected O, but got Unknown Try(h, typeof(PriceChangeManager), "OnDayStarted", new HarmonyMethod(typeof(MarketSync), "ClientBlockPrefix", (Type[])null), new HarmonyMethod(typeof(MarketSync), "HostRolledPostfix", (Type[])null)); } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed: " + type.Name + "." + method + ": " + ex.Message)); } } public static bool ClientBlockPrefix() { return CoopCore.Role != CoopRole.Client; } public static void HostRolledPostfix() { if (CoopCore.Role == CoopRole.Host) { s_rollGen++; } } public void HostTick(float dt, bool inGame) { if (!inGame || BroadcastState == null) { return; } _timer += dt; if (_timer < 2f) { return; } _timer -= 2f; try { if (CPlayerData.m_ItemPricePercentChangeList != null && CPlayerData.m_ItemPricePercentChangeList.Count != 0) { int num = 17; num = num * 31 + s_rollGen; num = HashFloats(num, CPlayerData.m_ItemPricePercentChangeList); num = HashMarket(num, CPlayerData.m_GenCardMarketPriceList); num = HashMarket(num, CPlayerData.m_GenCardMarketPriceListDestiny); num = HashMarket(num, CPlayerData.m_GenCardMarketPriceListGhost); num = HashMarket(num, CPlayerData.m_GenCardMarketPriceListGhostBlack); num = HashMarket(num, CPlayerData.m_GenCardMarketPriceListMegabot); num = HashMarket(num, CPlayerData.m_GenCardMarketPriceListFantasyRPG); num = HashMarket(num, CPlayerData.m_GenCardMarketPriceListCatJob); num = HashFloats(num, CPlayerData.m_SetGameEventPriceList); num = HashFloats(num, CPlayerData.m_GeneratedGameEventPriceList); num = HashFloats(num, CPlayerData.m_GameEventPricePercentChangeList); num = HashFloats(num, CPlayerData.m_GeneratedMarketPriceList); num = HashFloats(num, CPlayerData.m_GeneratedCostPriceList); num = HashFloats(num, CPlayerData.m_AverageItemCostList); _heal += 2f; if (num != _lastHash || !(_heal < 20f)) { _lastHash = num; _heal = 0f; BroadcastState(WriteState); } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("MarketSync host: " + ex.Message)); } } private static void WriteState(BinaryWriter bw) { bw.Write(s_rollGen); WritePercents(bw, CPlayerData.m_ItemPricePercentChangeList); WriteMarket(bw, CPlayerData.m_GenCardMarketPriceList); WriteMarket(bw, CPlayerData.m_GenCardMarketPriceListDestiny); WriteMarket(bw, CPlayerData.m_GenCardMarketPriceListGhost); WriteMarket(bw, CPlayerData.m_GenCardMarketPriceListGhostBlack); WriteMarket(bw, CPlayerData.m_GenCardMarketPriceListMegabot); WriteMarket(bw, CPlayerData.m_GenCardMarketPriceListFantasyRPG); WriteMarket(bw, CPlayerData.m_GenCardMarketPriceListCatJob); WriteFloats(bw, CPlayerData.m_SetGameEventPriceList); WriteFloats(bw, CPlayerData.m_GeneratedGameEventPriceList); WriteFloats(bw, CPlayerData.m_GameEventPricePercentChangeList); WriteSparseFloats(bw, CPlayerData.m_GeneratedMarketPriceList); WriteSparseFloats(bw, CPlayerData.m_GeneratedCostPriceList); WriteSparseFloats(bw, CPlayerData.m_AverageItemCostList); } public void ClientApplyState(BinaryReader br) { ApplyingRemote = true; try { ClientApplyInner(br); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("MarketSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(BinaryReader br) { int num = br.ReadInt32(); ReadPercentsInto(br, CPlayerData.m_ItemPricePercentChangeList); ReadMarketInto(br, CPlayerData.m_GenCardMarketPriceList); ReadMarketInto(br, CPlayerData.m_GenCardMarketPriceListDestiny); ReadMarketInto(br, CPlayerData.m_GenCardMarketPriceListGhost); ReadMarketInto(br, CPlayerData.m_GenCardMarketPriceListGhostBlack); ReadMarketInto(br, CPlayerData.m_GenCardMarketPriceListMegabot); ReadMarketInto(br, CPlayerData.m_GenCardMarketPriceListFantasyRPG); ReadMarketInto(br, CPlayerData.m_GenCardMarketPriceListCatJob); ReadFloatsInto(br, CPlayerData.m_SetGameEventPriceList); ReadFloatsInto(br, CPlayerData.m_GeneratedGameEventPriceList); ReadFloatsInto(br, CPlayerData.m_GameEventPricePercentChangeList); ReadSparseFloatsInto(br, CPlayerData.m_GeneratedMarketPriceList); ReadSparseFloatsInto(br, CPlayerData.m_GeneratedCostPriceList); ReadSparseFloatsInto(br, CPlayerData.m_AverageItemCostList); if (_lastAppliedGen == int.MinValue) { _lastAppliedGen = num; } else if (num != _lastAppliedGen) { _lastAppliedGen = num; try { CPlayerData.UpdateItemPricePercentChange(); CPlayerData.UpdatePastCardPricePercentChange(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("MarketSync history: " + ex.Message)); } } } private static void WritePercents(BinaryWriter bw, List list) { int num = 0; if (list != null) { for (int i = 0; i < list.Count; i++) { if (list[i] != 0f) { num++; } } } bw.Write(num); if (list == null) { return; } for (int j = 0; j < list.Count; j++) { if (list[j] != 0f) { bw.Write(j); bw.Write((short)Mathf.Clamp(Mathf.RoundToInt(list[j] * 100f), -32768, 32767)); } } } private static void WriteSparseFloats(BinaryWriter bw, List list) { int num = 0; if (list != null) { for (int i = 0; i < list.Count; i++) { if (list[i] != 0f) { num++; } } } bw.Write(num); if (list == null) { return; } for (int j = 0; j < list.Count; j++) { if (list[j] != 0f) { bw.Write(j); bw.Write(list[j]); } } } private static void ReadSparseFloatsInto(BinaryReader br, List list) { int num = br.ReadInt32(); for (int i = 0; i < num; i++) { int num2 = br.ReadInt32(); float value = br.ReadSingle(); if (list != null && num2 >= 0 && num2 <= 500000) { while (list.Count <= num2) { list.Add(0f); } list[num2] = value; } } } private static void ReadPercentsInto(BinaryReader br, List list) { if (list != null) { for (int i = 0; i < list.Count; i++) { list[i] = 0f; } } int num = br.ReadInt32(); for (int j = 0; j < num; j++) { int num2 = br.ReadInt32(); float value = (float)br.ReadInt16() / 100f; if (list != null && num2 >= 0 && num2 <= 500000) { while (list.Count <= num2) { list.Add(0f); } list[num2] = value; } } } private static void WriteMarket(BinaryWriter bw, List list) { int num = Mathf.Min(list?.Count ?? 0, 65535); bw.Write((ushort)num); for (int i = 0; i < num; i++) { float num2 = ((list[i] != null) ? list[i].pricePercentChangeList : 0f); bw.Write((short)Mathf.Clamp(Mathf.RoundToInt(num2 * 100f), -32768, 32767)); } } private static void ReadMarketInto(BinaryReader br, List list) { int num = br.ReadUInt16(); for (int i = 0; i < num; i++) { float pricePercentChangeList = (float)br.ReadInt16() / 100f; if (list != null && i < list.Count && list[i] != null) { list[i].pricePercentChangeList = pricePercentChangeList; } } } private static void WriteFloats(BinaryWriter bw, List list) { int num = Mathf.Min(list?.Count ?? 0, 65535); bw.Write((ushort)num); for (int i = 0; i < num; i++) { bw.Write(list[i]); } } private static void ReadFloatsInto(BinaryReader br, List list) { int num = br.ReadUInt16(); for (int i = 0; i < num; i++) { float value = br.ReadSingle(); if (list != null && i < list.Count) { list[i] = value; } } } private static int HashFloats(int h, List list) { if (list == null) { return h; } for (int i = 0; i < list.Count; i++) { h = h * 31 + (int)(list[i] * 100f); } return h; } private static int HashMarket(int h, List list) { if (list == null) { return h; } for (int i = 0; i < list.Count; i++) { h = h * 31 + (int)(((list[i] != null) ? list[i].pricePercentChangeList : 0f) * 100f); } return h; } } public class NpcSync { [Flags] private enum NpcFlags : byte { None = 0, HoldingBag = 1, HandingOverCash = 2, IsSitting = 4, IsPlaying = 8, IsHoldingBox = 0x10, Smelly = 0x20 } private struct Snap { public Vector3 Pos; public float Yaw; public float Speed; public NpcFlags Flags; public float Time; } private class Puppet { public GameObject Go; public Animator Anim; public CharacterCustomization Custom; public GameObject Bag; public GameObject Cash; public GameObject CardFan; public GameObject CardSingle; public GameObject Smelly; public string CharName = ""; public readonly Snap[] Buf = new Snap[4]; public int BufHead; public int BufCount; public NpcFlags Flags; public int AppliedFlags = -1; public float LastSeen; public float RenderYaw; public Vector3 PrevRenderedPos; public float AnimSpeed; } private const byte KindCustomer = 0; private const byte KindWorker = 1; private const float SendInterval = 0.125f; private const float InterpDelay = 0.15f; private const int ChunkSoftLimit = 1100; private const float NameRefreshInterval = 5f; private static readonly int HashMoveSpeed = Animator.StringToHash("MoveSpeed"); private static readonly int HashHoldingBag = Animator.StringToHash("HoldingBag"); private static readonly int HashHandingOverCash = Animator.StringToHash("HandingOverCash"); private static readonly int HashIsSitting = Animator.StringToHash("IsSitting"); private static readonly int HashIsPlaying = Animator.StringToHash("IsPlaying"); private static readonly int HashIsHoldingBox = Animator.StringToHash("IsHoldingBox"); private CustomerManager _cm; private float _sendTimer; private float _nameRefreshIn; private MemoryStream _sendMs; private BinaryWriter _sendBw; private int _chunkCount; private readonly Dictionary _sentNames = new Dictionary(); private readonly Dictionary _puppets = new Dictionary(); private CustomerManager _cmClient; private float _now; private float _clockOffset; private bool _clockInit; private static CustomerManager s_diagCm; public int PuppetCount => _puppets.Count; public void Reset() { _cm = null; _sendTimer = 0f; _nameRefreshIn = 0f; _sentNames.Clear(); ClearPuppets(); } public List HostCollect(float dt) { _sendTimer += dt; if (_sendTimer < 0.125f) { return null; } _sendTimer -= 0.125f; if (_sendTimer > 0.125f) { _sendTimer = 0.125f; } if ((Object)(object)_cm == (Object)null) { _cm = Object.FindObjectOfType(); } if ((Object)(object)_cm == (Object)null) { return null; } _nameRefreshIn -= 0.125f; if (_nameRefreshIn <= 0f) { _sentNames.Clear(); _nameRefreshIn = 5f; } if (_sendMs == null) { _sendMs = new MemoryStream(1280); _sendBw = new BinaryWriter(_sendMs); } List list = new List(1); float unscaledTime = Time.unscaledTime; BeginChunk(unscaledTime); List customerList = _cm.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { Customer val = customerList[i]; if ((Object)(object)val == (Object)null || !val.m_IsActive || !((Component)val).gameObject.activeSelf) { continue; } CharacterCustomization characterCustom = val.m_CharacterCustom; if ((Object)(object)characterCustom == (Object)null || string.IsNullOrEmpty(characterCustom.CharacterName)) { continue; } NpcFlags npcFlags = CollectFlags(val.m_Anim); try { if (val.IsSmelly()) { npcFlags |= NpcFlags.Smelly; } } catch { } WriteEntry(list, unscaledTime, 0, (ushort)i, characterCustom.CharacterName, ((Component)val).transform, val.m_CurrentMoveSpeed, npcFlags); } List workerList = WorkerManager.GetWorkerList(); if (workerList != null) { for (int j = 0; j < workerList.Count; j++) { Worker val2 = workerList[j]; if (!((Object)(object)val2 == (Object)null) && val2.m_IsActive && ((Component)val2).gameObject.activeSelf) { CharacterCustomization characterCustom2 = val2.m_CharacterCustom; if (!((Object)(object)characterCustom2 == (Object)null) && !string.IsNullOrEmpty(characterCustom2.CharacterName)) { WriteEntry(list, unscaledTime, 1, (ushort)j, characterCustom2.CharacterName, ((Component)val2).transform, 0f, CollectFlags(val2.m_Anim), val2.m_Anim); } } } } FlushChunk(list); if (list.Count <= 0) { return null; } return list; } private void BeginChunk(float hostTime) { _sendMs.SetLength(0L); _sendBw.Write(hostTime); _sendBw.Write((byte)0); _chunkCount = 0; } private void FlushChunk(List chunks) { if (_chunkCount != 0) { _sendBw.Flush(); _sendMs.Position = 4L; _sendMs.WriteByte((byte)_chunkCount); chunks.Add(_sendMs.ToArray()); _chunkCount = 0; } } private void WriteEntry(List chunks, float hostTime, byte kind, ushort index, string charName, Transform t, float moveSpeed, NpcFlags flags, Animator speedFromAnim = null) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) if (_sendMs.Position >= 1100 || _chunkCount == 255) { FlushChunk(chunks); BeginChunk(hostTime); } if ((Object)(object)speedFromAnim != (Object)null) { try { moveSpeed = speedFromAnim.GetFloat(HashMoveSpeed); } catch { } } int key = (kind << 16) | index; string value; bool flag = !_sentNames.TryGetValue(key, out value) || value != charName; if (flag) { _sentNames[key] = charName; } _sendBw.Write(kind); _sendBw.Write(index); _sendBw.Write(flag ? ((byte)1) : ((byte)0)); if (flag) { _sendBw.Write(charName); } Vector3 position = t.position; _sendBw.Write(position.x); _sendBw.Write(position.y); _sendBw.Write(position.z); _sendBw.Write(t.eulerAngles.y); _sendBw.Write(moveSpeed); _sendBw.Write((byte)flags); _chunkCount++; } private static NpcFlags CollectFlags(Animator anim) { NpcFlags npcFlags = NpcFlags.None; if ((Object)(object)anim == (Object)null) { return npcFlags; } try { if (anim.GetBool(HashHoldingBag)) { npcFlags |= NpcFlags.HoldingBag; } if (anim.GetBool(HashHandingOverCash)) { npcFlags |= NpcFlags.HandingOverCash; } if (anim.GetBool(HashIsSitting)) { npcFlags |= NpcFlags.IsSitting; } if (anim.GetBool(HashIsPlaying)) { npcFlags |= NpcFlags.IsPlaying; } if (anim.GetBool(HashIsHoldingBox)) { npcFlags |= NpcFlags.IsHoldingBox; } } catch { } return npcFlags; } public static int CountLocalActiveNpcs() { if ((Object)(object)s_diagCm == (Object)null) { s_diagCm = Object.FindObjectOfType(); } int num = 0; if ((Object)(object)s_diagCm != (Object)null) { List customerList = s_diagCm.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { if ((Object)(object)customerList[i] != (Object)null && ((Component)customerList[i]).gameObject.activeSelf) { num++; } } } List workerList = WorkerManager.GetWorkerList(); if (workerList != null) { for (int j = 0; j < workerList.Count; j++) { if ((Object)(object)workerList[j] != (Object)null && ((Component)workerList[j]).gameObject.activeSelf) { num++; } } } return num; } public void ClearPuppets() { foreach (Puppet value in _puppets.Values) { if ((Object)(object)value.Go != (Object)null) { Object.Destroy((Object)(object)value.Go); } } _puppets.Clear(); _cmClient = null; _clockInit = false; } public void ApplyBatch(BinaryReader br, bool inGame) { //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) float num = br.ReadSingle(); int num2 = br.ReadByte(); float num3 = _now - num; if (!_clockInit || Mathf.Abs(num3 - _clockOffset) > 1f) { if (_clockInit) { foreach (Puppet value2 in _puppets.Values) { value2.BufCount = 0; } } _clockOffset = num3; _clockInit = true; } else { _clockOffset += 0.1f * (num3 - _clockOffset); } float num4 = num + _clockOffset; Vector3 pos = default(Vector3); for (int i = 0; i < num2; i++) { byte b = br.ReadByte(); ushort num5 = br.ReadUInt16(); bool flag = (br.ReadByte() & 1) != 0; string text = (flag ? br.ReadString() : null); ((Vector3)(ref pos))..ctor(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); float yaw = br.ReadSingle(); float speed = br.ReadSingle(); NpcFlags flags = (NpcFlags)br.ReadByte(); if (!inGame) { continue; } int key = (b << 16) | num5; if (!_puppets.TryGetValue(key, out var value)) { if (!flag) { continue; } value = new Puppet(); _puppets[key] = value; } if (flag && value.CharName != text) { ReDress(value, text, pos); } else if ((Object)(object)value.Go == (Object)null && value.CharName.Length > 0) { Spawn(value, value.CharName, pos); } if (value.BufCount == 0 || num4 > value.Buf[value.BufHead].Time + 0.0005f) { value.BufHead = (value.BufHead + 1) & 3; value.Buf[value.BufHead] = new Snap { Pos = pos, Yaw = yaw, Speed = speed, Flags = flags, Time = num4 }; if (value.BufCount < 4) { value.BufCount++; } } value.Flags = flags; value.LastSeen = _now; } } private void ReDress(Puppet p, string charName, Vector3 pos) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) bool flag = p.CharName.Length > 0 && p.CharName.StartsWith("Female") != charName.StartsWith("Female"); if ((Object)(object)p.Go == (Object)null || (Object)(object)p.Custom == (Object)null || flag) { if ((Object)(object)p.Go != (Object)null) { Object.Destroy((Object)(object)p.Go); } p.Go = null; Spawn(p, charName, pos); return; } p.CharName = charName; ((Object)p.Go).name = "CoopNpc_" + charName; try { p.Custom.CharacterName = charName; p.Custom.Initialize(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("NPC re-dressing '" + charName + "': " + ex.Message)); } } public void TickPuppets(float dt, bool inGame) { //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) _now += dt; if (!inGame || dt <= 0f) { return; } float renderTime = _now - 0.15f; float num = 1f - Mathf.Exp(-18f * dt); float num2 = 1f - Mathf.Exp(-14f * dt); float num3 = 1f - Mathf.Exp(-8f * dt); List list = null; foreach (KeyValuePair puppet in _puppets) { Puppet value = puppet.Value; if (_now - value.LastSeen > 1.5f) { if ((Object)(object)value.Go != (Object)null) { Object.Destroy((Object)(object)value.Go); } (list = list ?? new List()).Add(puppet.Key); } else { if ((Object)(object)value.Go == (Object)null || value.BufCount == 0) { continue; } Sample(value, renderTime, out var target, out var targetYaw); Transform transform = value.Go.transform; Vector3 val = transform.position - target; bool flag = ((Vector3)(ref val)).sqrMagnitude > 25f; Vector3 val2 = (transform.position = (flag ? target : Vector3.Lerp(transform.position, target, num))); value.RenderYaw = (flag ? targetYaw : Mathf.LerpAngle(value.RenderYaw, targetYaw, num2)); transform.rotation = Quaternion.Euler(0f, value.RenderYaw, 0f); float num4; if (!flag) { val = val2 - value.PrevRenderedPos; num4 = Mathf.Min(((Vector3)(ref val)).magnitude / dt, 10f); } else { num4 = 0f; } float num5 = num4; value.PrevRenderedPos = val2; value.AnimSpeed = Mathf.Lerp(value.AnimSpeed, num5, num3); if (value.AnimSpeed < 0.05f) { value.AnimSpeed = 0f; } if ((Object)(object)value.Anim != (Object)null) { try { value.Anim.SetFloat(HashMoveSpeed, value.AnimSpeed); } catch { } } if ((int)value.Flags == value.AppliedFlags) { continue; } if ((Object)(object)value.Anim != (Object)null) { try { value.Anim.SetBool(HashHoldingBag, (value.Flags & NpcFlags.HoldingBag) != 0); value.Anim.SetBool(HashHandingOverCash, (value.Flags & NpcFlags.HandingOverCash) != 0); value.Anim.SetBool(HashIsSitting, (value.Flags & NpcFlags.IsSitting) != 0); value.Anim.SetBool(HashIsPlaying, (value.Flags & NpcFlags.IsPlaying) != 0); value.Anim.SetBool(HashIsHoldingBox, (value.Flags & NpcFlags.IsHoldingBox) != 0); } catch { } } Toggle(value.Bag, (value.Flags & NpcFlags.HoldingBag) != 0); Toggle(value.Cash, (value.Flags & NpcFlags.HandingOverCash) != 0); Toggle(value.CardFan, (value.Flags & NpcFlags.IsPlaying) != 0); Toggle(value.CardSingle, (value.Flags & NpcFlags.IsPlaying) != 0); Toggle(value.Smelly, (value.Flags & NpcFlags.Smelly) != 0); value.AppliedFlags = (int)value.Flags; } } if (list == null) { return; } foreach (int item in list) { _puppets.Remove(item); } } private static void Sample(Puppet p, float renderTime, out Vector3 target, out float targetYaw) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) Snap snap = p.Buf[p.BufHead]; if (snap.Time <= renderTime) { Vector3 val = Vector3.zero; if (p.BufCount >= 2) { Snap snap2 = p.Buf[(p.BufHead + 3) & 3]; float num = snap.Time - snap2.Time; if (num > 0.001f) { val = (snap.Pos - snap2.Pos) / num; val.y = 0f; val = Vector3.ClampMagnitude(val, 5f); } } float num2 = Mathf.Min(renderTime - snap.Time, 0.25f); val *= Mathf.Exp(-3f * num2); target = snap.Pos + val * num2; targetYaw = snap.Yaw; return; } Snap snap3 = snap; for (int i = 1; i < p.BufCount; i++) { Snap snap4 = p.Buf[(p.BufHead - i + 4) & 3]; if (snap4.Time <= renderTime) { float num3 = snap3.Time - snap4.Time; float num4 = ((num3 > 0.0001f) ? ((renderTime - snap4.Time) / num3) : 1f); target = Vector3.Lerp(snap4.Pos, snap3.Pos, num4); targetYaw = Mathf.LerpAngle(snap4.Yaw, snap3.Yaw, num4); return; } snap3 = snap4; } target = snap3.Pos; targetYaw = snap3.Yaw; } private static void Toggle(GameObject go, bool on) { if ((Object)(object)go != (Object)null && go.activeSelf != on) { go.SetActive(on); } } private void Spawn(Puppet p, string charName, Vector3 pos) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_cmClient == (Object)null) { _cmClient = Object.FindObjectOfType(); } if ((Object)(object)_cmClient == (Object)null) { return; } Customer val = (charName.StartsWith("Female") ? _cmClient.m_CustomerFemalePrefab : _cmClient.m_CustomerPrefab); if ((Object)(object)val == (Object)null) { return; } GameObject val2 = new GameObject("CoopNpcHolder_tmp"); val2.SetActive(false); GameObject val3 = Object.Instantiate(((Component)val).gameObject, val2.transform); val3.transform.SetParent((Transform)null, false); val3.transform.position = pos; val3.SetActive(true); Object.Destroy((Object)(object)val2); Customer component = val3.GetComponent(); p.Custom = (((Object)(object)component != (Object)null) ? component.m_CharacterCustom : null); try { if ((Object)(object)p.Custom != (Object)null && charName.Length > 0) { p.Custom.CharacterName = charName; p.Custom.Initialize(); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("NPC dressing '" + charName + "': " + ex.Message)); } if ((Object)(object)component != (Object)null) { p.Bag = (((Object)(object)component.m_ShoppingBagTransform != (Object)null) ? ((Component)component.m_ShoppingBagTransform).gameObject : null); p.Cash = (((Object)(object)component.m_CustomerCash != (Object)null) ? ((Component)component.m_CustomerCash).gameObject : null); p.CardFan = component.m_GameCardFanOut; p.CardSingle = component.m_GameCardSingle; p.Smelly = component.m_SmellyFX; try { Toggle(p.Bag, on: false); Toggle(p.Cash, on: false); Toggle(p.CardFan, on: false); Toggle(p.CardSingle, on: false); if ((Object)(object)component.m_CleanFX != (Object)null) { component.m_CleanFX.SetActive(false); } if ((Object)(object)component.m_ExclaimationMesh != (Object)null) { component.m_ExclaimationMesh.SetActive(false); } if ((Object)(object)component.m_InteractCollider != (Object)null) { component.m_InteractCollider.SetActive(false); } if ((Object)(object)component.m_SmellyFX != (Object)null) { component.m_SmellyFX.SetActive(false); } } catch { } } MonoBehaviour[] componentsInChildren = val3.GetComponentsInChildren(true); foreach (MonoBehaviour val4 in componentsInChildren) { if (!((Object)(object)val4 == (Object)null)) { switch (((object)val4).GetType().Name) { case "CopyPose": case "BlendshapeManager": case "ScaleCharacter": case "TransformBone": case "MipBiasAdjust": case "CharacterCustomization": continue; } Object.DestroyImmediate((Object)(object)val4); } } Component[] componentsInChildren2 = val3.GetComponentsInChildren(true); foreach (Component val5 in componentsInChildren2) { if (!((Object)(object)val5 == (Object)null)) { switch (((object)val5).GetType().Name) { case "NavMeshAgent": case "NavMeshObstacle": case "Seeker": case "FunnelModifier": Object.DestroyImmediate((Object)(object)val5); break; } } } Collider[] componentsInChildren3 = val3.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } Rigidbody[] componentsInChildren4 = val3.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren4.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren4[i]); } ((Object)val3).name = "CoopNpc_" + charName; p.Go = val3; p.Anim = val3.GetComponentInChildren(true); p.CharName = charName; p.PrevRenderedPos = pos; p.RenderYaw = 0f; p.AnimSpeed = 0f; p.AppliedFlags = -1; } } public class ObjMoveSync { public struct Entry { public int Key; public Vector3 Pos; public Quaternion Rot; } private struct Pose { public Vector3 P; public Quaternion R; public bool Valid; public bool Same(Vector3 p, Quaternion r) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //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_0014: 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_0029: Unknown result type (might be due to invalid IL or missing references) if (Valid) { Vector3 val = P - p; if (((Vector3)(ref val)).sqrMagnitude < 0.0004f) { return Mathf.Abs(Quaternion.Dot(R, r)) > 0.99999f; } } return false; } } private readonly Dictionary _sent = new Dictionary(); private readonly Dictionary _candidate = new Dictionary(); private ShelfManager _sm; private float _timer; public Action> OnLocalChanges; private static readonly Dictionary _tagGrpFields = new Dictionary(); public void Reset() { _sent.Clear(); _candidate.Clear(); _sm = null; _timer = -0.25f; } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } public void Tick(float dt, bool active) { if (!active) { return; } _timer += dt; if (_timer < 1f) { return; } _timer -= 1f; List changes = null; try { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } for (int i = 0; i < 15; i++) { Walk(PopulationSync.GetList(val, i), i, ref changes); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ObjMoveSync snapshot: " + ex.Message)); return; } if (changes != null && changes.Count > 0) { OnLocalChanges?.Invoke(changes); } } private void Walk(IList list, int kind, ref List changes) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) if (list == null) { return; } for (int i = 0; i < list.Count; i++) { object? obj = list[i]; Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || !val.gameObject.activeInHierarchy) { continue; } int key = (kind << 24) | (i & 0xFFFF); Vector3 position = val.transform.position; Quaternion rotation = val.transform.rotation; Pose value2; if (_sent.TryGetValue(key, out var value) && value.Same(position, rotation)) { _candidate.Remove(key); } else if (_candidate.TryGetValue(key, out value2) && value2.Same(position, rotation)) { _sent[key] = new Pose { P = position, R = rotation, Valid = true }; _candidate.Remove(key); if (changes == null) { changes = new List(); } if (changes.Count >= 64) { break; } changes.Add(new Entry { Key = key, Pos = position, Rot = rotation }); } else { _candidate[key] = new Pose { P = position, R = rotation, Valid = true }; } } } public void ApplyRemote(List entries) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } foreach (Entry entry in entries) { try { Transform val2 = Resolve(val, entry.Key); if (!((Object)(object)val2 == (Object)null)) { val2.SetPositionAndRotation(entry.Pos, entry.Rot); SyncTagGroup(val2); _sent[entry.Key] = new Pose { P = entry.Pos, R = entry.Rot, Valid = true }; _candidate.Remove(entry.Key); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"ObjMoveSync apply {entry.Key:X}: {ex.Message}"); } } } public static void SyncTagGroup(Transform objTransform) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) InteractableObject component = ((Component)objTransform).GetComponent(); if (!((Object)(object)component == (Object)null)) { Type type = ((object)component).GetType(); if (!_tagGrpFields.TryGetValue(type, out var value)) { value = AccessTools.Field(type, "m_Shelf_WorldUIGrp"); _tagGrpFields[type] = value; } object? obj = value?.GetValue(component); Transform val = (Transform)((obj is Transform) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { val.SetPositionAndRotation(objTransform.position, objTransform.rotation); } } } private static Transform Resolve(ShelfManager sm, int key) { int kind = key >> 24; int num = key & 0xFFFF; IList list = PopulationSync.GetList(sm, kind); if (list == null || num >= list.Count) { return null; } object? obj = list[num]; object? obj2 = ((obj is Component) ? obj : null); if (obj2 == null) { return null; } return ((Component)obj2).transform; } public static void WriteEntries(BinaryWriter bw, List entries) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)entries.Count); foreach (Entry entry in entries) { bw.Write(entry.Key); bw.Write(entry.Pos.x); bw.Write(entry.Pos.y); bw.Write(entry.Pos.z); bw.Write(entry.Rot.x); bw.Write(entry.Rot.y); bw.Write(entry.Rot.z); bw.Write(entry.Rot.w); } } public static List ReadEntries(BinaryReader br) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) int num = br.ReadByte(); List list = new List(num); for (int i = 0; i < num; i++) { list.Add(new Entry { Key = br.ReadInt32(), Pos = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()), Rot = new Quaternion(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle()) }); } return list; } } public class PlayTableSync { private struct SeatState { public bool Active; public int PlayMat; public int DeckBox; public int Comic; public bool Same(SeatState o) { if (Active == o.Active && PlayMat == o.PlayMat && DeckBox == o.DeckBox) { return Comic == o.Comic; } return false; } } private const float Cadence = 1.5f; private const float HealInterval = 12f; private const int MaxTables = 250; private const int MaxSeats = 8; private const int MaxTableBytes = 250; public Action> BroadcastState; private float _timer; private int _lastHash; private float _heal; private bool _loggedDrop; private ShelfManager _sm; private readonly Dictionary _applied = new Dictionary(); public static void ApplyPatches(Harmony h) { } public void Reset() { ClearMirrors(); _applied.Clear(); _timer = -7.6f; _lastHash = 0; _heal = 0f; _loggedDrop = false; _sm = null; } public void ForceResend() { _lastHash = 0; _heal = 999f; } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } public void HostTick(float dt, bool inGame) { if (!inGame) { return; } _timer += dt; if (_timer < 1.5f) { return; } _timer -= 1.5f; try { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } List tables = val.m_PlayTableList; int num = 17; int count = Mathf.Min(tables.Count, 250); if (tables.Count > 250 && !_loggedDrop) { _loggedDrop = true; CoopPlugin.Log.LogWarning((object)$"PlayTableSync: {tables.Count - 250} play tables beyond the {250} cap are not mirrored"); } for (int i = 0; i < count; i++) { InteractablePlayTable val2 = tables[i]; num = num * 31 + ((!((Object)(object)val2 == (Object)null)) ? 1 : 0); if ((Object)(object)val2 == (Object)null) { continue; } List tableGameItemSetList = val2.m_TableGameItemSetList; int num2 = ((tableGameItemSetList != null) ? Mathf.Min(tableGameItemSetList.Count, 8) : 0); for (int j = 0; j < num2; j++) { SeatState seatState = HostSeat(tableGameItemSetList[j]); num = num * 31 + (seatState.Active ? 1 : 0); if (seatState.Active) { num = num * 31 + seatState.PlayMat; num = num * 31 + seatState.DeckBox; num = num * 31 + seatState.Comic; } } } _heal += 1.5f; if (num != _lastHash || !(_heal < 12f)) { _lastHash = num; _heal = 0f; BroadcastState?.Invoke(delegate(BinaryWriter bw) { WriteState(bw, tables, count); }); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("PlayTableSync host: " + ex.Message)); } } private static SeatState HostSeat(TableGameItemSet set) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)set == (Object)null || !((Component)set).gameObject.activeSelf) { return default(SeatState); } TableGameItemSetData tableGameItemSetData = set.m_TableGameItemSetData; return new SeatState { Active = true, PlayMat = ((tableGameItemSetData != null) ? ((int)tableGameItemSetData.playMatType) : 0), DeckBox = ((tableGameItemSetData != null) ? ((int)tableGameItemSetData.deckBoxType) : 0), Comic = ((tableGameItemSetData != null) ? ((int)tableGameItemSetData.comicBookType) : 0) }; } private static void WriteState(BinaryWriter bw, List tables, int count) { bw.Write((byte)count); for (int i = 0; i < count; i++) { InteractablePlayTable val = tables[i]; List list = (((Object)(object)val != (Object)null) ? val.m_TableGameItemSetList : null); int num = ((list != null) ? Mathf.Min(list.Count, 8) : 0); bw.Write((byte)i); bw.Write((byte)num); for (int j = 0; j < num; j++) { SeatState seatState = HostSeat(list[j]); bw.Write(seatState.Active); if (seatState.Active) { bw.Write(seatState.PlayMat); bw.Write(seatState.DeckBox); bw.Write(seatState.Comic); } } } } public void ClientApplyState(BinaryReader br) { try { ClientApplyInner(br); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("PlayTableSync apply: " + ex.Message)); } } private void ClientApplyInner(BinaryReader br) { ShelfManager val = Sm(); List list = (((Object)(object)val != (Object)null) ? val.m_PlayTableList : null); int num = br.ReadByte(); for (int i = 0; i < num; i++) { int num2 = br.ReadByte(); int num3 = br.ReadByte(); InteractablePlayTable val2 = ((list != null && num2 < list.Count) ? list[num2] : null); bool flag = (Object)(object)val2 == (Object)null || val2.GetHasStartPlayerPlayCard(); List list2 = ((!flag) ? val2.m_TableGameItemSetList : null); for (int j = 0; j < num3; j++) { SeatState want = new SeatState { Active = br.ReadBoolean() }; if (want.Active) { want.PlayMat = br.ReadInt32(); want.DeckBox = br.ReadInt32(); want.Comic = br.ReadInt32(); } if (!flag && list2 != null && j < list2.Count) { ApplySeat(num2, j, list2[j], want); } } } } private void ApplySeat(int tableIdx, int seat, TableGameItemSet set, SeatState want) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown if ((Object)(object)set == (Object)null) { return; } int key = (tableIdx << 8) | seat; if (_applied.TryGetValue(key, out var value) && value.Same(want) && ((Component)set).gameObject.activeSelf == want.Active) { return; } try { if (want.Active) { TableGameItemSetData val = new TableGameItemSetData { playMatType = (EItemType)want.PlayMat, deckBoxType = (EItemType)want.DeckBox, comicBookType = (EItemType)want.Comic }; set.SpecificSetup(val); ((Component)set).gameObject.SetActive(true); } else { ((Component)set).gameObject.SetActive(false); } _applied[key] = want; } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)($"PlayTableSync seat {tableIdx}/{seat}: " + ex.Message)); } } private void ClearMirrors() { if (_applied.Count == 0) { return; } ShelfManager sm = _sm; List list = (((Object)(object)sm != (Object)null) ? sm.m_PlayTableList : null); if (list != null) { foreach (KeyValuePair item in _applied) { if (!item.Value.Active) { continue; } int num = item.Key >> 8; int num2 = item.Key & 0xFF; try { if (num < list.Count && !((Object)(object)list[num] == (Object)null)) { List tableGameItemSetList = list[num].m_TableGameItemSetList; if (tableGameItemSetList != null && num2 < tableGameItemSetList.Count && (Object)(object)tableGameItemSetList[num2] != (Object)null) { ((Component)tableGameItemSetList[num2]).gameObject.SetActive(false); } } } catch { } } } _applied.Clear(); } } public class PopulationSync { public struct Entry { public int ObjType; public Vector3 Pos; public Quaternion Rot; } public const int KindCount = 15; private ShelfManager _sm; private float _timer; private int _lastHash; private float _heal; public Action>> OnHostSnapshot; public static Action OnClientStructureChanged; public static IList GetList(ShelfManager sm, int kind) { return kind switch { 0 => sm.m_ShelfList, 1 => sm.m_WarehouseShelfList, 2 => sm.m_CardShelfList, 3 => sm.m_CardItemCombiShelfList, 4 => sm.m_CashierCounterList, 5 => sm.m_DecoObjectList, 6 => sm.m_PlayTableList, 7 => sm.m_WorkbenchList, 8 => sm.m_TrashBinList, 9 => sm.m_CardStorageShelfList, 10 => sm.m_AutoCleanserList, 11 => sm.m_AutoPackOpenerList, 12 => sm.m_EmptyBoxStorageList, 13 => sm.m_BulkDonationBoxList, 14 => sm.m_TournamentPrizeShelfList, _ => null, }; } public void Reset() { _sm = null; _timer = -1.1f; _lastHash = 0; _heal = 0f; } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } public void HostTick(float dt, bool active) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected I4, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Expected I4, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) if (!active) { return; } _timer += dt; if (_timer < 3f) { return; } _timer -= 3f; try { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } int num = 17; for (int i = 0; i < 15; i++) { IList list = GetList(val, i); int num2 = list?.Count ?? 0; num = num * 31 + num2; if (list == null) { continue; } for (int j = 0; j < num2 && j < 250; j++) { object? obj = list[j]; InteractableObject val2 = (InteractableObject)((obj is InteractableObject) ? obj : null); if (val2 != null) { num = num * 31 + val2.m_ObjectType; } } } _heal += 3f; if (num == _lastHash && _heal < 30f) { return; } _lastHash = num; _heal = 0f; List> list2 = new List>(15); for (int k = 0; k < 15; k++) { IList list3 = GetList(val, k); List list4 = new List(list3?.Count ?? 0); if (list3 != null) { for (int l = 0; l < list3.Count && l < 250; l++) { object? obj2 = list3[l]; InteractableObject val3 = (InteractableObject)((obj2 is InteractableObject) ? obj2 : null); if (!((Object)(object)val3 == (Object)null)) { list4.Add(new Entry { ObjType = (int)val3.m_ObjectType, Pos = ((Component)val3).transform.position, Rot = ((Component)val3).transform.rotation }); } } } list2.Add(list4); } OnHostSnapshot?.Invoke(list2); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("PopulationSync host: " + ex.Message)); } } public void ClientApply(List> hostLists) { ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return; } for (int i = 0; i < 15 && i < hostLists.Count; i++) { try { ReconcileKind(val, i, hostLists[i]); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"PopulationSync kind {i}: {ex.Message}"); } } } private static void ReconcileKind(ShelfManager sm, int kind, List want) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Invalid comparison between Unknown and I4 //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) IList list = GetList(sm, kind); if (list == null) { return; } int num = 8; while (list.Count > want.Count && num-- > 0) { object? obj = list[list.Count - 1]; InteractableObject val = (InteractableObject)((obj is InteractableObject) ? obj : null); if ((Object)(object)val == (Object)null) { list.RemoveAt(list.Count - 1); continue; } CoopPlugin.Log.LogInfo((object)$"population: removing extra {val.m_ObjectType} (kind {kind})"); val.OnDestroyed(); OnClientStructureChanged?.Invoke(kind); list = GetList(sm, kind); } for (int i = 0; i < list.Count && i < want.Count; i++) { object? obj2 = list[i]; InteractableObject val2 = (InteractableObject)((obj2 is InteractableObject) ? obj2 : null); if (!((Object)(object)val2 == (Object)null) && (int)val2.m_ObjectType != want[i].ObjType) { CoopPlugin.Log.LogInfo((object)$"population: repairing index {i} (kind {kind}): {val2.m_ObjectType} -> {(object)(EObjectType)want[i].ObjType}"); val2.OnDestroyed(); OnClientStructureChanged?.Invoke(kind); return; } } num = 8; while (list.Count < want.Count && num-- > 0) { Entry entry = want[list.Count]; InteractableObject val3 = ShelfManager.SpawnInteractableObject((EObjectType)entry.ObjType); if (!((Object)(object)val3 == (Object)null)) { ((Component)val3).transform.SetPositionAndRotation(entry.Pos, entry.Rot); CoopPlugin.Log.LogInfo((object)$"population: spawned {(object)(EObjectType)entry.ObjType} (kind {kind})"); OnClientStructureChanged?.Invoke(kind); list = GetList(sm, kind); continue; } break; } } public static void Write(BinaryWriter bw, List> all) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)all.Count); foreach (List item in all) { bw.Write((byte)Mathf.Min(item.Count, 250)); for (int i = 0; i < item.Count && i < 250; i++) { Entry entry = item[i]; bw.Write(entry.ObjType); bw.Write(entry.Pos.x); bw.Write(entry.Pos.y); bw.Write(entry.Pos.z); bw.Write(entry.Rot.x); bw.Write(entry.Rot.y); bw.Write(entry.Rot.z); bw.Write(entry.Rot.w); } } } public static List> Read(BinaryReader br) { //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_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) int num = br.ReadByte(); List> list = new List>(num); for (int i = 0; i < num; i++) { int num2 = br.ReadByte(); List list2 = new List(num2); for (int j = 0; j < num2; j++) { list2.Add(new Entry { ObjType = br.ReadInt32(), Pos = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()), Rot = new Quaternion(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle()) }); } list.Add(list2); } return list; } } public static class RegisterServe { public struct CounterInfo { public byte Index; public byte State; public byte Scanned; public byte Total; public List ItemTypes; public List ItemLocal; } private static readonly FieldInfo FiMannedByPlayer = AccessTools.Field(typeof(InteractableCashierCounter), "m_IsMannedByPlayer"); private static readonly FieldInfo FiMannedByNpc = AccessTools.Field(typeof(InteractableCashierCounter), "m_IsMannedByNPC"); private static readonly FieldInfo FiIsUsingCard = AccessTools.Field(typeof(InteractableCashierCounter), "m_IsUsingCard"); private static readonly FieldInfo FiTotalScanned = AccessTools.Field(typeof(InteractableCashierCounter), "m_TotalScannedItemCost"); private static readonly MethodInfo MiNpcChange = AccessTools.Method(typeof(InteractableCashierCounter), "NPCEvaluateMoneyChange", (Type[])null, (Type[])null); private static readonly MethodInfo MiCreditCard = AccessTools.Method(typeof(InteractableCashierCounter), "EvaluateCreditCard", (Type[])null, (Type[])null); private static readonly MethodInfo MiCheckChangeReady = AccessTools.Method(typeof(InteractableCashierCounter), "CheckChangeReady", (Type[])null, (Type[])null); private static readonly MethodInfo MiSpaceBar = AccessTools.Method(typeof(InteractableCashierCounter), "OnPressSpaceBar", (Type[])null, (Type[])null); private static readonly FieldInfo FiIsChangeReady = AccessTools.Field(typeof(InteractableCashierCounter), "m_IsChangeReady"); private static readonly FieldInfo FiScannedCount = AccessTools.Field(typeof(Customer), "m_ItemScannedCount"); private static ShelfManager _sm; private static ShelfManager Shelf() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } public static int FindNearestCounter(Vector3 playerPos, float maxDist = 3.5f, bool quiet = false) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) ShelfManager val = Shelf(); if ((Object)(object)val == (Object)null) { if (!quiet) { CoopPlugin.Log.LogInfo((object)"serve: no ShelfManager"); } return -1; } int num = -1; float num2 = maxDist * maxDist; for (int i = 0; i < val.m_CashierCounterList.Count; i++) { InteractableCashierCounter val2 = val.m_CashierCounterList[i]; if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = ((Component)val2).transform.position - playerPos; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; num = i; } } } if (!quiet) { CoopPlugin.Log.LogInfo((object)$"serve: {val.m_CashierCounterList.Count} counters, nearest={num} ({Mathf.Sqrt(num2):F1}m)"); } return num; } public static string Serve(int counterIndex, string serverName, out byte[] scanEcho) { scanEcho = null; string text = ServeInner(counterIndex, serverName, ref scanEcho); CoopPlugin.Log.LogDebug((object)$"serve: {serverName} @ counter {counterIndex} -> {text}"); return text; } private static string ServeInner(int counterIndex, string serverName, ref byte[] scanEcho) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Expected I4, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected I4, but got Unknown //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) ShelfManager val = Shelf(); if ((Object)(object)val == (Object)null || counterIndex < 0 || counterIndex >= val.m_CashierCounterList.Count) { return "no register here"; } InteractableCashierCounter val2 = val.m_CashierCounterList[counterIndex]; if ((Object)(object)val2 == (Object)null) { return "no register here"; } CoopPlugin.Log.LogDebug((object)string.Format("serve: counter {0} state={1} customer={2} byPlayer={3} byNPC={4}", counterIndex, val2.m_CashierCounterState, ((Object)(object)val2.m_CurrentCustomer != (Object)null) ? ((Object)val2.m_CurrentCustomer).name : "none", FiMannedByPlayer?.GetValue(val2), FiMannedByNpc?.GetValue(val2))); object obj = FiMannedByPlayer?.GetValue(val2); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return "the host is already at this register"; } obj = FiMannedByNpc?.GetValue(val2); bool flag2 = default(bool); int num2; if (obj is bool) { flag2 = (bool)obj; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { return "a worker is already on this register"; } Customer currentCustomer = val2.m_CurrentCustomer; if ((Object)(object)currentCustomer == (Object)null || !currentCustomer.m_IsActive) { return "no customer at the counter"; } ECashierCounterState cashierCounterState = val2.m_CashierCounterState; switch (cashierCounterState - 1) { case 0: { double num7 = ((FiTotalScanned?.GetValue(val2) is double num8) ? num8 : 0.0); bool flag5 = false; EItemType val3 = (EItemType)0; CardData val4 = null; List itemInBagList = currentCustomer.GetItemInBagList(); for (int i = 0; i < itemInBagList.Count; i++) { if (flag5) { break; } InteractableScanItem val5 = (((Object)(object)itemInBagList[i] != (Object)null) ? itemInBagList[i].m_InteractableScanItem : null); if ((Object)(object)val5 != (Object)null && val5.IsNotScanned()) { val3 = itemInBagList[i].GetItemType(); ((InteractableObject)val5).OnMouseButtonUp(); flag5 = true; } } if (!flag5) { List cardInBagList = currentCustomer.GetCardInBagList(); for (int j = 0; j < cardInBagList.Count; j++) { if (flag5) { break; } if ((Object)(object)cardInBagList[j] != (Object)null && cardInBagList[j].IsNotScanned()) { try { val4 = cardInBagList[j].m_Card3dUI.m_CardUI.GetCardData(); } catch { } ((InteractableObject)cardInBagList[j]).OnMouseButtonUp(); flag5 = true; } } } if (!flag5) { return "nothing left to scan - wait a moment"; } double num9 = ((FiTotalScanned?.GetValue(val2) is double num10) ? num10 : num7); using (MemoryStream memoryStream = new MemoryStream()) { using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write((byte)counterIndex); binaryWriter.Write(val4 != null); binaryWriter.Write(num9 - num7); binaryWriter.Write(num9); if (val4 != null) { Msg.WriteCard(binaryWriter, val4); } else { binaryWriter.Write((int)val3); } binaryWriter.Flush(); scanEcho = memoryStream.ToArray(); } int num11 = currentCustomer.GetItemInBagList().Count + currentCustomer.GetCardInBagList().Count; int num12 = ((FiScannedCount?.GetValue(currentCustomer) is int num13) ? num13 : 0); if (num12 < num11) { return $"scanned {num12}/{num11}"; } return $"all {num11} scanned - press again to take payment"; } case 1: if ((Object)(object)currentCustomer.m_CustomerCash != (Object)null && ((Component)currentCustomer.m_CustomerCash).gameObject.activeSelf) { ((InteractableObject)currentCustomer.m_CustomerCash).OnMouseButtonUp(); return "payment taken - press again to give change"; } return "waiting for the customer to pay..."; case 2: { obj = FiIsUsingCard?.GetValue(val2); bool flag3 = default(bool); int num3; if (obj is bool) { flag3 = (bool)obj; num3 = 1; } else { num3 = 0; } if (((uint)num3 & (flag3 ? 1u : 0u)) != 0) { double num4 = ((FiTotalScanned?.GetValue(val2) is double num5) ? num5 : 0.0); MiCreditCard?.Invoke(val2, new object[1] { num4 }); CoopPlugin.Log.LogDebug((object)$"{serverName} completed a card sale at register {counterIndex}"); return "sale complete!"; } obj = FiIsChangeReady?.GetValue(val2); bool flag4 = default(bool); int num6; if (obj is bool) { flag4 = (bool)obj; num6 = 1; } else { num6 = 0; } if (((uint)num6 & (flag4 ? 1u : 0u)) == 0) { MiNpcChange?.Invoke(val2, null); MiCheckChangeReady?.Invoke(val2, null); return "change counted - click again to hand it over"; } MiSpaceBar?.Invoke(val2, null); CoopPlugin.Log.LogDebug((object)$"{serverName} completed a cash sale at register {counterIndex}"); return "sale complete!"; } default: return "no customer ready at this register"; } } public static void ApplyScanEcho(InteractableCashierCounter counter, bool isCard, double price, double hostTotal, EItemType itemType, CardData card) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) FiTotalScanned?.SetValue(counter, hostTotal - price); if (isCard) { counter.AddScannedCardCostTotal(price, card); } else { counter.AddScannedItemCostTotal(price, itemType); } } public static void ClientResetTotals() { ShelfManager val = Shelf(); if ((Object)(object)val == (Object)null) { return; } for (int i = 0; i < val.m_CashierCounterList.Count; i++) { if ((Object)(object)val.m_CashierCounterList[i] != (Object)null) { FiTotalScanned?.SetValue(val.m_CashierCounterList[i], 0.0); } } } public static byte[] CollectStates() { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected I4, but got Unknown //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) ShelfManager val = Shelf(); if ((Object)(object)val == (Object)null) { return null; } using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); int num = 0; binaryWriter.Write((byte)0); for (int i = 0; i < val.m_CashierCounterList.Count && i < 250; i++) { InteractableCashierCounter val2 = val.m_CashierCounterList[i]; if ((Object)(object)val2 == (Object)null) { continue; } Customer currentCustomer = val2.m_CurrentCustomer; if ((Object)(object)currentCustomer == (Object)null || !currentCustomer.m_IsActive) { continue; } List itemInBagList = currentCustomer.GetItemInBagList(); List cardInBagList = currentCustomer.GetCardInBagList(); int num2 = itemInBagList.Count + cardInBagList.Count; int num3 = ((FiScannedCount?.GetValue(currentCustomer) is int num4) ? num4 : 0); binaryWriter.Write((byte)i); binaryWriter.Write((byte)val2.m_CashierCounterState); binaryWriter.Write((byte)Mathf.Clamp(num3, 0, 255)); binaryWriter.Write((byte)Mathf.Clamp(num2, 0, 255)); int num5 = Mathf.Min(itemInBagList.Count, 12); int num6 = 0; List list = new List(num5); List list2 = new List(num5); Transform transform = ((Component)val2).transform; for (int j = 0; j < itemInBagList.Count; j++) { if (num6 >= num5) { break; } InteractableScanItem val3 = (((Object)(object)itemInBagList[j] != (Object)null) ? itemInBagList[j].m_InteractableScanItem : null); if ((Object)(object)val3 != (Object)null && val3.IsNotScanned()) { list.Add((int)itemInBagList[j].GetItemType()); list2.Add(transform.InverseTransformPoint(((Component)itemInBagList[j]).transform.position)); num6++; } } binaryWriter.Write((byte)list.Count); for (int k = 0; k < list.Count; k++) { binaryWriter.Write(list[k]); binaryWriter.Write(list2[k].x); binaryWriter.Write(list2[k].y); binaryWriter.Write(list2[k].z); } num++; } if (num == 0) { return null; } binaryWriter.Flush(); memoryStream.Position = 0L; memoryStream.WriteByte((byte)num); return memoryStream.ToArray(); } public static List ReadStates(BinaryReader br) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) int num = br.ReadByte(); List list = new List(num); for (int i = 0; i < num; i++) { CounterInfo item = new CounterInfo { Index = br.ReadByte(), State = br.ReadByte(), Scanned = br.ReadByte(), Total = br.ReadByte() }; int num2 = br.ReadByte(); item.ItemTypes = new List(num2); item.ItemLocal = new List(num2); for (int j = 0; j < num2; j++) { item.ItemTypes.Add(br.ReadInt32()); item.ItemLocal.Add(new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle())); } list.Add(item); } return list; } } public class RegisterMirror { private readonly Dictionary _states = new Dictionary(); private readonly Dictionary> _props = new Dictionary>(); private readonly Dictionary> _propTypes = new Dictionary>(); private readonly Dictionary _propColliders = new Dictionary(); private ShelfManager _sm; private float _staleTimer; public bool TryGetPropCounter(Collider c, out int counterIdx) { return _propColliders.TryGetValue(c, out counterIdx); } public bool IsPaymentPhase(int counterIdx) { if (_states.TryGetValue(counterIdx, out var value)) { if (value.State != 2) { return value.State == 3; } return true; } return false; } public void Reset() { foreach (List value in _props.Values) { foreach (Item item in value) { if ((Object)(object)item != (Object)null) { ItemSpawnManager.DisableItem(item); } } } _props.Clear(); _propTypes.Clear(); _propColliders.Clear(); _states.Clear(); _sm = null; _staleTimer = 0f; } public void Apply(List infos) { _states.Clear(); foreach (RegisterServe.CounterInfo info in infos) { _states[info.Index] = info; } _staleTimer = 0f; RefreshProps(); } public void Tick(float dt) { _staleTimer += dt; if (_staleTimer > 3f && _states.Count > 0) { _states.Clear(); RefreshProps(); } } public string PromptFor(int nearestCounter) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected I4, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (nearestCounter < 0 || !_states.TryGetValue(nearestCounter, out var value)) { return null; } ECashierCounterState val = (ECashierCounterState)value.State; return (val - 1) switch { 0 => $"press {CoopPlugin.ServeKey.Value} - scan items ({value.Scanned}/{value.Total})", 1 => $"press {CoopPlugin.ServeKey.Value} - take payment", 2 => $"press {CoopPlugin.ServeKey.Value} - give change", _ => null, }; } private void ReleaseProp(Item item) { if (!((Object)(object)item == (Object)null)) { if ((Object)(object)item.m_Collider != (Object)null) { _propColliders.Remove((Collider)(object)item.m_Collider); } ItemSpawnManager.DisableItem(item); } } private void ReleaseProps(int idx) { foreach (Item item in _props[idx]) { ReleaseProp(item); } } private static bool SameTypes(List a, List b) { if (a.Count != b.Count) { return false; } for (int i = 0; i < a.Count; i++) { if (a[i] != b[i]) { return false; } } return true; } private Item SpawnProp(Transform t, int idx, int type, Vector3 local) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) try { ItemMeshData itemMeshData = InventoryBase.GetItemMeshData((EItemType)type); if (itemMeshData == null) { return null; } Item item = ItemSpawnManager.GetItem(t); item.SetMesh(itemMeshData.mesh, itemMeshData.material, (EItemType)type, itemMeshData.meshSecondary, itemMeshData.materialSecondary, itemMeshData.materialList); ((Component)item).transform.position = t.TransformPoint(local); ((Component)item).transform.rotation = t.rotation; ((Component)item).gameObject.SetActive(true); if ((Object)(object)item.m_Rigidbody != (Object)null) { item.m_Rigidbody.isKinematic = true; } if ((Object)(object)item.m_Collider != (Object)null) { ((Collider)item.m_Collider).enabled = true; _propColliders[(Collider)(object)item.m_Collider] = idx; } return item; } catch { return null; } } private void RefreshProps() { //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } if ((Object)(object)_sm == (Object)null) { return; } List list = null; foreach (KeyValuePair> prop in _props) { if (!_states.ContainsKey(prop.Key)) { List obj = list ?? new List(); list = obj; obj.Add(prop.Key); } } if (list != null) { foreach (int item in list) { ReleaseProps(item); _props.Remove(item); _propTypes.Remove(item); } } foreach (KeyValuePair state in _states) { int key = state.Key; RegisterServe.CounterInfo value = state.Value; if (key >= _sm.m_CashierCounterList.Count) { continue; } InteractableCashierCounter val = _sm.m_CashierCounterList[key]; if ((Object)(object)val == (Object)null) { continue; } List value2; bool flag = _propTypes.TryGetValue(key, out value2); if (flag && SameTypes(value2, value.ItemTypes)) { continue; } if (!_props.TryGetValue(key, out var value3)) { value3 = (_props[key] = new List()); } Transform transform = ((Component)val).transform; for (int i = 0; i < value.ItemTypes.Count; i++) { Vector3 val2 = (Vector3)((i < value.ItemLocal.Count) ? value.ItemLocal[i] : new Vector3(-0.45f + (float)(i % 4) * 0.3f, 1.02f, 0.05f + (float)(i / 4) * 0.3f)); if (flag && i < value2.Count && i < value3.Count && value2[i] == value.ItemTypes[i] && (Object)(object)value3[i] != (Object)null) { try { ((Component)value3[i]).transform.position = transform.TransformPoint(val2); ((Component)value3[i]).transform.rotation = transform.rotation; } catch { } } else if (i < value3.Count) { ReleaseProp(value3[i]); value3[i] = SpawnProp(transform, key, value.ItemTypes[i], val2); } else { value3.Add(SpawnProp(transform, key, value.ItemTypes[i], val2)); } } for (int num = value3.Count - 1; num >= value.ItemTypes.Count; num--) { ReleaseProp(value3[num]); value3.RemoveAt(num); } if (!flag) { value2 = (_propTypes[key] = new List(value.ItemTypes.Count)); } value2.Clear(); value2.AddRange(value.ItemTypes); } } } public class ReportSync { public static bool ApplyingRemote; public Action> BroadcastState; private const float Interval = 2f; private const float HealEvery = 15f; private const int ReviewTail = 15; private static bool s_openPending; private static GameReportDataCollect s_openSnapshot; private static bool s_reviewsDirty; private static GameReportDataCollect s_clientOpenReport; private static readonly FieldInfo FiIsLerping = AccessTools.Field(typeof(EndOfDayReportScreen), "m_IsLerpingNumber"); private static readonly FieldInfo FiPhoneMode = AccessTools.Field(typeof(InteractionPlayerController), "m_IsPhoneScreenMode"); private static readonly FieldInfo FiCashMode = AccessTools.Field(typeof(InteractionPlayerController), "m_IsCashCounterMode"); private float _timer; private int _lastHash; private float _heal; private int _reviewSeq; public void Reset() { _timer = -4.1f; _lastHash = 0; _heal = 0f; _reviewSeq = -1; s_openPending = false; s_reviewsDirty = false; } public void ForceResend() { _lastHash = 0; _heal = 15f; } public static void ApplyPatches(Harmony h) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_006a: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown Try(h, typeof(EndOfDayReportScreen), "OpenScreen", null, new HarmonyMethod(typeof(ReportSync), "ReportOpenedPostfix", (Type[])null)); Try(h, typeof(CustomerReviewManager), "AddCustomerReview", new HarmonyMethod(typeof(ReportSync), "ReviewAddPrefix", (Type[])null), new HarmonyMethod(typeof(ReportSync), "ReviewAddPostfix", (Type[])null)); Try(h, typeof(EndOfDayReportScreen), "OnPressGoNextButton", new HarmonyMethod(typeof(ReportSync), "NextButtonPrefix", (Type[])null)); Try(h, typeof(EndOfDayReportScreen), "OnPressGoNextDay", new HarmonyMethod(typeof(ReportSync), "NextDayBlockPrefix", (Type[])null)); } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed: " + type.Name + "." + method + ": " + ex.Message)); } } public static void ReportOpenedPostfix() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role != CoopRole.Host) { return; } try { if (!EndOfDayReportScreen.IsActive()) { return; } } catch { return; } s_openSnapshot = CPlayerData.m_GameReportDataCollect; s_openPending = true; } public static void ReviewAddPrefix(out int __state) { __state = CPlayerData.m_CustomerReviewCount; } public static void ReviewAddPostfix(int __state) { if (CoopCore.Role == CoopRole.Host && CPlayerData.m_CustomerReviewCount != __state) { s_reviewsDirty = true; } } public static bool NextButtonPrefix(EndOfDayReportScreen __instance) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role != CoopRole.Client) { return true; } bool flag = false; try { flag = FiIsLerping != null && (bool)FiIsLerping.GetValue(__instance); } catch { } if (flag) { return true; } try { CPlayerData.m_GameReportDataCollect = s_clientOpenReport; EndOfDayReportScreen.CloseScreen(); } catch { } return false; } public static bool NextDayBlockPrefix() { return CoopCore.Role != CoopRole.Client; } public void HostTick(float dt, bool inGame) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) if (!inGame || BroadcastState == null) { return; } if (s_openPending) { s_openPending = false; GameReportDataCollect snap = s_openSnapshot; try { BroadcastState(delegate(BinaryWriter bw) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) WriteState(bw, snap, openScreen: true); }); _heal = 0f; return; } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ReportSync open: " + ex.Message)); return; } } _timer += dt; if (_timer < 2f) { return; } _timer -= 2f; try { if (s_reviewsDirty) { s_reviewsDirty = false; _lastHash = 0; } int num = HashState(); _heal += 2f; if (num != _lastHash || !(_heal < 15f)) { _lastHash = num; _heal = 0f; GameReportDataCollect live = CPlayerData.m_GameReportDataCollect; BroadcastState(delegate(BinaryWriter bw) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) WriteState(bw, live, openScreen: false); }); } } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("ReportSync host: " + ex2.Message)); } } private static int HashState() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) GameReportDataCollect gameReportDataCollect = CPlayerData.m_GameReportDataCollect; return (((((((((((((((((((((((17 * 31 + gameReportDataCollect.customerVisited) * 31 + gameReportDataCollect.checkoutCount) * 31 + gameReportDataCollect.customerDisatisfied) * 31 + gameReportDataCollect.customerBoughtItem) * 31 + gameReportDataCollect.customerBoughtCard) * 31 + gameReportDataCollect.customerPlayed) * 31 + gameReportDataCollect.storeExpGained) * 31 + gameReportDataCollect.storeLevelGained) * 31 + gameReportDataCollect.itemAmountSold) * 31 + gameReportDataCollect.cardAmountSold) * 31 + (int)(gameReportDataCollect.totalPlayTableTime * 100f)) * 31 + (int)(gameReportDataCollect.totalItemEarning * 100f)) * 31 + (int)(gameReportDataCollect.totalCardEarning * 100f)) * 31 + (int)(gameReportDataCollect.totalPlayTableEarning * 100f)) * 31 + (int)(gameReportDataCollect.supplyCost * 100f)) * 31 + (int)(gameReportDataCollect.upgradeCost * 100f)) * 31 + (int)(gameReportDataCollect.employeeCost * 100f)) * 31 + (int)(gameReportDataCollect.rentCost * 100f)) * 31 + (int)(gameReportDataCollect.billCost * 100f)) * 31 + gameReportDataCollect.cardPackOpened) * 31 + gameReportDataCollect.smellyCustomerCleaned) * 31 + gameReportDataCollect.manualCheckoutCount) * 31 + gameReportDataCollect.gemMintCardObtained) * 31 + CPlayerData.m_CustomerReviewCount; } private static void WriteState(BinaryWriter bw, GameReportDataCollect r, bool openScreen) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Expected I4, but got Unknown //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Expected I4, but got Unknown bw.Write(openScreen ? ((byte)1) : ((byte)0)); bw.Write(r.customerVisited); bw.Write(r.checkoutCount); bw.Write(r.customerDisatisfied); bw.Write(r.customerBoughtItem); bw.Write(r.customerBoughtCard); bw.Write(r.customerPlayed); bw.Write(r.storeExpGained); bw.Write(r.storeLevelGained); bw.Write(r.itemAmountSold); bw.Write(r.cardAmountSold); bw.Write(r.totalPlayTableTime); bw.Write(r.totalItemEarning); bw.Write(r.totalCardEarning); bw.Write(r.totalPlayTableEarning); bw.Write(r.supplyCost); bw.Write(r.upgradeCost); bw.Write(r.employeeCost); bw.Write(r.rentCost); bw.Write(r.billCost); bw.Write(r.cardPackOpened); bw.Write(r.smellyCustomerCleaned); bw.Write(r.manualCheckoutCount); bw.Write(r.gemMintCardObtained); List customerReviewDataList = CPlayerData.m_CustomerReviewDataList; bw.Write(CPlayerData.m_CustomerReviewCount); bw.Write(CPlayerData.m_CustomerReviewScoreAverage); int num = Mathf.Min(customerReviewDataList?.Count ?? 0, 15); bw.Write((byte)num); for (int i = 0; i < num; i++) { CustomerReviewData val = customerReviewDataList[customerReviewDataList.Count - num + i]; bw.Write((int)val.customerReviewType); bw.Write((byte)Mathf.Clamp(val.starLevel, 0, 255)); bw.Write((byte)Mathf.Clamp(val.textSOGoodBadLevel, 0, 255)); bw.Write(val.textSOIndex); bw.Write(val.day); bw.Write((byte)Mathf.Clamp(val.hour, 0, 255)); bw.Write((byte)Mathf.Clamp(val.minute, 0, 255)); bw.Write((int)val.itemType); bw.Write(val.customerName ?? ""); } } public void ClientApplyState(BinaryReader br) { ApplyingRemote = true; try { ClientApplyInner(br); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ReportSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(BinaryReader br) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Expected O, but got Unknown //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) bool flag = br.ReadByte() != 0; GameReportDataCollect gameReportDataCollect = new GameReportDataCollect { customerVisited = br.ReadInt32(), checkoutCount = br.ReadInt32(), customerDisatisfied = br.ReadInt32(), customerBoughtItem = br.ReadInt32(), customerBoughtCard = br.ReadInt32(), customerPlayed = br.ReadInt32(), storeExpGained = br.ReadInt32(), storeLevelGained = br.ReadInt32(), itemAmountSold = br.ReadInt32(), cardAmountSold = br.ReadInt32(), totalPlayTableTime = br.ReadSingle(), totalItemEarning = br.ReadSingle(), totalCardEarning = br.ReadSingle(), totalPlayTableEarning = br.ReadSingle(), supplyCost = br.ReadSingle(), upgradeCost = br.ReadSingle(), employeeCost = br.ReadSingle(), rentCost = br.ReadSingle(), billCost = br.ReadSingle(), cardPackOpened = br.ReadInt32(), smellyCustomerCleaned = br.ReadInt32(), manualCheckoutCount = br.ReadInt32(), gemMintCardObtained = br.ReadInt32() }; CPlayerData.m_GameReportDataCollect = gameReportDataCollect; int num = br.ReadInt32(); float customerReviewScoreAverage = br.ReadSingle(); int num2 = br.ReadByte(); List customerReviewDataList = CPlayerData.m_CustomerReviewDataList; if (_reviewSeq < 0) { _reviewSeq = CPlayerData.m_CustomerReviewCount; } int num3 = num - num2 + 1; for (int i = 0; i < num2; i++) { CustomerReviewData val = new CustomerReviewData(); val.customerReviewType = (ECustomerReviewType)br.ReadInt32(); val.starLevel = br.ReadByte(); val.textSOGoodBadLevel = br.ReadByte(); val.textSOIndex = br.ReadInt32(); val.day = br.ReadInt32(); val.hour = br.ReadByte(); val.minute = br.ReadByte(); val.itemType = (EItemType)br.ReadInt32(); val.customerName = br.ReadString(); if (num3 + i > _reviewSeq) { customerReviewDataList?.Add(val); } } if (num > _reviewSeq) { _reviewSeq = num; } CPlayerData.m_CustomerReviewCount = num; CPlayerData.m_CustomerReviewScoreAverage = customerReviewScoreAverage; if (customerReviewDataList != null) { while (customerReviewDataList.Count > 50) { customerReviewDataList.RemoveAt(0); } } if (flag) { s_clientOpenReport = gameReportDataCollect; TryOpenReportScreen(); } } private static void TryOpenReportScreen() { try { if ((Object)(object)CSingleton.Instance == (Object)null || EndOfDayReportScreen.IsActive()) { return; } InteractionPlayerController instance = CSingleton.Instance; if ((Object)(object)instance != (Object)null) { try { if (FiPhoneMode != null && (bool)FiPhoneMode.GetValue(instance)) { return; } } catch { } try { if (FiCashMode != null && (bool)FiCashMode.GetValue(instance)) { instance.OnExitCashCounterMode(); } } catch { } } EndOfDayReportScreen.OpenScreen(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ReportSync open screen: " + ex.Message)); } } } public static class SaveTransfer { public static int CoopSlot => CoopPlugin.ClientWorldSlot?.Value ?? 7; public static string SlotPath(int slot) { return Application.persistentDataPath + "/savedGames_Release" + slot + ".json"; } public static byte[] BuildHostPayload() { CGameManager instance = CSingleton.Instance; int currentSaveLoadSlotSelectedIndex = instance.m_CurrentSaveLoadSlotSelectedIndex; instance.SaveGameData(currentSaveLoadSlotSelectedIndex); string text = SlotPath(currentSaveLoadSlotSelectedIndex); if (!File.Exists(text)) { throw new FileNotFoundException("Host save file missing after save", text); } return File.ReadAllBytes(text); } public static void ApplyAndLoad(byte[] saveBytes) { string path = SlotPath(CoopSlot); string path2 = Application.persistentDataPath + "/savedGames_Release" + CoopSlot + ".gd"; File.WriteAllBytes(path, saveBytes); if (File.Exists(path2)) { File.Delete(path2); } CSingleton.Instance.m_ForceNoCloudSaveLoad = true; CoopPlugin.Log.LogInfo((object)$"Coop save received ({saveBytes.Length / 1024} KB), loading world..."); ForceLoadSlot(CoopSlot); } public static void ForceLoadSlot(int slot) { CGameManager instance = CSingleton.Instance; instance.m_CurrentSaveLoadSlotSelectedIndex = slot; typeof(CGameManager).GetField("m_InitLoaded", BindingFlags.Static | BindingFlags.NonPublic)?.SetValue(null, false); instance.LoadMainLevelAsync("Start", slot); } } public class SettingsSync { private const byte OpBuyDeco = 1; private const byte OpEquipDeco = 2; private const byte OpGameEvent = 3; private const byte OpGameEventFee = 4; private const byte OpCashier = 5; private const byte OpTableNumber = 6; public static SettingsSync Instance; public static bool ApplyingRemote; public Action> SendOp; public Action> BroadcastState; private float _timer; private int _lastHash; private float _heal; private readonly MemoryStream _stateMs = new MemoryStream(1024); private BinaryWriter _stateBw; private static readonly FieldInfo FiBuyCategory = AccessTools.Field(typeof(ShopBuyDecoUIScreen), "m_CategoryIndex"); public SettingsSync() { Instance = this; _stateBw = new BinaryWriter(_stateMs); } public void Reset() { _timer = -2.6f; _lastHash = 0; _heal = 0f; ApplyingRemote = false; } public void ForceResend() { _lastHash = 0; _heal = 15f; } public void HostTick(float dt, bool inGame) { if (!inGame) { return; } _timer += dt; if (_timer < 1.5f) { return; } _timer -= 1.5f; try { _stateMs.SetLength(0L); _stateMs.Position = 0L; WriteState(_stateBw); _stateBw.Flush(); int len = (int)_stateMs.Length; byte[] buf = _stateMs.GetBuffer(); int num = 17; for (int i = 0; i < len; i++) { num = num * 31 + buf[i]; } _heal += 1.5f; if (num != _lastHash || !(_heal < 15f)) { _lastHash = num; _heal = 0f; BroadcastState?.Invoke(delegate(BinaryWriter bw) { bw.Write(buf, 0, len); }); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("SettingsSync host: " + ex.Message)); } } public void HostApplyOp(BinaryReader br) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) byte b = br.ReadByte(); try { switch (b) { case 1: { int category = br.ReadByte(); int index = br.ReadInt32(); HostBuyDeco(category, index); break; } case 2: { int wall = br.ReadInt32(); int wallB = br.ReadInt32(); int floor = br.ReadInt32(); int floorB = br.ReadInt32(); int ceiling = br.ReadInt32(); int ceilingB = br.ReadInt32(); ApplyingRemote = true; try { ApplyEquips(wall, wallB, floor, floorB, ceiling, ceilingB); break; } finally { ApplyingRemote = false; } } case 3: { int num4 = br.ReadInt32(); int num5 = br.ReadInt32(); CPlayerData.m_PendingGameEventFormat = (EGameEventFormat)num4; CPlayerData.m_PendingGameEventExpansionType = (ECardExpansionType)num5; break; } case 4: { int num6 = br.ReadInt32(); float num7 = br.ReadSingle(); if (num6 >= 0 && num6 < CPlayerData.m_SetGameEventPriceList.Count) { ApplyingRemote = true; try { PriceChangeManager.SetGameEventPrice((EGameEventFormat)num6, Mathf.Max(0f, num7)); break; } finally { ApplyingRemote = false; } } break; } case 5: { int num3 = br.ReadByte(); byte b2 = br.ReadByte(); List list2 = CSingleton.Instance?.m_CashierCounterList; if (list2 == null || num3 >= list2.Count || !((Object)(object)list2[num3] != (Object)null)) { break; } ApplyingRemote = true; try { bool flag = (b2 & 1) != 0; bool flag2 = (b2 & 2) != 0; if (list2[num3].CanCheckout() != flag) { list2[num3].SetCanCheckout(flag); } if (list2[num3].CanTradeCard() != flag2) { list2[num3].SetCanTradeCard(flag2); } break; } finally { ApplyingRemote = false; } } case 6: { int num = br.ReadByte(); int num2 = br.ReadInt32(); List list = CSingleton.Instance?.m_PlayTableList; if (list == null || num >= list.Count || !((Object)(object)list[num] != (Object)null)) { break; } ApplyingRemote = true; try { if (list[num].GetTournamentPlayTableNumber() != num2) { list[num].SetTournamentPlayTableNumber(Mathf.Max(0, num2)); } break; } finally { ApplyingRemote = false; } } default: CoopPlugin.Log.LogWarning((object)("SettingsSync: unknown sub-op " + b)); break; } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)($"SettingsSync op {b}: " + ex.Message)); } } private void HostBuyDeco(int category, int index) { //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown ShelfData_ScriptableObject val = CSingleton.Instance?.m_ObjectData_SO; if ((Object)(object)val == (Object)null) { return; } List list; switch (category) { default: return; case 2: list = val.m_CeilingDecoDataList; break; case 1: list = val.m_FloorDecoDataList; break; case 0: list = val.m_WallDecoDataList; break; } List list2 = list; if (index < 0 || index >= list2.Count || list2[index] == null || category switch { 1 => CPlayerData.IsDecoFloorUnlocked(index), 0 => CPlayerData.IsDecoWallUnlocked(index), _ => CPlayerData.IsDecoCeilingUnlocked(index), }) { return; } float price = list2[index].price; if (CPlayerData.m_CoinAmountDouble < (double)price) { CoopPlugin.Log.LogInfo((object)$"deco buy refused (funds): cat {category} idx {index}"); return; } CPlayerData.m_GameReportDataCollect.upgradeCost -= price; CPlayerData.m_GameReportDataCollectPermanent.upgradeCost -= price; PriceChangeManager.AddTransaction(0f - price, (ETransactionType)10, category, index, (CardData)null); CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(price, false)); switch (category) { case 0: CPlayerData.SetUnlockDecoWall(index, true); break; case 1: CPlayerData.SetUnlockDecoFloor(index, true); break; default: CPlayerData.SetUnlockDecoCeiling(index, true); break; } CoopPlugin.Log.LogInfo((object)$"partner bought deco: cat {category} idx {index} for {price}"); } public void ClientApplyState(BinaryReader br) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) ApplyingRemote = true; try { ApplyBoolList(br, CPlayerData.m_UnlockedDecoWallList, (Action)CPlayerData.SetUnlockDecoWall); ApplyBoolList(br, CPlayerData.m_UnlockedDecoFloorList, (Action)CPlayerData.SetUnlockDecoFloor); ApplyBoolList(br, CPlayerData.m_UnlockedDecoCeilingList, (Action)CPlayerData.SetUnlockDecoCeiling); int wall = br.ReadInt32(); int wallB = br.ReadInt32(); int floor = br.ReadInt32(); int floorB = br.ReadInt32(); int ceiling = br.ReadInt32(); int ceilingB = br.ReadInt32(); ApplyEquips(wall, wallB, floor, floorB, ceiling, ceilingB); CPlayerData.m_GameEventFormat = (EGameEventFormat)br.ReadInt32(); CPlayerData.m_PendingGameEventFormat = (EGameEventFormat)br.ReadInt32(); CPlayerData.m_GameEventExpansionType = (ECardExpansionType)br.ReadInt32(); CPlayerData.m_PendingGameEventExpansionType = (ECardExpansionType)br.ReadInt32(); int num = br.ReadByte(); for (int i = 0; i < num; i++) { float value = br.ReadSingle(); if (i < CPlayerData.m_SetGameEventPriceList.Count) { CPlayerData.m_SetGameEventPriceList[i] = value; } } List list = CSingleton.Instance?.m_CashierCounterList; int num2 = br.ReadByte(); for (int j = 0; j < num2; j++) { byte b = br.ReadByte(); if (list != null && j < list.Count && !((Object)(object)list[j] == (Object)null)) { bool flag = (b & 1) != 0; bool flag2 = (b & 2) != 0; if (list[j].CanCheckout() != flag) { list[j].SetCanCheckout(flag); } if (list[j].CanTradeCard() != flag2) { list[j].SetCanTradeCard(flag2); } } } List list2 = CSingleton.Instance?.m_PlayTableList; int num3 = br.ReadByte(); for (int k = 0; k < num3; k++) { int num4 = br.ReadByte(); if (list2 != null && k < list2.Count && !((Object)(object)list2[k] == (Object)null) && list2[k].GetTournamentPlayTableNumber() != num4) { list2[k].SetTournamentPlayTableNumber(num4); } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("SettingsSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private static void ApplyBoolList(BinaryReader br, List local, Action setter) { int num = br.ReadByte(); for (int i = 0; i < num; i++) { bool flag = br.ReadBoolean(); if (local != null && i < local.Count && local[i] != flag) { setter(i, flag); } } } private static void ApplyEquips(int wall, int wallB, int floor, int floorB, int ceiling, int ceilingB) { ShelfData_ScriptableObject val = CSingleton.Instance?.m_ObjectData_SO; if ((Object)(object)val == (Object)null) { return; } if (wall != CPlayerData.m_EquippedWallDecoIndex && wall >= 0 && wall < val.m_WallDecoDataList.Count) { CPlayerData.m_EquippedWallDecoIndex = wall; try { ShopCustomizationManager.ChangeWallMaterial(wall, false); } catch { } } if (wallB != CPlayerData.m_EquippedWallDecoIndexB && wallB >= 0 && wallB < val.m_WallDecoDataList.Count) { CPlayerData.m_EquippedWallDecoIndexB = wallB; try { ShopCustomizationManager.ChangeWallMaterial(wallB, true); } catch { } } if (floor != CPlayerData.m_EquippedFloorDecoIndex && floor >= 0 && floor < val.m_FloorDecoDataList.Count) { CPlayerData.m_EquippedFloorDecoIndex = floor; try { ShopCustomizationManager.ChangeFloorMaterial(floor, false); } catch { } } if (floorB != CPlayerData.m_EquippedFloorDecoIndexB && floorB >= 0 && floorB < val.m_FloorDecoDataList.Count) { CPlayerData.m_EquippedFloorDecoIndexB = floorB; try { ShopCustomizationManager.ChangeFloorMaterial(floorB, true); } catch { } } if (ceiling != CPlayerData.m_EquippedCeilingDecoIndex && ceiling >= 0 && ceiling < val.m_CeilingDecoDataList.Count) { CPlayerData.m_EquippedCeilingDecoIndex = ceiling; try { ShopCustomizationManager.ChangeCeilingMaterial(ceiling, false); } catch { } } if (ceilingB == CPlayerData.m_EquippedCeilingDecoIndexB || ceilingB < 0 || ceilingB >= val.m_CeilingDecoDataList.Count) { return; } CPlayerData.m_EquippedCeilingDecoIndexB = ceilingB; try { ShopCustomizationManager.ChangeCeilingMaterial(ceilingB, true); } catch { } } private static void WriteState(BinaryWriter bw) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected I4, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected I4, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected I4, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected I4, but got Unknown WriteBoolList(bw, CPlayerData.m_UnlockedDecoWallList); WriteBoolList(bw, CPlayerData.m_UnlockedDecoFloorList); WriteBoolList(bw, CPlayerData.m_UnlockedDecoCeilingList); bw.Write(CPlayerData.m_EquippedWallDecoIndex); bw.Write(CPlayerData.m_EquippedWallDecoIndexB); bw.Write(CPlayerData.m_EquippedFloorDecoIndex); bw.Write(CPlayerData.m_EquippedFloorDecoIndexB); bw.Write(CPlayerData.m_EquippedCeilingDecoIndex); bw.Write(CPlayerData.m_EquippedCeilingDecoIndexB); bw.Write((int)CPlayerData.m_GameEventFormat); bw.Write((int)CPlayerData.m_PendingGameEventFormat); bw.Write((int)CPlayerData.m_GameEventExpansionType); bw.Write((int)CPlayerData.m_PendingGameEventExpansionType); List setGameEventPriceList = CPlayerData.m_SetGameEventPriceList; int num = Mathf.Min(setGameEventPriceList.Count, 255); bw.Write((byte)num); for (int i = 0; i < num; i++) { bw.Write(setGameEventPriceList[i]); } List list = CSingleton.Instance?.m_CashierCounterList; int num2 = ((list != null) ? Mathf.Min(list.Count, 255) : 0); bw.Write((byte)num2); for (int j = 0; j < num2; j++) { byte value = 3; if ((Object)(object)list[j] != (Object)null) { value = (byte)((list[j].CanCheckout() ? 1u : 0u) | (uint)(list[j].CanTradeCard() ? 2 : 0)); } bw.Write(value); } List list2 = CSingleton.Instance?.m_PlayTableList; int num3 = ((list2 != null) ? Mathf.Min(list2.Count, 255) : 0); bw.Write((byte)num3); for (int k = 0; k < num3; k++) { int num4 = (((Object)(object)list2[k] != (Object)null) ? list2[k].GetTournamentPlayTableNumber() : 0); bw.Write((byte)Mathf.Clamp(num4, 0, 255)); } } private static void WriteBoolList(BinaryWriter bw, List list) { int num = ((list != null) ? Mathf.Min(list.Count, 255) : 0); bw.Write((byte)num); for (int i = 0; i < num; i++) { bw.Write(list[i]); } } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown Try(h, typeof(ShopBuyDecoUIScreen), "OnPressBuyShopDeco", new HarmonyMethod(typeof(SettingsSync), "BuyDecoPrefix", (Type[])null)); Try(h, typeof(PlaceDecoUIScreen), "OnPressSwitchShopDeco", null, new HarmonyMethod(typeof(SettingsSync), "EquipDecoPostfix", (Type[])null)); Try(h, typeof(SetGameEventFormatScreen), "OnPressConfirmBtn", null, new HarmonyMethod(typeof(SettingsSync), "GameEventPostfix", (Type[])null)); Try(h, typeof(SetGameEventScreen), "OnPressReset", null, new HarmonyMethod(typeof(SettingsSync), "GameEventPostfix", (Type[])null)); Try(h, typeof(PriceChangeManager), "SetGameEventPrice", null, new HarmonyMethod(typeof(SettingsSync), "GameEventFeePostfix", (Type[])null)); Try(h, typeof(InteractableCashierCounter), "SetCanCheckout", null, new HarmonyMethod(typeof(SettingsSync), "CashierPostfix", (Type[])null)); Try(h, typeof(InteractableCashierCounter), "SetCanTradeCard", null, new HarmonyMethod(typeof(SettingsSync), "CashierPostfix", (Type[])null)); Try(h, typeof(InteractablePlayTable), "SetTournamentPlayTableNumber", null, new HarmonyMethod(typeof(SettingsSync), "TableNumberPostfix", (Type[])null)); } public static bool BuyDecoPrefix(ShopBuyDecoUIScreen __instance, int shopDecoIndex, float price) { if (CoopCore.Role != CoopRole.Client) { return true; } int num = -1; try { num = (int)FiBuyCategory.GetValue(__instance); } catch { } if (num < 0 || num > 2) { return true; } if (CPlayerData.m_CoinAmountDouble < (double)price) { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)0); return false; } SettingsSync instance = Instance; if (instance != null && instance.SendOp != null) { int c = num; instance.SendOp(delegate(BinaryWriter bw) { bw.Write((byte)1); bw.Write((byte)c); bw.Write(shopDecoIndex); }); } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "deco purchase sent to the host"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } public static void EquipDecoPostfix() { if (ApplyingRemote || CoopCore.Role != CoopRole.Client) { return; } SettingsSync instance = Instance; if (instance != null && instance.SendOp != null) { instance.SendOp(delegate(BinaryWriter bw) { bw.Write((byte)2); bw.Write(CPlayerData.m_EquippedWallDecoIndex); bw.Write(CPlayerData.m_EquippedWallDecoIndexB); bw.Write(CPlayerData.m_EquippedFloorDecoIndex); bw.Write(CPlayerData.m_EquippedFloorDecoIndexB); bw.Write(CPlayerData.m_EquippedCeilingDecoIndex); bw.Write(CPlayerData.m_EquippedCeilingDecoIndexB); }); } } public static void GameEventPostfix() { if (ApplyingRemote || CoopCore.Role != CoopRole.Client) { return; } SettingsSync instance = Instance; if (instance != null && instance.SendOp != null) { instance.SendOp(delegate(BinaryWriter bw) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown bw.Write((byte)3); bw.Write((int)CPlayerData.m_PendingGameEventFormat); bw.Write((int)CPlayerData.m_PendingGameEventExpansionType); }); } } public static void GameEventFeePostfix(EGameEventFormat gameEventFormat, 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 (ApplyingRemote || CoopCore.Role != CoopRole.Client) { return; } SettingsSync instance = Instance; if (instance != null && instance.SendOp != null) { instance.SendOp(delegate(BinaryWriter bw) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected I4, but got Unknown bw.Write((byte)4); bw.Write((int)gameEventFormat); bw.Write(price); }); } } public static void CashierPostfix(InteractableCashierCounter __instance) { if (ApplyingRemote || CoopCore.Role != CoopRole.Client) { return; } SettingsSync instance = Instance; if (instance == null || instance.SendOp == null) { return; } List list = CSingleton.Instance?.m_CashierCounterList; if (list == null) { return; } int idx = list.IndexOf(__instance); if (idx >= 0 && idx <= 254) { byte flags = (byte)((__instance.CanCheckout() ? 1u : 0u) | (uint)(__instance.CanTradeCard() ? 2 : 0)); instance.SendOp(delegate(BinaryWriter bw) { bw.Write((byte)5); bw.Write((byte)idx); bw.Write(flags); }); } } public static void TableNumberPostfix(InteractablePlayTable __instance, int tableNumber) { if (ApplyingRemote || CoopCore.Role != CoopRole.Client) { return; } SettingsSync instance = Instance; if (instance == null || instance.SendOp == null) { return; } List list = CSingleton.Instance?.m_PlayTableList; if (list == null) { return; } int idx = list.IndexOf(__instance); if (idx >= 0 && idx <= 254) { instance.SendOp(delegate(BinaryWriter bw) { bw.Write((byte)6); bw.Write((byte)idx); bw.Write(tableNumber); }); } } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("SettingsSync patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("SettingsSync patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } } public class ShopStateSync { private const byte OpPayBill = 1; private const byte OpUnlock = 2; private const byte OpToggleSign = 3; private static ShopStateSync _instance; public static bool ApplyingRemote; private static readonly MethodInfo MiBillEvaluateUI = AccessTools.Method(typeof(RentBillScreen), "EvaluateUI", (Type[])null, (Type[])null); private static readonly MethodInfo MiBillNotification = AccessTools.Method(typeof(RentBillScreen), "EvaluateBillNotification", (Type[])null, (Type[])null); private static readonly MethodInfo MiOpenSignMesh = AccessTools.Method(typeof(InteractableOpenCloseSign), "EvaluateSignOpenCloseMesh", (Type[])null, (Type[])null); private static readonly MethodInfo MiWarehouseSignMesh = AccessTools.Method(typeof(InteractableWarehouseAllowEnterSign), "EvaluateSignOpenCloseMesh", (Type[])null, (Type[])null); public Action> SendOp; public Action> BroadcastState; private float _timer; private int _lastHash; private float _heal; private RentBillScreen _billScreen; private InteractableOpenCloseSign _openSign; private InteractableWarehouseAllowEnterSign _warehouseSign; public ShopStateSync() { _instance = this; } public void Reset() { _timer = -1.9f; _lastHash = 0; _heal = 0f; _billScreen = null; _openSign = null; _warehouseSign = null; } public void ForceResend() { _lastHash = 0; _heal = 15f; } private RentBillScreen BillScreen() { if ((Object)(object)_billScreen == (Object)null) { _billScreen = Object.FindObjectOfType(true); } return _billScreen; } private InteractableOpenCloseSign OpenSign() { if ((Object)(object)_openSign == (Object)null) { _openSign = Object.FindObjectOfType(true); } return _openSign; } private InteractableWarehouseAllowEnterSign WarehouseSign() { if ((Object)(object)_warehouseSign == (Object)null) { _warehouseSign = Object.FindObjectOfType(true); } return _warehouseSign; } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown Try(h, typeof(RentBillScreen), "OnPressPayRentBill", new HarmonyMethod(typeof(ShopStateSync), "PayRentPrefix", (Type[])null)); Try(h, typeof(RentBillScreen), "OnPressPayElectricBill", new HarmonyMethod(typeof(ShopStateSync), "PayElectricPrefix", (Type[])null)); Try(h, typeof(RentBillScreen), "OnPressPaySalaryBill", new HarmonyMethod(typeof(ShopStateSync), "PaySalaryPrefix", (Type[])null)); Try(h, typeof(RentBillScreen), "OnPressPayAllBill", new HarmonyMethod(typeof(ShopStateSync), "PayAllPrefix", (Type[])null)); Try(h, typeof(RentBillScreen), "EvaluateNewDayBill", new HarmonyMethod(typeof(ShopStateSync), "BillAccrualPrefix", (Type[])null)); Try(h, typeof(ExpansionShopUIScreen), "EvaluateCartCheckout", new HarmonyMethod(typeof(ShopStateSync), "RoomCheckoutPrefix", (Type[])null)); Try(h, typeof(ExpansionShopUIScreen), "OnPressUnlockShopB", new HarmonyMethod(typeof(ShopStateSync), "UnlockShopBPrefix", (Type[])null)); Try(h, typeof(InteractableOpenCloseSign), "OnMouseButtonUp", new HarmonyMethod(typeof(ShopStateSync), "OpenSignPrefix", (Type[])null)); Try(h, typeof(InteractableWarehouseAllowEnterSign), "OnMouseButtonUp", new HarmonyMethod(typeof(ShopStateSync), "WarehouseSignPrefix", (Type[])null)); } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed: " + type.Name + "." + method + ": " + ex.Message)); } } private static bool PayBillPrefix(byte billType, bool forcePay) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } if (!forcePay) { _instance?.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)1); bw.Write(billType); }); } return false; } public static bool PayRentPrefix(bool forcePay) { return PayBillPrefix(1, forcePay); } public static bool PayElectricPrefix(bool forcePay) { return PayBillPrefix(2, forcePay); } public static bool PaySalaryPrefix(bool forcePay) { return PayBillPrefix(3, forcePay); } public static bool PayAllPrefix() { return PayBillPrefix(0, forcePay: false); } public static bool BillAccrualPrefix() { return CoopCore.Role != CoopRole.Client; } public static bool RoomCheckoutPrefix(bool isShopB) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } byte kind = (isShopB ? ((byte)1) : ((byte)0)); _instance?.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)2); bw.Write(kind); }); return false; } public static bool UnlockShopBPrefix() { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } if (CPlayerData.m_IsWarehouseRoomUnlocked) { return true; } _instance?.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)2); bw.Write((byte)2); }); return false; } public static bool OpenSignPrefix() { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } _instance?.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)3); bw.Write((byte)0); }); return false; } public static bool WarehouseSignPrefix() { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } _instance?.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)3); bw.Write((byte)1); }); return false; } public void HostTick(float dt, bool inGame) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (!inGame || BroadcastState == null) { return; } _timer += dt; if (_timer < 1f) { return; } _timer -= 1f; try { int num = 17; for (EBillType val = (EBillType)1; (int)val <= 3; val = (EBillType)(val + 1)) { BillData bill = CPlayerData.GetBill(val); num = num * 31 + bill.billDayPassed; num = num * 31 + (int)(bill.amountToPay * 100f); } num = num * 31 + CPlayerData.m_UnlockRoomCount; num = num * 31 + CPlayerData.m_UnlockWarehouseRoomCount; num = num * 31 + (int)((CPlayerData.m_IsWarehouseRoomUnlocked ? 1u : 0u) | (uint)(CPlayerData.m_IsShopOpen ? 2 : 0) | (uint)(CPlayerData.m_IsWarehouseDoorClosed ? 4 : 0)); _heal += 1f; if (num != _lastHash || !(_heal < 15f)) { _lastHash = num; _heal = 0f; BroadcastState(WriteState); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ShopStateSync host: " + ex.Message)); } } private static void WriteState(BinaryWriter bw) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) for (EBillType val = (EBillType)1; (int)val <= 3; val = (EBillType)(val + 1)) { BillData bill = CPlayerData.GetBill(val); bw.Write(bill.billDayPassed); bw.Write(bill.amountToPay); } bw.Write(CPlayerData.m_UnlockRoomCount); bw.Write(CPlayerData.m_UnlockWarehouseRoomCount); bw.Write(CPlayerData.m_IsWarehouseRoomUnlocked); bw.Write(CPlayerData.m_IsShopOpen); bw.Write(CPlayerData.m_IsWarehouseDoorClosed); } public void HostApplyOp(BinaryReader br) { if (CoopCore.Role != CoopRole.Host) { return; } byte b = br.ReadByte(); byte b2 = br.ReadByte(); try { switch (b) { case 1: HostPayBill(b2); break; case 2: HostUnlock(b2); break; case 3: HostToggleSign(b2); break; } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ShopStateSync op " + b + ": " + ex.Message)); } ForceResend(); } private void HostPayBill(byte billType) { RentBillScreen val = BillScreen(); if ((Object)(object)val == (Object)null) { CoopPlugin.Log.LogWarning((object)"ShopStateSync: RentBillScreen not found; pay op dropped"); return; } switch (billType) { case 0: val.OnPressPayAllBill(); break; case 1: val.OnPressPayRentBill(false); break; case 2: val.OnPressPayElectricBill(false); break; case 3: val.OnPressPaySalaryBill(false); break; } } private void HostUnlock(byte kind) { //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Expected O, but got Unknown //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown UnlockRoomManager instance = CSingleton.Instance; if ((Object)(object)instance == (Object)null || ((Object)(object)CSingleton.Instance != (Object)null && CSingleton.Instance.m_IsPrologue)) { return; } switch (kind) { case 2: { if (CPlayerData.m_IsWarehouseRoomUnlocked) { return; } float shopB_UnlockPrice = instance.m_ShopB_UnlockPrice; if (CPlayerData.m_ShopLevel + 1 < instance.m_ShopB_UnlockLevelRequired || CPlayerData.m_CoinAmountDouble < (double)shopB_UnlockPrice) { return; } PriceChangeManager.AddTransaction(0f - shopB_UnlockPrice, (ETransactionType)3, 1, -1, (CardData)null); CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(shopB_UnlockPrice, false)); instance.SetUnlockWarehouseRoom(true); AchievementManager.OnShopLotBUnlocked(); CEventManager.QueueEvent((CEvent)new CEventPlayer_AddShopExp(Mathf.Clamp(Mathf.RoundToInt(shopB_UnlockPrice / 100f), 5, 100), false)); CPlayerData.m_GameReportDataCollect.upgradeCost -= shopB_UnlockPrice; CPlayerData.m_GameReportDataCollectPermanent.upgradeCost -= shopB_UnlockPrice; SoundManager.PlayAudio("SFX_CustomerBuy", 0.6f, 1f); break; } case 1: { if (!CPlayerData.m_IsWarehouseRoomUnlocked) { return; } int unlockWarehouseRoomCount = CPlayerData.m_UnlockWarehouseRoomCount; if (unlockWarehouseRoomCount >= instance.m_LockedWarehouseRoomBlockerList.Count) { return; } float unlockWarehouseRoomCost = CPlayerData.GetUnlockWarehouseRoomCost(unlockWarehouseRoomCount); if (CPlayerData.m_CoinAmountDouble < (double)unlockWarehouseRoomCost) { return; } PriceChangeManager.AddTransaction(0f - unlockWarehouseRoomCost, (ETransactionType)3, 0, unlockWarehouseRoomCount, (CardData)null); CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(unlockWarehouseRoomCost, false)); instance.StartUnlockNextWarehouseRoom(); CEventManager.QueueEvent((CEvent)new CEventPlayer_AddShopExp(Mathf.Clamp(Mathf.RoundToInt(unlockWarehouseRoomCost / 100f), 5, 100), false)); CPlayerData.m_GameReportDataCollect.upgradeCost -= unlockWarehouseRoomCost; CPlayerData.m_GameReportDataCollectPermanent.upgradeCost -= unlockWarehouseRoomCost; SoundManager.PlayAudio("SFX_CustomerBuy", 0.6f, 1f); break; } default: { int unlockRoomCount = CPlayerData.m_UnlockRoomCount; if (unlockRoomCount >= instance.m_LockedRoomBlockerList.Count) { return; } float unlockShopRoomCost = CPlayerData.GetUnlockShopRoomCost(unlockRoomCount); if (CPlayerData.m_CoinAmountDouble < (double)unlockShopRoomCost) { return; } PriceChangeManager.AddTransaction(0f - unlockShopRoomCost, (ETransactionType)3, 1, unlockRoomCount, (CardData)null); CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(unlockShopRoomCost, false)); instance.StartUnlockNextRoom(); CEventManager.QueueEvent((CEvent)new CEventPlayer_AddShopExp(Mathf.Clamp(Mathf.RoundToInt(unlockShopRoomCost / 100f), 5, 100), false)); CPlayerData.m_GameReportDataCollect.upgradeCost -= unlockShopRoomCost; CPlayerData.m_GameReportDataCollectPermanent.upgradeCost -= unlockShopRoomCost; SoundManager.PlayAudio("SFX_CustomerBuy", 0.6f, 1f); break; } } try { CSingleton.Instance.SaveInteractableObjectData(false); } catch { } } private void HostToggleSign(byte which) { if (which == 0) { InteractableOpenCloseSign val = OpenSign(); if ((Object)(object)val != (Object)null) { ((InteractableObject)val).OnMouseButtonUp(); } } else { InteractableWarehouseAllowEnterSign val2 = WarehouseSign(); if ((Object)(object)val2 != (Object)null) { ((InteractableObject)val2).OnMouseButtonUp(); } } } public void ClientApplyState(BinaryReader br) { ApplyingRemote = true; try { ClientApplyInner(br); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ShopStateSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(BinaryReader br) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) bool flag = false; for (EBillType val = (EBillType)1; (int)val <= 3; val = (EBillType)(val + 1)) { int num = br.ReadInt32(); float num2 = br.ReadSingle(); BillData bill = CPlayerData.GetBill(val); if (bill.billDayPassed != num || bill.amountToPay != num2) { bill.billDayPassed = num; bill.amountToPay = num2; flag = true; } } int num3 = br.ReadInt32(); int num4 = br.ReadInt32(); bool flag2 = br.ReadBoolean(); bool flag3 = br.ReadBoolean(); bool flag4 = br.ReadBoolean(); if (flag && (Object)(object)BillScreen() != (Object)null) { try { MiBillEvaluateUI?.Invoke(_billScreen, null); } catch { } try { MiBillNotification?.Invoke(_billScreen, null); } catch { } } UnlockRoomManager instance = CSingleton.Instance; if ((Object)(object)instance != (Object)null) { if (flag2 && !CPlayerData.m_IsWarehouseRoomUnlocked) { instance.SetUnlockWarehouseRoom(true); } int num5 = 0; while (CPlayerData.m_UnlockRoomCount < num3 && num5 < 64) { instance.StartUnlockNextRoom(); num5++; } int num6 = 0; while (CPlayerData.m_UnlockWarehouseRoomCount < num4 && num6 < 64) { instance.StartUnlockNextWarehouseRoom(); num6++; } } if (CPlayerData.m_IsShopOpen != flag3) { CPlayerData.m_IsShopOpen = flag3; InteractableOpenCloseSign val2 = OpenSign(); if ((Object)(object)val2 != (Object)null) { try { MiOpenSignMesh?.Invoke(val2, null); } catch { } } } if (CPlayerData.m_IsWarehouseDoorClosed == flag4) { return; } CPlayerData.m_IsWarehouseDoorClosed = flag4; InteractableWarehouseAllowEnterSign val3 = WarehouseSign(); if ((Object)(object)val3 != (Object)null) { try { MiWarehouseSignMesh?.Invoke(val3, null); return; } catch { return; } } if ((Object)(object)instance != (Object)null) { instance.EvaluateWarehouseRoomOpenClose(); } } } public static class SidecarTransfer { public static byte[] BuildBundle(int hostSlot) { string persistentDataPath = Application.persistentDataPath; List list = new List(); Regex regex = new Regex($"(_|Release){hostSlot}(_|\\.|$)"); string[] directories = Directory.GetDirectories(persistentDataPath); foreach (string path in directories) { if (Path.GetFileName(path) == "Screenshots" || Path.GetFileName(path) == "Unity") { continue; } string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories); foreach (string text in files) { string fileName = Path.GetFileName(text); if (regex.IsMatch(fileName) || fileName == "enum_values.json") { list.Add(text); } } } using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(list.Count); foreach (string item in list) { string value = item.Substring(persistentDataPath.Length + 1).Replace('\\', '/'); byte[] array = File.ReadAllBytes(item); binaryWriter.Write(value); binaryWriter.Write(array.Length); binaryWriter.Write(array); } binaryWriter.Flush(); CoopPlugin.Log.LogInfo((object)$"Sidecar bundle: {list.Count} mod files, {memoryStream.Length / 1024} KB"); return memoryStream.ToArray(); } public static void ApplyBundle(byte[] bundle, int hostSlot, int clientSlot) { if (bundle == null || bundle.Length < 4) { return; } string persistentDataPath = Application.persistentDataPath; Regex regex = new Regex($"(?<=_|Release){hostSlot}(?=_|\\.|$)"); int num = 0; int num2 = 0; using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(bundle, writable: false))) { int num3 = binaryReader.ReadInt32(); for (int i = 0; i < num3; i++) { string path = binaryReader.ReadString(); int count = binaryReader.ReadInt32(); byte[] array = binaryReader.ReadBytes(count); string path2 = Path.GetDirectoryName(path) ?? ""; string fileName = Path.GetFileName(path); if (fileName == "enum_values.json") { string path3 = Path.Combine(persistentDataPath, path2, fileName); if (!File.Exists(path3)) { Directory.CreateDirectory(Path.GetDirectoryName(path3)); File.WriteAllBytes(path3, array); num++; } else if (!BytesEqual(File.ReadAllBytes(path3), array)) { num2++; CoopPlugin.Log.LogWarning((object)"enum_values.json differs from the host's. Modded item IDs may not line up. For perfect fidelity on a dedicated co-op PC, delete LocalLow/OPNeonGames/Card Shop Simulator/PrefabLoader/enum_values.json once (while not using solo modded saves) and rejoin."); } } else { string path4 = regex.Replace(fileName, clientSlot.ToString()); string text = Path.Combine(persistentDataPath, path2, path4); Directory.CreateDirectory(Path.GetDirectoryName(text)); if (File.Exists(text) && !File.Exists(text + ".coopbak")) { File.Copy(text, text + ".coopbak"); } File.WriteAllBytes(text, array); num++; } } } CoopPlugin.Log.LogInfo((object)$"Sidecar bundle applied: {num} files (slot {hostSlot} -> {clientSlot}), {num2} skipped"); } private static bool BytesEqual(byte[] a, byte[] b) { if (a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { return false; } } return true; } } public class StaffSync { private struct Entry { public bool Hired; public bool HasData; public byte PrimaryTask; public byte SecondaryTask; public byte WorkerTask; public byte BonusCount; public bool BonusBoosted; public bool FillNoLabel; public bool RoundUpPrice; public bool RoundUpCardPrice; public bool AvoidSetCardPrice; public bool AvoidSetCardPriceRestock; public float PriceMult; public float CardPriceMult; public List PackTypes; } private const byte OpHire = 1; private const float SendInterval = 1f; private const float HealInterval = 15f; private const int MaxWorkers = 32; public static StaffSync Instance; public static bool ApplyingRemote; public Action> SendOp; public Action> BroadcastState; private static readonly FieldInfo FiPanelIsHired = AccessTools.Field(typeof(HireWorkerPanelUI), "m_IsHired"); private static readonly FieldInfo FiPanelIndex = AccessTools.Field(typeof(HireWorkerPanelUI), "m_Index"); private static readonly FieldInfo FiPanelLevelRequired = AccessTools.Field(typeof(HireWorkerPanelUI), "m_LevelRequired"); private static readonly FieldInfo FiPanelHireFee = AccessTools.Field(typeof(HireWorkerPanelUI), "m_TotalHireFee"); private static readonly FieldInfo FiPanelScreen = AccessTools.Field(typeof(HireWorkerPanelUI), "m_HireWorkerScreen"); private static readonly MethodInfo MiPanelEvaluateHired = AccessTools.Method(typeof(HireWorkerPanelUI), "EvaluateHired", (Type[])null, (Type[])null); private WorkerManager _wm; private HireWorkerScreen _hireScreen; private bool _hireScreenSearched; private float _timer; private int _lastHash; private float _heal; private bool _force; private readonly List _buf = new List(32); public StaffSync() { Instance = this; } public void Reset() { _wm = null; _hireScreen = null; _hireScreenSearched = false; _timer = -0.7f; _lastHash = 0; _heal = 0f; _force = false; } public void ForceResend() { _lastHash = 0; _heal = 0f; _force = true; } private WorkerManager Wm() { if ((Object)(object)_wm == (Object)null) { _wm = CSingleton.Instance; } return _wm; } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown Try(h, typeof(HireWorkerPanelUI), "OnPressHireButton", new HarmonyMethod(typeof(StaffSync), "HirePrefix", (Type[])null)); } public static bool HirePrefix(HireWorkerPanelUI __instance) { if (CoopCore.Role != CoopRole.Client) { return true; } try { if ((bool)FiPanelIsHired.GetValue(__instance)) { return false; } int index = (int)FiPanelIndex.GetValue(__instance); int num = (int)FiPanelLevelRequired.GetValue(__instance); float num2 = (float)FiPanelHireFee.GetValue(__instance); if (CPlayerData.m_ShopLevel + 1 < num) { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)2); return false; } if (index < CPlayerData.m_IsWorkerHired.Count && CPlayerData.GetIsWorkerHired(index)) { return false; } if (CPlayerData.m_CoinAmountDouble < (double)num2) { NotEnoughResourceTextPopup.ShowText((ENotEnoughResourceText)0); return false; } StaffSync instance = Instance; if (instance == null || instance.SendOp == null) { CoopPlugin.Log.LogWarning((object)"StaffSync: hire pressed but SendOp not wired - ignored"); return false; } instance.SendOp(delegate(BinaryWriter bw) { bw.Write((byte)1); bw.Write(index); }); SoundManager.GenericConfirm(1f, 1f); if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "hired - starting work at the host's shop"; CoopCore.Instance.RegisterLineTimer = 4f; } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("StaffSync hire prefix: " + ex.Message)); } return false; } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } public void HostApplyOp(BinaryReader br) { byte b = br.ReadByte(); if (b == 1) { HostHire(br.ReadInt32()); } else { CoopPlugin.Log.LogWarning((object)("StaffSync: unknown op " + b)); } } private void HostHire(int index) { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown try { WorkerManager val = Wm(); if ((Object)(object)val == (Object)null || val.m_WorkerDataList == null) { return; } if (index < 0 || index >= val.m_WorkerDataList.Count || index >= CPlayerData.m_IsWorkerHired.Count) { CoopPlugin.Log.LogWarning((object)("StaffSync: hire op for unknown worker " + index)); } else { if (CPlayerData.GetIsWorkerHired(index)) { return; } WorkerData workerData = WorkerManager.GetWorkerData(index); if (CPlayerData.m_ShopLevel + 1 < workerData.shopLevelRequired) { return; } CGameManager instance = CSingleton.Instance; if ((Object)(object)instance != (Object)null && instance.m_IsPrologue && !workerData.prologueShow) { return; } if (CPlayerData.m_CoinAmountDouble < (double)workerData.hiringCost) { CoopPlugin.Log.LogInfo((object)("StaffSync: hire refused, not enough money for worker " + index)); return; } PriceChangeManager.AddTransaction(0f - workerData.hiringCost, (ETransactionType)8, index, 0, (CardData)null); CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(workerData.hiringCost, false)); CPlayerData.SetIsWorkerHired(index, true); val.ActivateWorker(index, true); CPlayerData.m_GameReportDataCollect.employeeCost -= workerData.hiringCost; CPlayerData.m_GameReportDataCollectPermanent.employeeCost -= workerData.hiringCost; int num = 0; for (int i = 0; i < CPlayerData.m_IsWorkerHired.Count; i++) { if (CPlayerData.m_IsWorkerHired[i]) { num++; } } AchievementManager.OnStaffHired(num); SoundManager.PlayAudio("SFX_CustomerBuy", 0.6f, 1f); CoopPlugin.Log.LogInfo((object)("StaffSync: joiner hired worker " + index)); ForceResend(); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("StaffSync host hire: " + ex.Message)); } } public void HostTick(float dt, bool inGame) { if (!inGame) { return; } _timer += dt; if (_timer < 1f) { return; } _timer -= 1f; try { WorkerManager val = Wm(); if ((Object)(object)val == (Object)null || val.m_WorkerDataList == null) { return; } Collect(val, _buf); int num = HashEntries(_buf); _heal += 1f; if (_force || num != _lastHash || !(_heal < 15f)) { _force = false; _lastHash = num; _heal = 0f; List list = _buf; BroadcastState?.Invoke(delegate(BinaryWriter bw) { WriteState(bw, list); }); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("StaffSync host: " + ex.Message)); } } private static void Collect(WorkerManager wm, List outList) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) outList.Clear(); int num = Mathf.Min(wm.m_WorkerDataList.Count, 32); List workerList = WorkerManager.GetWorkerList(); List workerSaveDataList = CPlayerData.m_WorkerSaveDataList; for (int i = 0; i < num; i++) { Entry item = new Entry { Hired = (i < CPlayerData.m_IsWorkerHired.Count && CPlayerData.GetIsWorkerHired(i)) }; WorkerSaveData val = null; Worker val2 = ((workerList != null && i < workerList.Count) ? workerList[i] : null); if ((Object)(object)val2 != (Object)null && val2.m_IsActive) { try { val = val2.GetWorkerSaveData(); } catch { } } if (val == null && workerSaveDataList != null && i < workerSaveDataList.Count) { val = workerSaveDataList[i]; } if (val != null) { item.HasData = true; item.PrimaryTask = (byte)val.primaryTask; item.SecondaryTask = (byte)val.secondaryTask; item.WorkerTask = (byte)val.workerTask; item.BonusCount = (byte)Mathf.Clamp(val.bonusBoostedCount, 0, 255); item.BonusBoosted = val.isBonusBoosted; item.FillNoLabel = val.isFillShelfWithoutLabel; item.RoundUpPrice = val.isRoundUpPrice; item.RoundUpCardPrice = val.isRoundUpCardPrice; item.AvoidSetCardPrice = val.isAvoidSetCardPrice; item.AvoidSetCardPriceRestock = val.isAvoidSetCardPriceWhileRestock; item.PriceMult = val.setPriceMultiplier; item.CardPriceMult = val.setCardPriceMultiplier; item.PackTypes = val.cardPackItemTypeEnabledList; } outList.Add(item); } } private static int HashEntries(List list) { int num = 17; for (int i = 0; i < list.Count; i++) { Entry e = list[i]; num = num * 31 + (e.Hired ? 1 : 0); if (!e.HasData) { num *= 31; continue; } num = num * 31 + e.PrimaryTask; num = num * 31 + e.SecondaryTask; num = num * 31 + e.WorkerTask; num = num * 31 + e.BonusCount; num = num * 31 + PackFlags(e); num = num * 31 + (int)(e.PriceMult * 100f); num = num * 31 + (int)(e.CardPriceMult * 100f); if (e.PackTypes != null) { for (int j = 0; j < e.PackTypes.Count; j++) { num = num * 31 + (e.PackTypes[j] ? 1 : 0); } } } return num; } public void ClientApplyState(BinaryReader br) { ApplyingRemote = true; try { ClientApplyInner(br); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("StaffSync client: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(BinaryReader br) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown int num = br.ReadByte(); bool flag = false; List workerSaveDataList = CPlayerData.m_WorkerSaveDataList; for (int i = 0; i < num; i++) { Entry entry = ReadEntry(br); if (i < CPlayerData.m_IsWorkerHired.Count && CPlayerData.GetIsWorkerHired(i) != entry.Hired) { CPlayerData.SetIsWorkerHired(i, entry.Hired); flag = true; } if (entry.HasData && workerSaveDataList != null) { while (workerSaveDataList.Count <= i) { workerSaveDataList.Add(new WorkerSaveData()); } WorkerSaveData val = workerSaveDataList[i]; if (val == null) { val = (workerSaveDataList[i] = new WorkerSaveData()); } val.primaryTask = (EWorkerTask)entry.PrimaryTask; val.secondaryTask = (EWorkerTask)entry.SecondaryTask; val.workerTask = (EWorkerTask)entry.WorkerTask; val.bonusBoostedCount = entry.BonusCount; val.isBonusBoosted = entry.BonusBoosted; val.isFillShelfWithoutLabel = entry.FillNoLabel; val.isRoundUpPrice = entry.RoundUpPrice; val.isRoundUpCardPrice = entry.RoundUpCardPrice; val.isAvoidSetCardPrice = entry.AvoidSetCardPrice; val.isAvoidSetCardPriceWhileRestock = entry.AvoidSetCardPriceRestock; val.setPriceMultiplier = entry.PriceMult; val.setCardPriceMultiplier = entry.CardPriceMult; if (entry.PackTypes != null) { val.cardPackItemTypeEnabledList = entry.PackTypes; } } } if (flag) { RefreshHirePanels(); } } private void RefreshHirePanels() { if (!_hireScreenSearched) { _hireScreenSearched = true; _hireScreen = Object.FindObjectOfType(true); } if ((Object)(object)_hireScreen == (Object)null || _hireScreen.m_HireWorkerPanelUIList == null || MiPanelEvaluateHired == null || FiPanelScreen == null) { return; } for (int i = 0; i < _hireScreen.m_HireWorkerPanelUIList.Count; i++) { HireWorkerPanelUI val = _hireScreen.m_HireWorkerPanelUIList[i]; if (!((Object)(object)val == (Object)null) && FiPanelScreen.GetValue(val) != null) { try { MiPanelEvaluateHired.Invoke(val, null); } catch { } } } } private static byte PackFlags(Entry e) { return (byte)((e.Hired ? 1u : 0u) | (uint)(e.HasData ? 2 : 0) | (uint)(e.BonusBoosted ? 4 : 0) | (uint)(e.FillNoLabel ? 8 : 0) | (uint)(e.RoundUpPrice ? 16 : 0) | (uint)(e.RoundUpCardPrice ? 32 : 0) | (uint)(e.AvoidSetCardPrice ? 64 : 0) | (uint)(e.AvoidSetCardPriceRestock ? 128 : 0)); } private static void WriteState(BinaryWriter bw, List list) { bw.Write((byte)Mathf.Min(list.Count, 32)); for (int i = 0; i < list.Count && i < 32; i++) { Entry e = list[i]; bw.Write(PackFlags(e)); if (!e.HasData) { continue; } bw.Write(e.PrimaryTask); bw.Write(e.SecondaryTask); bw.Write(e.WorkerTask); bw.Write(e.BonusCount); bw.Write(e.PriceMult); bw.Write(e.CardPriceMult); int num = ((e.PackTypes != null) ? Mathf.Min(e.PackTypes.Count, 255) : 0); bw.Write((byte)num); for (int j = 0; j < num; j += 8) { byte b = 0; for (int k = 0; k < 8 && j + k < num; k++) { if (e.PackTypes[j + k]) { b |= (byte)(1 << k); } } bw.Write(b); } } } private static Entry ReadEntry(BinaryReader br) { byte b = br.ReadByte(); Entry result = new Entry { Hired = ((b & 1) != 0), HasData = ((b & 2) != 0), BonusBoosted = ((b & 4) != 0), FillNoLabel = ((b & 8) != 0), RoundUpPrice = ((b & 0x10) != 0), RoundUpCardPrice = ((b & 0x20) != 0), AvoidSetCardPrice = ((b & 0x40) != 0), AvoidSetCardPriceRestock = ((b & 0x80) != 0) }; if (!result.HasData) { return result; } result.PrimaryTask = br.ReadByte(); result.SecondaryTask = br.ReadByte(); result.WorkerTask = br.ReadByte(); result.BonusCount = br.ReadByte(); result.PriceMult = br.ReadSingle(); result.CardPriceMult = br.ReadSingle(); int num = br.ReadByte(); List list = new List(num); for (int i = 0; i < num; i += 8) { byte b2 = br.ReadByte(); for (int j = 0; j < 8 && i + j < num; j++) { list.Add((b2 & (1 << j)) != 0); } } result.PackTypes = list; return result; } } public class TournamentSync { private struct PairingEntry { public int SortedIndex; public int ModelIndex; public bool IsFemale; public bool IsWin; public bool HasResult; public int WinCount; public int WinPoints; public int OMW; public int OOMW; } public Action> BroadcastState; public static bool ApplyingRemote; private float _timer; private int _lastHash; private float _heal; public void Reset() { _timer = -6.1f; _lastHash = 0; _heal = 0f; } public void ForceResend() { _lastHash = 0; _heal = 999f; } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown Try(h, typeof(HostTournamentScreen), "OnPressConfirm", new HarmonyMethod(typeof(TournamentSync), "ScheduleBlockPrefix", (Type[])null)); Try(h, typeof(HostTournamentScreen), "OnPressCancel", new HarmonyMethod(typeof(TournamentSync), "ScheduleBlockPrefix", (Type[])null)); Try(h, typeof(HostTournamentScreen), "ConfirmCancelTournament", new HarmonyMethod(typeof(TournamentSync), "ScheduleBlockPrefix", (Type[])null)); Try(h, typeof(HostTournamentScreen), "OnPressPrizeSetup", new HarmonyMethod(typeof(TournamentSync), "ScheduleBlockPrefix", (Type[])null)); } public static bool ScheduleBlockPrefix() { if (CoopCore.Role != CoopRole.Client) { return true; } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "the host schedules tournaments"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } public void HostTick(float dt, bool inGame) { if (!inGame) { return; } _timer += dt; if (_timer < 1.5f) { return; } _timer -= 1.5f; try { TournamentData td = CPlayerData.m_TournamentData; if (td == null) { return; } int num = ComputeHash(td); _heal += 1.5f; if (num != _lastHash || !(_heal < 15f)) { _lastHash = num; _heal = 0f; BroadcastState?.Invoke(delegate(BinaryWriter bw) { WriteState(bw, td); }); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TournamentSync host: " + ex.Message)); } } public void ClientApplyState(BinaryReader br) { ApplyingRemote = true; try { ClientApplyInner(br); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TournamentSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(BinaryReader br) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) TournamentData val = CPlayerData.m_TournamentData; if (val == null) { TournamentData val2 = new TournamentData(); val = val2; CPlayerData.m_TournamentData = val2; } byte b = br.ReadByte(); val.m_IsHostingTournament = (b & 1) != 0; bool isTournamentDay = val.m_IsTournamentDay; bool isTournamentDayOver = val.m_IsTournamentDayOver; val.m_IsTournamentDay = (b & 2) != 0; val.m_IsTournamentDayOver = (b & 4) != 0; val.m_TournamentMaxPlayerCount = br.ReadInt32(); val.m_TournamentSignedUpCustomerCount = br.ReadInt32(); val.m_TournamentFinishedCurrentRoundCustomerCount = br.ReadInt32(); val.m_TournamentCurrentRound = br.ReadInt32(); val.m_TournamentMaxRound = br.ReadInt32(); val.m_TournamentFee = br.ReadSingle(); val.m_TournamentTotalValue = br.ReadSingle(); if (val.m_PrizeDataList == null) { val.m_PrizeDataList = new List(); } int num = br.ReadByte(); while (val.m_PrizeDataList.Count < num) { val.m_PrizeDataList.Add(new TournamentPrizeDataList { m_PrizeDataList = new List() }); } for (int i = 0; i < num; i++) { TournamentPrizeDataList val3 = val.m_PrizeDataList[i]; if (val3.m_PrizeDataList == null) { val3.m_PrizeDataList = new List(); } val3.m_PrizeDataList.Clear(); int num2 = br.ReadByte(); for (int j = 0; j < num2; j++) { TournamentPrizeData val4 = new TournamentPrizeData(); if (br.ReadBoolean()) { val4.m_CardData = Msg.ReadCard(br); } val4.m_ItemType = (EItemType)br.ReadInt32(); val4.m_Count = br.ReadInt32(); val3.m_PrizeDataList.Add(val4); } } int num3 = br.ReadByte(); List list = new List(num3); for (int k = 0; k < num3; k++) { PairingEntry item = new PairingEntry { SortedIndex = br.ReadByte(), ModelIndex = br.ReadInt32() }; byte b2 = br.ReadByte(); item.IsFemale = (b2 & 1) != 0; item.IsWin = (b2 & 2) != 0; item.HasResult = (b2 & 4) != 0; item.WinCount = br.ReadInt32(); item.WinPoints = br.ReadInt32(); item.OMW = br.ReadInt32(); item.OOMW = br.ReadInt32(); list.Add(item); } int num4 = ComputeHash(val); for (int l = 0; l < list.Count; l++) { PairingEntry pairingEntry = list[l]; num4 = num4 * 31 + pairingEntry.SortedIndex; num4 = num4 * 31 + pairingEntry.ModelIndex; num4 = num4 * 31 + (int)((pairingEntry.IsFemale ? 1u : 0u) | (uint)(pairingEntry.IsWin ? 2 : 0) | (uint)(pairingEntry.HasResult ? 4 : 0)); num4 = num4 * 31 + pairingEntry.WinCount; num4 = num4 * 31 + pairingEntry.WinPoints; num4 = num4 * 31 + pairingEntry.OMW; num4 = num4 * 31 + pairingEntry.OOMW; } if (num4 != _lastHash) { _lastHash = num4; RefreshBoards(val, list, isTournamentDay != val.m_IsTournamentDay || isTournamentDayOver != val.m_IsTournamentDayOver); } } private void RefreshBoards(TournamentData td, List digest, bool visibilityChanged) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown CustomerManager instance = CSingleton.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_TournamentPairingScreen == (Object)null) { return; } TournamentPairingScreen tournamentPairingScreen = instance.m_TournamentPairingScreen; bool flag = td.m_IsTournamentDay || td.m_IsTournamentDayOver; if (visibilityChanged) { try { ((Component)tournamentPairingScreen).gameObject.SetActive(flag); List tournamentPrizeShelfList = ShelfManager.GetTournamentPrizeShelfList(); for (int i = 0; i < tournamentPrizeShelfList.Count; i++) { if ((Object)(object)tournamentPrizeShelfList[i] != (Object)null && (Object)(object)tournamentPrizeShelfList[i].m_ScreenMesh != (Object)null) { tournamentPrizeShelfList[i].m_ScreenMesh.SetActive(flag); } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TournamentSync board vis: " + ex.Message)); } } if (!flag) { tournamentPairingScreen.ShowPairingScreen(false, 0); return; } tournamentPairingScreen.ShowPairingScreen(true, td.m_TournamentMaxPlayerCount); tournamentPairingScreen.UpdateCurrentRound(td.m_TournamentCurrentRound, td.m_TournamentMaxRound); int num = ((tournamentPairingScreen.m_TournamentPairingUIGrpList != null) ? tournamentPairingScreen.m_TournamentPairingUIGrpList.Count : 0); for (int j = 0; j < digest.Count; j++) { PairingEntry pairingEntry = digest[j]; if (pairingEntry.SortedIndex / 2 < num) { tournamentPairingScreen.OnCustomerRegisterStart(pairingEntry.SortedIndex, pairingEntry.ModelIndex, pairingEntry.IsFemale); CustomerTournamentData val = new CustomerTournamentData { m_TournamentCustomerSortedIndex = pairingEntry.SortedIndex, m_IsTournamentWin = pairingEntry.IsWin, m_HasRegisteredTournamentResult = pairingEntry.HasResult, m_TournamentWinCount = pairingEntry.WinCount, m_TournamentWinPoints = pairingEntry.WinPoints, m_TournamentOMW = pairingEntry.OMW, m_TournamentOOMW = pairingEntry.OOMW }; tournamentPairingScreen.m_TournamentPairingUIGrpList[pairingEntry.SortedIndex / 2].UpdateCustomerData(val); } } } private static void WriteState(BinaryWriter bw, TournamentData td) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)((td.m_IsHostingTournament ? 1u : 0u) | (uint)(td.m_IsTournamentDay ? 2 : 0) | (uint)(td.m_IsTournamentDayOver ? 4 : 0))); bw.Write(td.m_TournamentMaxPlayerCount); bw.Write(td.m_TournamentSignedUpCustomerCount); bw.Write(td.m_TournamentFinishedCurrentRoundCustomerCount); bw.Write(td.m_TournamentCurrentRound); bw.Write(td.m_TournamentMaxRound); bw.Write(td.m_TournamentFee); bw.Write(td.m_TournamentTotalValue); List prizeDataList = td.m_PrizeDataList; int num = ((prizeDataList != null) ? Mathf.Min(prizeDataList.Count, 8) : 0); bw.Write((byte)num); for (int i = 0; i < num; i++) { List list = ((prizeDataList[i] != null) ? prizeDataList[i].m_PrizeDataList : null); int num2 = ((list != null) ? Mathf.Min(list.Count, 64) : 0); bw.Write((byte)num2); for (int j = 0; j < num2; j++) { TournamentPrizeData val = list[j]; bool flag = val != null && val.m_CardData != null; bw.Write(flag); if (flag) { Msg.WriteCard(bw, val.m_CardData); } bw.Write((val != null) ? ((int)val.m_ItemType) : 0); bw.Write(val?.m_Count ?? 0); } } CustomerManager instance = CSingleton.Instance; List list2 = (((Object)(object)instance != (Object)null) ? instance.m_TournamentSortedCustomerList : null); int num3 = ((list2 != null) ? Mathf.Min(list2.Count, 64) : 0); bw.Write((byte)num3); for (int k = 0; k < num3; k++) { Customer val2 = list2[k]; CustomerTournamentData val3 = (((Object)(object)val2 != (Object)null) ? val2.GetCustomerTournamentData() : null); if (val3 == null) { bw.Write((byte)0); bw.Write(0); bw.Write((byte)0); bw.Write(0); bw.Write(0); bw.Write(0); bw.Write(0); } else { bw.Write((byte)Mathf.Clamp(val3.m_TournamentCustomerSortedIndex, 0, 255)); bw.Write(val2.GetCustomerModelIndex()); bw.Write((byte)((val2.m_IsFemale ? 1u : 0u) | (uint)(val3.m_IsTournamentWin ? 2 : 0) | (uint)(val3.m_HasRegisteredTournamentResult ? 4 : 0))); bw.Write(val3.m_TournamentWinCount); bw.Write(val3.m_TournamentWinPoints); bw.Write(val3.m_TournamentOMW); bw.Write(val3.m_TournamentOOMW); } } } private static int ComputeHash(TournamentData td) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected I4, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected I4, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected I4, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected I4, but got Unknown int num = 17; num = num * 31 + (int)((td.m_IsHostingTournament ? 1u : 0u) | (uint)(td.m_IsTournamentDay ? 2 : 0) | (uint)(td.m_IsTournamentDayOver ? 4 : 0)); num = num * 31 + td.m_TournamentMaxPlayerCount; num = num * 31 + td.m_TournamentSignedUpCustomerCount; num = num * 31 + td.m_TournamentFinishedCurrentRoundCustomerCount; num = num * 31 + td.m_TournamentCurrentRound; num = num * 31 + td.m_TournamentMaxRound; num = num * 31 + (int)(td.m_TournamentFee * 100f); num = num * 31 + (int)(td.m_TournamentTotalValue * 100f); List prizeDataList = td.m_PrizeDataList; if (prizeDataList != null) { for (int i = 0; i < prizeDataList.Count; i++) { List list = ((prizeDataList[i] != null) ? prizeDataList[i].m_PrizeDataList : null); if (list == null) { continue; } for (int j = 0; j < list.Count; j++) { TournamentPrizeData val = list[j]; if (val != null) { num = num * 31 + val.m_ItemType; num = num * 31 + val.m_Count; if (val.m_CardData != null) { num = num * 31 + val.m_CardData.expansionType; num = num * 31 + val.m_CardData.monsterType; num = num * 31 + val.m_CardData.borderType; num = num * 31 + (int)((val.m_CardData.isFoil ? 1u : 0u) | (uint)(val.m_CardData.isDestiny ? 2 : 0)); } } } } } if (CoopCore.Role == CoopRole.Host) { CustomerManager instance = CSingleton.Instance; List list2 = (((Object)(object)instance != (Object)null) ? instance.m_TournamentSortedCustomerList : null); if (list2 != null) { for (int k = 0; k < list2.Count; k++) { CustomerTournamentData val2 = (((Object)(object)list2[k] != (Object)null) ? list2[k].GetCustomerTournamentData() : null); if (val2 != null) { num = num * 31 + val2.m_TournamentCustomerSortedIndex; num = num * 31 + (((Object)(object)list2[k] != (Object)null) ? list2[k].GetCustomerModelIndex() : 0); num = num * 31 + ((((Object)(object)list2[k] != (Object)null && list2[k].m_IsFemale) ? 1 : 0) | (val2.m_IsTournamentWin ? 2 : 0) | (val2.m_HasRegisteredTournamentResult ? 4 : 0)); num = num * 31 + val2.m_TournamentWinCount; num = num * 31 + val2.m_TournamentWinPoints; num = num * 31 + val2.m_TournamentOMW; num = num * 31 + val2.m_TournamentOOMW; } } } } return num; } } public class TradeServe { private struct Offer { public byte CounterIdx; public bool Known; public bool Trading; public CardData CardL; public CardData CardR; public float Price; public float Remaining; } private const float Cadence = 0.5f; private const float HealInterval = 6f; private const float StaleAfter = 13f; private const float VanillaWait = 60f; private const int MaxOffers = 32; private const KeyCode DeclineKey = (KeyCode)98; public Action> SendOp; public Action> BroadcastState; private const byte OpAccept = 1; private const byte OpDecline = 2; private static TradeServe _live; private static readonly FieldInfo FiTradeData = AccessTools.Field(typeof(Customer), "m_CustomerTradeData"); private static readonly FieldInfo FiTradeCounter = AccessTools.Field(typeof(Customer), "m_CurrentTradeCardCashierCounter"); private static readonly FieldInfo FiHasTraded = AccessTools.Field(typeof(Customer), "m_HasTradedCard"); private static readonly FieldInfo FiCustTimer = AccessTools.Field(typeof(Customer), "m_Timer"); private static readonly FieldInfo FiCustTimerMax = AccessTools.Field(typeof(Customer), "m_TimerMax"); private static readonly FieldInfo FiPausing = AccessTools.Field(typeof(Customer), "m_IsPausingAction"); private static readonly MethodInfo MiDetermine = AccessTools.Method(typeof(Customer), "DetermineShopAction", (Type[])null, (Type[])null); private static readonly FieldInfo FiScrTrading = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_IsTrading"); private static readonly FieldInfo FiScrAccepted = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_HasAccepted"); private static readonly FieldInfo FiScrPriceSet = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_PriceSet"); private static readonly FieldInfo FiScrLastPrice = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_LastPriceSet"); private static readonly FieldInfo FiScrAsk = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_SellCardAskPrice"); private static readonly FieldInfo FiScrMarket = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_SellCardMarketPrice"); private static readonly FieldInfo FiScrMaxDecline = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_MaxDeclineCount"); private static readonly FieldInfo FiScrDecline = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_DeclineCount"); private static readonly FieldInfo FiScrCardL = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_CardData_L"); private static readonly FieldInfo FiScrCardR = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_CardData_R"); private float _timer; private int _lastHash; private float _heal; private byte _resultSeq; private string _result = ""; private ShelfManager _sm; private readonly List _hostBuf = new List(); private readonly HashSet _preRollFailed = new HashSet(); private readonly HashSet _unknownLogged = new HashSet(); private readonly Dictionary _offers = new Dictionary(); private readonly List _keyBuf = new List(); private float _staleTimer; private float _opThrottle; private int _seenSeq = -1; private int _lastOfferCount = -1; private int _pendingCounter = -1; private bool _nativeBroken; private Transform _playerTf; private static float Reach => CoopPlugin.ServeReach.Value; public TradeServe() { _live = this; } public void Reset() { _timer = -0.83f; _lastHash = 0; _heal = 0f; _resultSeq = 0; _result = ""; _sm = null; _hostBuf.Clear(); _preRollFailed.Clear(); _unknownLogged.Clear(); _offers.Clear(); _staleTimer = 0f; _opThrottle = 0f; _seenSeq = -1; _lastOfferCount = -1; _pendingCounter = -1; _nativeBroken = false; _playerTf = null; } public void ForceResend() { _lastHash = 0; _heal = 999f; } public static void ApplyPatches(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown Try(h, typeof(Customer), "OnMousePress", new HarmonyMethod(typeof(TradeServe), "ClientTradeBlockPrefix", (Type[])null)); Try(h, typeof(CustomerTradeCardScreen), "OnPressAccept", new HarmonyMethod(typeof(TradeServe), "ClientAcceptPrefix", (Type[])null)); Try(h, typeof(CustomerTradeCardScreen), "OnPressDecline", new HarmonyMethod(typeof(TradeServe), "ClientDeclinePrefix", (Type[])null)); Try(h, typeof(CustomerTradeCardScreen), "OnPressLetMeThink", new HarmonyMethod(typeof(TradeServe), "ClientLetMeThinkPrefix", (Type[])null)); Try(h, typeof(Customer), "OnPressStopInteract", new HarmonyMethod(typeof(TradeServe), "ClientStopInteractPrefix", (Type[])null)); } public static bool ClientTradeBlockPrefix() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role != CoopRole.Client) { return true; } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = $"stand at the counter and press {CoopPlugin.ServeKey.Value} to answer the trade"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } public static bool ClientAcceptPrefix(CustomerTradeCardScreen __instance) { if (CoopCore.Role != CoopRole.Client) { return true; } TradeServe live = _live; if (live == null) { return false; } int pendingCounter = live._pendingCounter; float num = ((FiScrPriceSet?.GetValue(__instance) is float num2) ? num2 : 0f); CoopPlugin.Log.LogInfo((object)$"TradeServe client: accept pressed on native screen (counter {pendingCounter}, price {num:F2})"); if (pendingCounter >= 0) { live.SendOpFor(1, pendingCounter, num, "answering the customer..."); } try { ((UIScreenBase)__instance).CloseScreen(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: screen close: " + ex.Message)); } return false; } public static bool ClientDeclinePrefix() { if (CoopCore.Role != CoopRole.Client) { return true; } TradeServe live = _live; if (live != null && live._pendingCounter >= 0) { CoopPlugin.Log.LogInfo((object)$"TradeServe client: decline pressed on native screen (counter {live._pendingCounter})"); live.SendOpFor(2, live._pendingCounter, 0f, "declining..."); } return true; } public static bool ClientLetMeThinkPrefix(CustomerTradeCardScreen __instance) { if (CoopCore.Role != CoopRole.Client) { return true; } try { ((UIScreenBase)__instance).CloseScreen(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: screen close: " + ex.Message)); } return false; } public static bool ClientStopInteractPrefix(Customer __instance) { if (CoopCore.Role != CoopRole.Client) { return true; } FiTradeData?.SetValue(__instance, null); FiPausing?.SetValue(__instance, false); try { InteractionPlayerController instance = CSingleton.Instance; if ((Object)(object)instance != (Object)null) { instance.ExitWorkerInteractMode(); instance.StopAimLookAt(); if ((Object)(object)instance.m_WalkerCtrl != (Object)null) { instance.m_WalkerCtrl.SetStopMovement(false); } instance.ExitUIMode(); } GameUIScreen.ResetToolTipVisibility(); GameUIScreen.ResetEnterGoNextDayIndicatorVisible(); TutorialManager.SetGameUIVisible(true); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: UI restore: " + ex.Message)); } if (_live != null) { _live._pendingCounter = -1; } return false; } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } private static string CardName(CardData c) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (c == null) { return "a card"; } string text = null; try { MonsterData monsterData = InventoryBase.GetMonsterData(c.monsterType); if (monsterData != null) { text = monsterData.GetName(); } } catch { } if (string.IsNullOrEmpty(text)) { text = ((object)Unsafe.As(ref c.monsterType)/*cast due to .constrained prefix*/).ToString(); } if (c.isFoil) { text += " (foil)"; } if (c.cardGrade > 0) { text += $" [grade {c.cardGrade}]"; } return text; } private static string Price(float p) { try { return GameInstance.GetPriceString(p, false, true, false, "F2"); } catch { return "$" + p.ToString("F2"); } } private static int CardHash(CardData c) { //IL_000b: 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_0013: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected I4, but got Unknown if (c == null) { return 0; } return (int)(((((17 * 31 + c.monsterType) * 31 + c.expansionType) * 31 + c.borderType) * 31 + ((c.isFoil ? 1u : 0u) | (uint)(c.isDestiny ? 2 : 0))) * 31 + c.cardGrade); } public void HostTick(float dt, bool inGame) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Invalid comparison between Unknown and I4 if (!inGame) { return; } _timer += dt; if (_timer < 0.5f) { return; } _timer -= 0.5f; try { CustomerManager instance = CSingleton.Instance; ShelfManager val = Sm(); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null) { return; } _hostBuf.Clear(); List customerList = instance.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { if (_hostBuf.Count >= 32) { break; } Customer val2 = customerList[i]; if ((Object)(object)val2 == (Object)null || !val2.m_IsActive || (int)val2.m_CurrentState != 18) { continue; } object? obj = FiTradeCounter?.GetValue(val2); InteractableCashierCounter val3 = (InteractableCashierCounter)((obj is InteractableCashierCounter) ? obj : null); if ((Object)(object)val3 == (Object)null) { continue; } int num = val.m_CashierCounterList.IndexOf(val3); if (num >= 0 && num <= 250) { object? obj2 = FiTradeData?.GetValue(val2); CustomerTradeData val4 = (CustomerTradeData)((obj2 is CustomerTradeData) ? obj2 : null); if (val4 == null) { val4 = PreRoll(instance, val2); } float num2 = ((FiCustTimer?.GetValue(val2) is float num3) ? num3 : 0f); bool flag = val4 != null && val4.m_CardData_L != null && (!val4.m_IsTrading || val4.m_CardData_R != null); if (!flag && _unknownLogged.Add(((Object)val2).GetInstanceID())) { CoopPlugin.Log.LogInfo((object)$"TradeServe host: offer at counter {num} broadcast as host-only (no pre-rolled data yet)"); } Offer item = new Offer { CounterIdx = (byte)num, Known = flag, Remaining = Mathf.Clamp(60f - num2, 0f, 60f) }; if (flag) { item.Trading = val4.m_IsTrading; item.CardL = val4.m_CardData_L; item.CardR = val4.m_CardData_R; item.Price = val4.m_SellCardAskPrice; } _hostBuf.Add(item); } } int num4 = 17; num4 = num4 * 31 + _resultSeq; for (int j = 0; j < _hostBuf.Count; j++) { Offer offer = _hostBuf[j]; num4 = num4 * 31 + offer.CounterIdx; num4 = num4 * 31 + (int)((offer.Known ? 1u : 0u) | (uint)(offer.Trading ? 2 : 0)); num4 = num4 * 31 + CardHash(offer.CardL); num4 = num4 * 31 + CardHash(offer.CardR); num4 = num4 * 31 + (int)(offer.Price * 100f); } _heal += 0.5f; bool flag2 = num4 != _lastHash; if (!flag2 && _heal < 6f) { return; } _lastHash = num4; _heal = 0f; if (flag2) { string text = ""; for (int k = 0; k < _hostBuf.Count; k++) { Offer offer2 = _hostBuf[k]; text += string.Format(" [{0}:{1}]", offer2.CounterIdx, (!offer2.Known) ? "unknown" : (offer2.Trading ? ("trade " + CardName(offer2.CardL)) : ("sell " + CardName(offer2.CardL) + " @ " + Price(offer2.Price)))); } CoopPlugin.Log.LogInfo((object)$"TradeServe host: broadcasting {_hostBuf.Count} offer(s){text}"); } BroadcastState?.Invoke(WriteState); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe host: " + ex.Message)); } } private CustomerTradeData PreRoll(CustomerManager cm, Customer cust) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Expected O, but got Unknown if (FiTradeData == null) { return null; } CustomerTradeCardScreen customerTradeCardScreen = cm.m_CustomerTradeCardScreen; if ((Object)(object)customerTradeCardScreen == (Object)null || cm.m_IsPlayerTrading || ((UIScreenBase)customerTradeCardScreen).IsScreenOpened()) { return null; } int instanceID = ((Object)cust).GetInstanceID(); if (_preRollFailed.Contains(instanceID)) { return null; } try { customerTradeCardScreen.SetCustomer(cust, (CustomerTradeData)null); CustomerTradeData val = new CustomerTradeData(); object obj = FiScrTrading?.GetValue(customerTradeCardScreen); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } val.m_IsTrading = (byte)((uint)num & (flag ? 1u : 0u)) != 0; val.m_PriceSet = ((FiScrPriceSet?.GetValue(customerTradeCardScreen) is float num2) ? num2 : 0f); val.m_LastPriceSet = ((FiScrLastPrice?.GetValue(customerTradeCardScreen) is float num3) ? num3 : 0f); val.m_SellCardAskPrice = ((FiScrAsk?.GetValue(customerTradeCardScreen) is float num4) ? num4 : 0f); val.m_SellCardMarketPrice = ((FiScrMarket?.GetValue(customerTradeCardScreen) is float num5) ? num5 : 0f); val.m_MaxDeclineCount = ((FiScrMaxDecline?.GetValue(customerTradeCardScreen) is int num6) ? num6 : 0); val.m_DeclineCount = ((FiScrDecline?.GetValue(customerTradeCardScreen) is int num7) ? num7 : 0); ref CardData cardData_L = ref val.m_CardData_L; object? obj2 = FiScrCardL?.GetValue(customerTradeCardScreen); cardData_L = (CardData)((obj2 is CardData) ? obj2 : null); ref CardData cardData_R = ref val.m_CardData_R; object? obj3 = FiScrCardR?.GetValue(customerTradeCardScreen); cardData_R = (CardData)((obj3 is CardData) ? obj3 : null); CustomerTradeData val2 = val; if (val2.m_CardData_L == null) { _preRollFailed.Add(instanceID); CoopPlugin.Log.LogWarning((object)"TradeServe: pre-roll produced no card; offer left for the host to serve"); return null; } FiTradeData?.SetValue(cust, val2); CoopPlugin.Log.LogInfo((object)("TradeServe host: pre-rolled " + (val2.m_IsTrading ? ("trade " + CardName(val2.m_CardData_L) + " for " + CardName(val2.m_CardData_R)) : ("sell-in " + CardName(val2.m_CardData_L) + " @ " + Price(val2.m_SellCardAskPrice))))); return val2; } catch (Exception ex) { _preRollFailed.Add(instanceID); CoopPlugin.Log.LogWarning((object)("TradeServe pre-roll: " + ex.Message)); return null; } finally { try { cm.m_IsPlayerTrading = false; } catch { } } } private void WriteState(BinaryWriter bw) { bw.Write(_resultSeq); bw.Write(_result ?? ""); bw.Write((byte)_hostBuf.Count); for (int i = 0; i < _hostBuf.Count; i++) { Offer offer = _hostBuf[i]; bw.Write(offer.CounterIdx); bw.Write((byte)((offer.Known ? 1u : 0u) | (uint)(offer.Trading ? 2 : 0))); if (offer.Known) { Msg.WriteCard(bw, offer.CardL); if (offer.Trading) { Msg.WriteCard(bw, offer.CardR); } else { bw.Write(offer.Price); } } bw.Write(offer.Remaining); } } private void Result(string text) { _result = text; _resultSeq++; ForceResend(); CoopPlugin.Log.LogInfo((object)("TradeServe: " + text)); } public void HostApplyOp(BinaryReader br) { byte b = br.ReadByte(); int num = br.ReadByte(); float num2 = br.ReadSingle(); CoopPlugin.Log.LogInfo((object)string.Format("TradeServe host: received {0} @ counter {1}, price {2:F2}", b switch { 2 => "decline", 1 => "accept", _ => "op " + b, }, num, num2)); try { HostApplyOpInner(b, num, num2); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe op: " + ex)); Result("trade failed - ask the host to serve them"); } } private void HostApplyOpInner(byte op, int idx, float price) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Invalid comparison between Unknown and I4 CustomerManager instance = CSingleton.Instance; ShelfManager val = Sm(); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null || idx < 0 || idx >= val.m_CashierCounterList.Count) { Result("no counter there"); return; } InteractableCashierCounter val2 = val.m_CashierCounterList[idx]; Customer val3 = null; List customerList = instance.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { Customer val4 = customerList[i]; if ((Object)(object)val4 != (Object)null && val4.m_IsActive && (int)val4.m_CurrentState == 18 && FiTradeCounter?.GetValue(val4) == val2) { val3 = val4; break; } } if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { Result("the customer already left"); return; } if (instance.m_IsPlayerTrading) { Result("the host is talking to that customer right now"); return; } switch (op) { case 2: FinishCustomer(val3, val2); Result("trade declined - the customer moves on"); break; case 1: { object? obj = FiTradeData?.GetValue(val3); CustomerTradeData val5 = (CustomerTradeData)((obj is CustomerTradeData) ? obj : null); if (val5 == null) { val5 = PreRoll(instance, val3); } if (val5 == null || val5.m_CardData_L == null) { Result("couldn't read the offer - the host must serve this one"); break; } float num = ((float.IsNaN(price) || price < 0f) ? val5.m_SellCardAskPrice : price); if (val5.m_IsTrading) { CardData cardData_R = val5.m_CardData_R; if (cardData_R == null || !((cardData_R.cardGrade == 0) ? (CPlayerData.GetCardAmount(cardData_R) > 0) : CPlayerData.HasGradedCardInAlbum(cardData_R))) { Result("the binder no longer has " + CardName(cardData_R) + " to trade"); break; } } else if (CPlayerData.m_CoinAmountDouble < (double)num) { Result("not enough money to pay " + Price(num)); break; } CustomerTradeCardScreen customerTradeCardScreen = instance.m_CustomerTradeCardScreen; if ((Object)(object)customerTradeCardScreen == (Object)null || ((UIScreenBase)customerTradeCardScreen).IsScreenOpened()) { Result("the host has the trade screen open"); break; } float sellCardAskPrice = val5.m_SellCardAskPrice; int declineCount = val5.m_DeclineCount; bool flag2; float num3; int num5; try { customerTradeCardScreen.SetCustomer(val3, val5); if (!val5.m_IsTrading) { FiScrPriceSet?.SetValue(customerTradeCardScreen, num); } CoopPlugin.Log.LogInfo((object)("TradeServe host: applying vanilla accept (" + (val5.m_IsTrading ? "trade" : ("bid " + Price(num) + " vs ask " + Price(sellCardAskPrice))) + ")")); customerTradeCardScreen.OnPressAccept(); object obj2 = FiScrAccepted?.GetValue(customerTradeCardScreen); bool flag = default(bool); int num2; if (obj2 is bool) { flag = (bool)obj2; num2 = 1; } else { num2 = 0; } flag2 = (byte)((uint)num2 & (flag ? 1u : 0u)) != 0; num3 = ((FiScrAsk?.GetValue(customerTradeCardScreen) is float num4) ? num4 : sellCardAskPrice); num5 = ((FiScrDecline?.GetValue(customerTradeCardScreen) is int num6) ? num6 : declineCount); if (!flag2) { val5.m_PriceSet = ((FiScrPriceSet?.GetValue(customerTradeCardScreen) is float num7) ? num7 : num); val5.m_LastPriceSet = ((FiScrLastPrice?.GetValue(customerTradeCardScreen) is float num8) ? num8 : num); val5.m_SellCardAskPrice = num3; val5.m_MaxDeclineCount = ((FiScrMaxDecline?.GetValue(customerTradeCardScreen) is int num9) ? num9 : val5.m_MaxDeclineCount); val5.m_DeclineCount = num5; FiTradeData?.SetValue(val3, val5); } } finally { try { instance.m_IsPlayerTrading = false; } catch { } } if (flag2) { FinishCustomer(val3, val2); Result(val5.m_IsTrading ? ("traded " + CardName(val5.m_CardData_R) + " for " + CardName(val5.m_CardData_L)) : ("bought " + CardName(val5.m_CardData_L) + " for " + Price(num))); } else if (val5.m_IsTrading) { Result("the trade fell through - the host must serve this one"); } else if (num5 == declineCount) { FinishCustomer(val3, val2); Result("the customer lost patience and left"); } else { Result((Mathf.Abs(num3 - sellCardAskPrice) > 0.005f) ? ("they refuse " + Price(num) + " - now asking " + Price(num3)) : ("they refuse " + Price(num) + " - try higher")); } break; } } } private static void FinishCustomer(Customer cust, InteractableCashierCounter counter) { FiCustTimer?.SetValue(cust, 0f); FiCustTimerMax?.SetValue(cust, 0f); FiTradeData?.SetValue(cust, null); FiPausing?.SetValue(cust, false); try { if ((Object)(object)cust.m_ExclaimationMesh != (Object)null) { cust.m_ExclaimationMesh.SetActive(false); } if ((Object)(object)cust.m_InteractCollider != (Object)null) { cust.m_InteractCollider.SetActive(false); } } catch { } FiHasTraded?.SetValue(cust, true); try { if (counter != null) { counter.CustomerFinishTradingCard(); } } catch { } FiTradeCounter?.SetValue(cust, null); try { MiDetermine?.Invoke(cust, null); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe finish: " + ex.Message)); } } public void ClientApplyState(BinaryReader br) { byte b = br.ReadByte(); string text = br.ReadString(); int num = br.ReadByte(); _offers.Clear(); for (int i = 0; i < num; i++) { Offer value = new Offer { CounterIdx = br.ReadByte() }; byte b2 = br.ReadByte(); value.Known = (b2 & 1) != 0; value.Trading = (b2 & 2) != 0; if (value.Known) { value.CardL = Msg.ReadCard(br); if (value.Trading) { value.CardR = Msg.ReadCard(br); } else { value.Price = br.ReadSingle(); } } value.Remaining = br.ReadSingle(); _offers[value.CounterIdx] = value; } _staleTimer = 0f; if (num != _lastOfferCount) { _lastOfferCount = num; CoopPlugin.Log.LogInfo((object)$"TradeServe client: state received, {num} live offer(s)"); } if (b != _seenSeq) { _seenSeq = b; if (text.Length > 0 && (Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = text; CoopCore.Instance.RegisterLineTimer = 4f; } } } public bool HasOffer(int counterIdx) { if (counterIdx >= 0) { return _offers.ContainsKey(counterIdx); } return false; } public string PromptFor(int nearestCounter) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (nearestCounter < 0 || !_offers.TryGetValue(nearestCounter, out var value)) { return null; } if (!value.Known) { return "a customer wants to trade - the host must serve them"; } string text = (_nativeBroken ? $"{CoopPlugin.ServeKey.Value} accept, {(object)(KeyCode)98} decline" : $"{CoopPlugin.ServeKey.Value} answer, {(object)(KeyCode)98} decline"); if (value.Trading) { return "trade: their " + CardName(value.CardL) + " for your " + CardName(value.CardR) + " - " + text; } return "sell-in: " + CardName(value.CardL) + " for " + Price(value.Price) + " - " + text; } private Transform PlayerBody() { if ((Object)(object)_playerTf != (Object)null) { return _playerTf; } InteractionPlayerController instance = CSingleton.Instance; if ((Object)(object)instance == (Object)null) { return null; } _playerTf = (((Object)(object)instance.m_WalkerCtrl != (Object)null) ? ((Component)instance.m_WalkerCtrl).transform : ((Component)instance).transform); return _playerTf; } private void SendOpFor(byte op, int idx, float price, string line) { _opThrottle = 0.5f; CoopPlugin.Log.LogInfo((object)string.Format("TradeServe client: sending {0} @ counter {1}, price {2:F2}", (op == 1) ? "accept" : "decline", idx, price)); SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write(op); bw.Write((byte)idx); bw.Write(price); }); _offers.Remove(idx); if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = line; CoopCore.Instance.RegisterLineTimer = 2f; } } private void OpenNativeScreen(int idx, Offer offer) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown CustomerManager instance = CSingleton.Instance; CustomerTradeCardScreen val = (((Object)(object)instance != (Object)null) ? instance.m_CustomerTradeCardScreen : null); if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("no CustomerTradeCardScreen"); } if (((UIScreenBase)val).IsScreenOpened()) { return; } Customer val2 = null; List customerList = instance.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { if ((Object)(object)customerList[i] != (Object)null) { val2 = customerList[i]; break; } } if ((Object)(object)val2 == (Object)null) { throw new InvalidOperationException("no carrier customer in the pool"); } CustomerTradeData val3 = new CustomerTradeData { m_IsTrading = offer.Trading, m_CardData_L = offer.CardL, m_CardData_R = offer.CardR, m_SellCardAskPrice = offer.Price, m_SellCardMarketPrice = 0f, m_PriceSet = 0f, m_LastPriceSet = 0f, m_MaxDeclineCount = 0, m_DeclineCount = 0 }; val.SetCustomer(val2, val3); InteractionPlayerController instance2 = CSingleton.Instance; if ((Object)(object)instance2 == (Object)null) { throw new InvalidOperationException("no InteractionPlayerController"); } instance2.EnterWorkerInteractMode(); instance2.EnterUIMode(); instance2.EnterLockMoveMode(); GameUIScreen.HideToolTip(); GameUIScreen.HideEnterGoNextDayIndicatorVisible(); TutorialManager.SetGameUIVisible(false); ((UIScreenBase)val).OpenScreen(); _pendingCounter = idx; CoopPlugin.Log.LogInfo((object)$"TradeServe client: opened native trade screen for counter {idx}"); } public void ClientTick(float dt, bool inGame) { //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) if (_opThrottle > 0f) { _opThrottle -= dt; } if (_offers.Count > 0) { _staleTimer += dt; if (_staleTimer > 13f) { _offers.Clear(); } else { _keyBuf.Clear(); foreach (KeyValuePair offer in _offers) { _keyBuf.Add(offer.Key); } for (int i = 0; i < _keyBuf.Count; i++) { Offer value = _offers[_keyBuf[i]]; value.Remaining -= dt; if (value.Remaining <= 0f) { _offers.Remove(_keyBuf[i]); } else { _offers[_keyBuf[i]] = value; } } } } if (_pendingCounter >= 0) { CustomerManager instance = CSingleton.Instance; CustomerTradeCardScreen val = (((Object)(object)instance != (Object)null) ? instance.m_CustomerTradeCardScreen : null); if ((Object)(object)val == (Object)null || !((UIScreenBase)val).IsScreenOpened()) { _pendingCounter = -1; } else if (!_offers.ContainsKey(_pendingCounter)) { CoopPlugin.Log.LogInfo((object)"TradeServe client: offer vanished while the screen was open - closing it"); try { ((UIScreenBase)val).CloseScreen(); } catch { } _pendingCounter = -1; if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "the customer left"; CoopCore.Instance.RegisterLineTimer = 3f; } } } else { if (!inGame || _offers.Count == 0 || _opThrottle > 0f || CoopUI.TextFieldFocused) { return; } bool keyDown = Input.GetKeyDown(CoopPlugin.ServeKey.Value); bool keyDown2 = Input.GetKeyDown((KeyCode)98); if (!keyDown && !keyDown2) { return; } Transform val2 = PlayerBody(); if ((Object)(object)val2 == (Object)null) { CoopPlugin.Log.LogInfo((object)"TradeServe client: key pressed but no player body resolved"); return; } int num = RegisterServe.FindNearestCounter(val2.position, Reach, quiet: true); if (num < 0 || !_offers.TryGetValue(num, out var value2)) { CoopPlugin.Log.LogInfo((object)string.Format("TradeServe client: {0} key ignored (nearest counter {1}, offers at [{2}])", keyDown ? "serve" : "decline", num, string.Join(",", _offers.Keys))); return; } if (!value2.Known) { _opThrottle = 0.5f; CoopPlugin.Log.LogInfo((object)$"TradeServe client: offer at counter {num} is host-only (pre-roll failed on the host)"); if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "the host must serve this one"; CoopCore.Instance.RegisterLineTimer = 3f; } return; } if (keyDown2) { SendOpFor(2, num, 0f, "declining..."); return; } if (!_nativeBroken) { _opThrottle = 0.3f; try { OpenNativeScreen(num, value2); return; } catch (Exception ex) { _nativeBroken = true; _pendingCounter = -1; try { CustomerManager instance2 = CSingleton.Instance; if ((Object)(object)instance2 != (Object)null) { instance2.m_IsPlayerTrading = false; } } catch { } CoopPlugin.Log.LogWarning((object)("TradeServe client: native trade screen failed, falling back to prompt keys: " + ex)); if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = $"trade screen unavailable - {CoopPlugin.ServeKey.Value} accepts at asking price, {(object)(KeyCode)98} declines"; CoopCore.Instance.RegisterLineTimer = 4f; } return; } } SendOpFor(1, num, -1f, "answering the customer..."); } } } public class WorldSync { public struct Entry { public int Key; public int Type; public int Count; } private struct CompState { public int Type; public int Count; } private readonly Dictionary _last = new Dictionary(); private readonly Dictionary> _whComps = new Dictionary>(); private float _timer; public Action> OnLocalChanges; private static readonly FieldInfo FiWarehouseComps = AccessTools.Field(typeof(WarehouseShelf), "m_ItemCompartmentList"); private ShelfManager _sm; private static readonly FieldInfo FiStoredItemList = AccessTools.Field(typeof(ShelfCompartment), "m_StoredItemList"); private ShelfManager ResolveShelfManager() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } private static int Key(int kind, int shelf, int comp) { return (kind << 24) | ((shelf & 0xFFFF) << 8) | (comp & 0xFF); } public void Reset() { _last.Clear(); _whComps.Clear(); _timer = 0.35f; _sm = null; } public void Tick(float dt, bool inGame) { if (!inGame) { return; } _timer += dt; if (_timer < 0.75f) { return; } _timer -= 0.75f; List changes = null; try { ShelfManager val = ResolveShelfManager(); if ((Object)(object)val == (Object)null) { return; } for (int i = 0; i < val.m_ShelfList.Count; i++) { Shelf val2 = val.m_ShelfList[i]; if (!((Object)(object)val2 == (Object)null)) { List itemCompartmentList = val2.GetItemCompartmentList(); for (int j = 0; j < itemCompartmentList.Count; j++) { Visit(Key(0, i, j), itemCompartmentList[j], ref changes); } } } for (int k = 0; k < val.m_WarehouseShelfList.Count; k++) { WarehouseShelf val3 = val.m_WarehouseShelfList[k]; if ((Object)(object)val3 == (Object)null) { continue; } if (!_whComps.TryGetValue(val3, out var value) || value == null) { value = (_whComps[val3] = FiWarehouseComps?.GetValue(val3) as List); } if (value != null) { for (int l = 0; l < value.Count; l++) { Visit(Key(1, k, l), value[l], ref changes); } } } for (int m = 0; m < val.m_CardItemCombiShelfList.Count; m++) { CardItemCombiShelf val4 = val.m_CardItemCombiShelfList[m]; if (!((Object)(object)val4 == (Object)null)) { List itemCompartmentList2 = val4.GetItemCompartmentList(); for (int n = 0; n < itemCompartmentList2.Count; n++) { Visit(Key(3, m, n), itemCompartmentList2[n], ref changes); } } } for (int num = 0; num < val.m_TournamentPrizeShelfList.Count; num++) { TournamentPrizeShelf val5 = val.m_TournamentPrizeShelfList[num]; if (!((Object)(object)val5 == (Object)null)) { List itemCompartmentList3 = ((CardItemCombiShelf)val5).GetItemCompartmentList(); for (int num2 = 0; num2 < itemCompartmentList3.Count; num2++) { Visit(Key(14, num, num2), itemCompartmentList3[num2], ref changes); } } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("WorldSync snapshot: " + ex.Message)); return; } if (changes != null && changes.Count > 0) { OnLocalChanges?.Invoke(changes); } } private void Visit(int key, ShelfCompartment comp, ref List changes) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected I4, but got Unknown if ((Object)(object)comp == (Object)null) { return; } int num = (int)comp.GetItemType(); int itemCount = comp.GetItemCount(); if (!_last.TryGetValue(key, out var value) || value.Type != num || value.Count != itemCount) { if (changes == null) { changes = new List(); } if (changes.Count < 512) { _last[key] = new CompState { Type = num, Count = itemCount }; changes.Add(new Entry { Key = key, Type = num, Count = itemCount }); } } } public void ApplyRemote(List entries) { ShelfManager val = ResolveShelfManager(); if ((Object)(object)val == (Object)null) { return; } foreach (Entry entry in entries) { try { ShelfCompartment val2 = Resolve(val, entry.Key); if (!((Object)(object)val2 == (Object)null)) { ApplyCompartment(val2, entry.Type, entry.Count); _last[entry.Key] = new CompState { Type = entry.Type, Count = entry.Count }; } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"WorldSync apply {entry.Key:X}: {ex.Message}"); } } } private static ShelfCompartment Resolve(ShelfManager sm, int key) { int num = key >> 24; int num2 = (key >> 8) & 0xFFFF; int num3 = key & 0xFF; switch (num) { case 0: { if (num2 >= sm.m_ShelfList.Count) { return null; } Shelf obj3 = sm.m_ShelfList[num2]; List list4 = ((obj3 != null) ? obj3.GetItemCompartmentList() : null); if (list4 == null || num3 >= list4.Count) { return null; } return list4[num3]; } case 3: { if (num2 >= sm.m_CardItemCombiShelfList.Count) { return null; } CardItemCombiShelf obj2 = sm.m_CardItemCombiShelfList[num2]; List list3 = ((obj2 != null) ? obj2.GetItemCompartmentList() : null); if (list3 == null || num3 >= list3.Count) { return null; } return list3[num3]; } case 14: { if (num2 >= sm.m_TournamentPrizeShelfList.Count) { return null; } TournamentPrizeShelf obj = sm.m_TournamentPrizeShelfList[num2]; List list2 = ((obj != null) ? ((CardItemCombiShelf)obj).GetItemCompartmentList() : null); if (list2 == null || num3 >= list2.Count) { return null; } return list2[num3]; } default: if (num2 >= sm.m_WarehouseShelfList.Count) { return null; } if (!(FiWarehouseComps?.GetValue(sm.m_WarehouseShelfList[num2]) is List list) || num3 >= list.Count) { return null; } return list[num3]; } } private static void ApplyCompartment(ShelfCompartment comp, int type, int count) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected I4, but got Unknown int num = (int)comp.GetItemType(); int itemCount = comp.GetItemCount(); if (itemCount == count && (num == type || count == 0)) { return; } if (num == type && count < itemCount && count > 0) { for (int num2 = itemCount - count; num2 > 0; num2--) { Item lastItem = comp.GetLastItem(); if ((Object)(object)lastItem == (Object)null) { break; } comp.RemoveItem(lastItem); ItemSpawnManager.DisableItem(lastItem); } if (comp.GetItemCount() == count) { return; } } Clear(comp); if (count > 0) { comp.SetCompartmentItemType((EItemType)type); comp.CalculatePositionList(); comp.SpawnItem(count, true); } } private static void Clear(ShelfCompartment comp) { if (FiStoredItemList?.GetValue(comp) is List { Count: >0 } list) { foreach (Item item in new List(list)) { if (!((Object)(object)item == (Object)null)) { comp.RemoveItem(item); ItemSpawnManager.DisableItem(item); } } list.Clear(); return; } for (int i = 0; i < 4096; i++) { Item lastItem = comp.GetLastItem(); if (!((Object)(object)lastItem == (Object)null)) { comp.RemoveItem(lastItem); ItemSpawnManager.DisableItem(lastItem); continue; } break; } } public static void WriteEntries(BinaryWriter bw, List entries) { bw.Write((ushort)entries.Count); foreach (Entry entry in entries) { bw.Write(entry.Key); bw.Write(entry.Type); bw.Write((ushort)Math.Max(0, Math.Min(entry.Count, 65535))); } } public static List ReadEntries(BinaryReader br) { int num = br.ReadUInt16(); List list = new List(num); for (int i = 0; i < num; i++) { list.Add(new Entry { Key = br.ReadInt32(), Type = br.ReadInt32(), Count = br.ReadUInt16() }); } return list; } } } namespace CardShopCoop.Patches { public static class GamePatches { public static bool ApplyingRemoteLicense; public static bool ApplyingRemotePrice; public static bool ApplyingRemoteCards; public static bool AllowNextDayStarted; public static void ApplyAll(Harmony h) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Expected O, but got Unknown //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Expected O, but got Unknown //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Expected O, but got Unknown Try(h, typeof(CGameManager), "SaveGameData", new HarmonyMethod(typeof(GamePatches), "SaveGuardPrefix", (Type[])null)); Try(h, typeof(CustomerManager), "Update", new HarmonyMethod(typeof(GamePatches), "ClientBlockPrefix", (Type[])null)); Try(h, typeof(Customer), "ActivateCustomer", new HarmonyMethod(typeof(GamePatches), "ClientBlockPrefix", (Type[])null)); Try(h, typeof(WorkerManager), "ActivateWorker", new HarmonyMethod(typeof(GamePatches), "ClientBlockPrefix", (Type[])null)); Try(h, typeof(CEventManager), "QueueEvent", new HarmonyMethod(typeof(GamePatches), "DayEndBlockPrefix", (Type[])null)); Try(h, typeof(CPlayerData), "AddCard", null, new HarmonyMethod(typeof(GamePatches), "AddCardPostfix", (Type[])null)); Try(h, typeof(CPlayerData), "ReduceCard", null, new HarmonyMethod(typeof(GamePatches), "ReduceCardPostfix", (Type[])null)); Try(h, typeof(CPlayerData), "SetCardPrice", null, new HarmonyMethod(typeof(GamePatches), "SetCardPricePostfix", (Type[])null)); Try(h, typeof(RestockManager), "SpawnPackageBoxItemMultipleFrame", new HarmonyMethod(typeof(GamePatches), "OrderPrefix", (Type[])null)); Try(h, typeof(ShopRenamer), "ShowRenameShopScreen", new HarmonyMethod(typeof(GamePatches), "RenamerBlockPrefix", (Type[])null)); Try(h, typeof(ShelfManager), "SpawnInteractableObjectInPackageBox", new HarmonyMethod(typeof(GamePatches), "FurnitureOrderPrefix", (Type[])null)); Try(h, typeof(InteractablePackagingBox_Item), "OnDestroyed", new HarmonyMethod(typeof(GamePatches), "BoxDestroyedPrefix", (Type[])null)); Try(h, typeof(CPlayerData), "SetUnlockItemLicense", null, new HarmonyMethod(typeof(GamePatches), "LicenseUnlockPostfix", (Type[])null)); Try(h, typeof(CPlayerData), "ReduceCardUsingIndex", null, new HarmonyMethod(typeof(GamePatches), "ReduceCardIndexPostfix", (Type[])null)); TryModule("staff", StaffSync.ApplyPatches, h); TryModule("shopstate", ShopStateSync.ApplyPatches, h); TryModule("settings", SettingsSync.ApplyPatches, h); TryModule("market", MarketSync.ApplyPatches, h); TryModule("report", ReportSync.ApplyPatches, h); TryModule("containers", ContainerSync.ApplyPatches, h); TryModule("tournament", TournamentSync.ApplyPatches, h); TryModule("grading", GradingSync.ApplyPatches, h); TryModule("trades", TradeServe.ApplyPatches, h); TryModule("playtables", PlayTableSync.ApplyPatches, h); TryModule("cardboxes", CardBoxSync.ApplyPatches, h); } private static void TryModule(string name, Action apply, Harmony h) { try { apply(h); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Module patches failed (" + name + "): " + ex.Message)); } } public static void ReduceCardIndexPostfix(int index, ECardExpansionType expansionType, bool isDestiny, int reduceAmount) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (ApplyingRemoteCards || CoopCore.Role == CoopRole.None) { return; } try { CardData cardData = CPlayerData.GetCardData(index, expansionType, isDestiny); if (cardData != null) { CoopCore.Instance?.ForwardCardDelta(cardData, reduceAmount, isAdd: false); } } catch { } } public static bool BoxDestroyedPrefix(InteractablePackagingBox_Item __instance) { if (!BoxSync.ApplyingRemote) { BoxSync.LocalBoxDestroyed?.Invoke(__instance); } return true; } public static void LicenseUnlockPostfix(int index) { if (!ApplyingRemoteLicense && CoopCore.Role != CoopRole.None) { CoopCore.Instance?.ForwardLicense(index); } } public static bool FurnitureOrderPrefix(EObjectType objType, Vector3 spawnPos, Quaternion spawnRot) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown if (CoopCore.Role != CoopRole.Client) { return true; } CoopCore.Instance?.ForwardFurniture((int)objType, spawnPos, spawnRot); if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "furniture delivered at the host's shop"; CoopCore.Instance.RegisterLineTimer = 4f; } return false; } public static bool OrderPrefix(int restockIndex, int count) { if (CoopCore.Role != CoopRole.Client) { return true; } CoopCore.Instance?.ForwardOrder(restockIndex, count); return false; } public static bool RenamerBlockPrefix() { if (CoopCore.Role != CoopRole.Client) { return true; } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "the host names the shop"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } public static void SetCardPricePostfix(CardData cardData, float priceSet) { if (!ApplyingRemotePrice && CoopCore.Role != CoopRole.None) { CoopCore.Instance?.ForwardCardPrice(cardData, priceSet); } } public static void AddCardPostfix(CardData cardData, int addAmount) { if (!ApplyingRemoteCards && CoopCore.Role != CoopRole.None) { CoopCore.Instance?.ForwardCardDelta(cardData, addAmount, isAdd: true); } } public static void ReduceCardPostfix(CardData cardData, int reduceAmount) { if (!ApplyingRemoteCards && CoopCore.Role != CoopRole.None) { CoopCore.Instance?.ForwardCardDelta(cardData, reduceAmount, isAdd: false); } } private static void Try(Harmony h, Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { CoopPlugin.Log.LogWarning((object)("Patch target missing: " + type.Name + "." + method)); } else { h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("Patch failed for " + type.Name + "." + method + ": " + ex.Message)); } } public static bool SaveGuardPrefix(ref int saveSlotIndex) { return CoopCore.Role != CoopRole.Client; } public static bool ClientBlockPrefix() { return CoopCore.Role != CoopRole.Client; } public static bool DayEndBlockPrefix(CEvent evt) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role != CoopRole.Client) { return true; } if (evt is CEventPlayer_OnDayEnded) { return false; } if (evt is CEventPlayer_OnDayStarted) { if (AllowNextDayStarted) { AllowNextDayStarted = false; return true; } return false; } CEventPlayer_AddCoin val = (CEventPlayer_AddCoin)(object)((evt is CEventPlayer_AddCoin) ? evt : null); if (val != null) { CoopCore.Instance?.ForwardContribution(1, val.m_CoinValue); return false; } CEventPlayer_ReduceCoin val2 = (CEventPlayer_ReduceCoin)(object)((evt is CEventPlayer_ReduceCoin) ? evt : null); if (val2 != null) { CoopCore.Instance?.ForwardContribution(2, val2.m_CoinValue); return false; } CEventPlayer_AddShopExp val3 = (CEventPlayer_AddShopExp)(object)((evt is CEventPlayer_AddShopExp) ? evt : null); if (val3 != null) { CoopCore.Instance?.ForwardContribution(3, val3.m_ExpValue); return false; } CEventPlayer_AddFame val4 = (CEventPlayer_AddFame)(object)((evt is CEventPlayer_AddFame) ? evt : null); if (val4 != null) { CoopCore.Instance?.ForwardContribution(4, val4.m_FameValue); return false; } CEventPlayer_ItemPriceChanged val5 = (CEventPlayer_ItemPriceChanged)(object)((evt is CEventPlayer_ItemPriceChanged) ? evt : null); if (val5 != null && !ApplyingRemotePrice) { CoopCore.Instance?.ForwardItemPrice(val5.m_ItemType, val5.m_Price); return true; } return true; } } } namespace CardShopCoop.Net { public interface ICoopTransport : IDisposable { ConcurrentQueue Incoming { get; } ConcurrentQueue Disconnects { get; } ConcurrentQueue Connects { get; } int ConnectionCount { get; } double TimeoutSeconds { get; } void Send(int connId, byte[] frame); void Broadcast(byte[] frame); void SendTransient(int connId, byte[] frame); void BroadcastTransient(byte[] frame); double SecondsSinceLastRecv(int connId); List ConnIds(); void Kick(int connId); void Stop(); void PumpMainThread(); } public enum MsgType : byte { Hello = 1, Welcome, SaveChunk, SaveDone, PlayerState, CoinSet, DayTime, Emote, Ping, Pong, Bye, ShelfDelta, ShelfRequest, PriceList, BundleChunk, BundleDone, ProgressSet, Activity, EconContrib, CardDelta, NpcState, ServeRequest, ServeStatus, CardShelfDelta, CardShelfRequest, CardPriceSet, RegisterState, ScanEcho, Roster, RelayState, RelayTag, ObjMoveDelta, ObjMoveRequest, BoxState, BoxRequest, OrderRequest, ShopName, ItemPriceContrib, LightState, PopState, FurnitureOrder, BoxRemoved, LicenseUnlock, LicenseState, StaffOp, StaffState, ShopOp, ShopState, SettingsOp, SettingsState, MarketState, ReportState, ContainerOp, ContainerState, TournamentState, GradingOp, GradingState, TradeOp, TradeState, TableState, CardBoxOp, CardBoxState, EnumSync, Toast, CatalogDigest } public struct InMsg { public int ConnId; public MsgType Type; public byte[] Payload; } public static class Msg { [ThreadStatic] private static MemoryStream _buildMs; [ThreadStatic] private static BinaryWriter _buildBw; public static byte[] Build(MsgType type, Action write = null) { if (_buildMs == null) { _buildMs = new MemoryStream(4096); _buildBw = new BinaryWriter(_buildMs); } MemoryStream buildMs = _buildMs; BinaryWriter buildBw = _buildBw; buildMs.SetLength(0L); buildMs.Position = 0L; buildBw.Write(0); buildBw.Write((byte)type); write?.Invoke(buildBw); buildBw.Flush(); long position = buildMs.Position; buildMs.Position = 0L; buildBw.Write((int)(position - 4)); buildBw.Flush(); return buildMs.ToArray(); } public static BinaryReader Reader(byte[] payload) { return new BinaryReader(new MemoryStream(payload, writable: false)); } public static byte[] Gzip(byte[] data) { using MemoryStream memoryStream = new MemoryStream(); using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionLevel.Fastest)) { gZipStream.Write(data, 0, data.Length); } return memoryStream.ToArray(); } public static byte[] Gunzip(byte[] data) { using MemoryStream stream = new MemoryStream(data, writable: false); using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(); gZipStream.CopyTo(memoryStream); return memoryStream.ToArray(); } public static void WriteCard(BinaryWriter bw, CardData card) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected I4, but got Unknown //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected I4, but got Unknown //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown bw.Write((int)card.expansionType); bw.Write((int)card.monsterType); bw.Write((int)card.borderType); bw.Write(card.isFoil); bw.Write(card.isDestiny); bw.Write(card.isChampionCard); bw.Write(card.isNew); bw.Write(card.cardGrade); bw.Write(card.gradedCardIndex); } public static CardData ReadCard(BinaryReader br) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown return new CardData { expansionType = (ECardExpansionType)br.ReadInt32(), monsterType = (EMonsterType)br.ReadInt32(), borderType = (ECardBorderType)br.ReadInt32(), isFoil = br.ReadBoolean(), isDestiny = br.ReadBoolean(), isChampionCard = br.ReadBoolean(), isNew = br.ReadBoolean(), cardGrade = br.ReadInt32(), gradedCardIndex = br.ReadInt32() }; } } public class SteamTransport : ICoopTransport, IDisposable { private struct Outgoing { public int ConnId; public byte[] Frame; } private const int Channel = 71; public byte[] KeepaliveFrame; private readonly bool _isHost; private readonly Dictionary _peers = new Dictionary(); private readonly Dictionary _ids = new Dictionary(); private readonly Dictionary _lastRecv = new Dictionary(); private readonly ConcurrentQueue _transientOutbox = new ConcurrentQueue(); private readonly ConcurrentQueue _reliableOutbox = new ConcurrentQueue(); private Outgoing? _stalled; private readonly List _transientScratch = new List(32); private readonly Dictionary _newestTransient = new Dictionary(32); private double _lastTransientRefusedLog = -10.0; private List _connIdsCache; private int _nextConnId = 1; private byte[] _readBuf = new byte[614400]; private float _keepaliveTimer; private bool _stopped; private Callback _cbSessionReq; private Callback _cbSessionFail; public CSteamID LobbyId = CSteamID.Nil; public ConcurrentQueue Incoming { get; } = new ConcurrentQueue(); public ConcurrentQueue Disconnects { get; } = new ConcurrentQueue(); public ConcurrentQueue Connects { get; } = new ConcurrentQueue(); public double TimeoutSeconds => 180.0; public int ConnectionCount => _peers.Count; public SteamTransport(bool isHost) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) _isHost = isHost; SteamNetworking.AllowP2PPacketRelay(true); _cbSessionReq = Callback.Create((DispatchDelegate)OnSessionRequest); _cbSessionFail = Callback.Create((DispatchDelegate)OnSessionFail); } private void OnSessionRequest(P2PSessionRequest_t req) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (_stopped) { return; } if (!((!_isHost) ? _ids.ContainsKey(req.m_steamIDRemote) : (LobbyId != CSteamID.Nil && IsLobbyMember(req.m_steamIDRemote)))) { CoopPlugin.Log.LogWarning((object)$"steam: rejected session from {req.m_steamIDRemote}"); return; } SteamNetworking.AcceptP2PSessionWithUser(req.m_steamIDRemote); if (!_ids.ContainsKey(req.m_steamIDRemote)) { AddPeer(req.m_steamIDRemote); } } private bool IsLobbyMember(CSteamID user) { //IL_0001: 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) //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) int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(LobbyId); for (int i = 0; i < numLobbyMembers; i++) { if (SteamMatchmaking.GetLobbyMemberByIndex(LobbyId, i) == user) { return true; } } return false; } private void OnSessionFail(P2PSessionConnectFail_t fail) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (_ids.TryGetValue(fail.m_steamIDRemote, out var value)) { CoopPlugin.Log.LogWarning((object)$"steam: session failed with {fail.m_steamIDRemote} (err {fail.m_eP2PSessionError})"); Kick(value); } } private int AddPeer(CSteamID sid) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) int num = _nextConnId++; _peers[num] = sid; _ids[sid] = num; _connIdsCache = null; _lastRecv[num] = Time.realtimeSinceStartupAsDouble; Connects.Enqueue(num); CoopPlugin.Log.LogInfo((object)$"steam: peer {sid} connected as {num}"); return num; } public void ConnectToHost(CSteamID host) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) AddPeer(host); } public void Send(int connId, byte[] frame) { _reliableOutbox.Enqueue(new Outgoing { ConnId = connId, Frame = frame }); } public void Broadcast(byte[] frame) { foreach (KeyValuePair peer in _peers) { _reliableOutbox.Enqueue(new Outgoing { ConnId = peer.Key, Frame = frame }); } } public void SendTransient(int connId, byte[] frame) { _transientOutbox.Enqueue(new Outgoing { ConnId = connId, Frame = frame }); } public void BroadcastTransient(byte[] frame) { foreach (KeyValuePair peer in _peers) { _transientOutbox.Enqueue(new Outgoing { ConnId = peer.Key, Frame = frame }); } } public void PumpMainThread() { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) if (_stopped) { return; } _transientScratch.Clear(); _newestTransient.Clear(); Outgoing result; while (_transientOutbox.TryDequeue(out result)) { int key = (result.ConnId << 8) | result.Frame[4]; if (_newestTransient.TryGetValue(key, out var value)) { _transientScratch[value] = default(Outgoing); } _newestTransient[key] = _transientScratch.Count; _transientScratch.Add(result); } for (int i = 0; i < _transientScratch.Count; i++) { Outgoing outgoing = _transientScratch[i]; if (outgoing.Frame != null && _peers.TryGetValue(outgoing.ConnId, out var value2) && !SteamNetworking.SendP2PPacket(value2, outgoing.Frame, (uint)outgoing.Frame.Length, (EP2PSend)1, 71)) { double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (realtimeSinceStartupAsDouble - _lastTransientRefusedLog >= 10.0) { _lastTransientRefusedLog = realtimeSinceStartupAsDouble; CoopPlugin.Log.LogWarning((object)$"steam: transient packet refused (size {outgoing.Frame.Length})"); } } } _transientScratch.Clear(); int num = 1048576; while (num > 0) { Outgoing result2; if (_stalled.HasValue) { result2 = _stalled.Value; _stalled = null; } else if (!_reliableOutbox.TryDequeue(out result2)) { break; } if (_peers.TryGetValue(result2.ConnId, out var value3)) { if (!SteamNetworking.SendP2PPacket(value3, result2.Frame, (uint)result2.Frame.Length, (EP2PSend)2, 71)) { _stalled = result2; break; } num -= result2.Frame.Length; } } _keepaliveTimer += Time.unscaledDeltaTime; if (_keepaliveTimer >= 2f && KeepaliveFrame != null && _peers.Count > 0) { _keepaliveTimer = 0f; foreach (KeyValuePair peer in _peers) { SteamNetworking.SendP2PPacket(peer.Value, KeepaliveFrame, (uint)KeepaliveFrame.Length, (EP2PSend)2, 71); } } uint num2 = default(uint); uint num3 = default(uint); CSteamID val = default(CSteamID); while (SteamNetworking.IsP2PPacketAvailable(ref num2, 71)) { if (num2 > _readBuf.Length) { _readBuf = new byte[num2]; } if (!SteamNetworking.ReadP2PPacket(_readBuf, (uint)_readBuf.Length, ref num3, ref val, 71)) { break; } if (num3 < 5) { continue; } if (!_ids.TryGetValue(val, out var value4)) { if (!_isHost || !(LobbyId != CSteamID.Nil) || !IsLobbyMember(val)) { continue; } value4 = AddPeer(val); } _lastRecv[value4] = Time.realtimeSinceStartupAsDouble; int num4 = BitConverter.ToInt32(_readBuf, 0); if (num4 == (int)(num3 - 4) && num4 >= 1) { byte[] array = new byte[num4 - 1]; Buffer.BlockCopy(_readBuf, 5, array, 0, num4 - 1); Incoming.Enqueue(new InMsg { ConnId = value4, Type = (MsgType)_readBuf[4], Payload = array }); } } } public double SecondsSinceLastRecv(int connId) { if (!_lastRecv.TryGetValue(connId, out var value)) { return double.MaxValue; } return Time.realtimeSinceStartupAsDouble - value; } public List ConnIds() { return _connIdsCache ?? (_connIdsCache = new List(_peers.Keys)); } public void Kick(int connId) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (_peers.TryGetValue(connId, out var value)) { SteamNetworking.CloseP2PSessionWithUser(value); _peers.Remove(connId); _ids.Remove(value); _connIdsCache = null; _lastRecv.Remove(connId); Disconnects.Enqueue(connId); } } public void Stop() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) if (_stopped) { return; } _stopped = true; foreach (KeyValuePair peer in _peers) { SteamNetworking.CloseP2PSessionWithUser(peer.Value); } _peers.Clear(); _ids.Clear(); _connIdsCache = null; if (LobbyId != CSteamID.Nil) { try { SteamMatchmaking.LeaveLobby(LobbyId); } catch { } LobbyId = CSteamID.Nil; } _cbSessionReq?.Dispose(); _cbSessionReq = null; _cbSessionFail?.Dispose(); _cbSessionFail = null; } public void Dispose() { Stop(); } } public class SteamLobby { public struct LobbyRow { public CSteamID Id; public string Name; public int Players; public int Max; public bool HasPw; public string Ver; } private Callback _cbCreated; private Callback _cbEnter; private Callback _cbJoinRequested; private CallResult _lobbyList; public CSteamID LobbyId = CSteamID.Nil; private bool _joining; private bool _pendingPublic; private string _pendingName = ""; private bool _pendingHasPw; public Action OnLobbyCreated; public Action OnEnteredLobby; public Action OnInviteAccepted; public Action OnError; public Action OnListUpdated; public readonly List Lobbies = new List(); public bool ListRefreshing { get; private set; } public unsafe void Init() { _cbCreated = Callback.Create((DispatchDelegate)delegate(LobbyCreated_t e) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if ((int)e.m_eResult != 1) { OnError?.Invoke("Steam lobby creation failed: " + ((object)(*(EResult*)(&e.m_eResult))/*cast due to .constrained prefix*/).ToString()); } else { LobbyId = new CSteamID(e.m_ulSteamIDLobby); SteamMatchmaking.SetLobbyData(LobbyId, "coopmod", "cardshopcoop"); SteamMatchmaking.SetLobbyData(LobbyId, "coopver", "1.0.6"); SteamMatchmaking.SetLobbyData(LobbyId, "name", string.IsNullOrEmpty(_pendingName) ? (CoopPlugin.PlayerName.Value + "'s shop") : _pendingName); SteamMatchmaking.SetLobbyData(LobbyId, "pw", _pendingHasPw ? "1" : "0"); OnLobbyCreated?.Invoke(LobbyId); } }); _lobbyList = CallResult.Create((APIDispatchDelegate)delegate(LobbyMatchList_t e, bool ioFail) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) ListRefreshing = false; Lobbies.Clear(); if (ioFail) { OnError?.Invoke("Steam lobby list failed"); } else { for (int i = 0; i < e.m_nLobbiesMatching; i++) { CSteamID lobbyByIndex = SteamMatchmaking.GetLobbyByIndex(i); if (!(lobbyByIndex == CSteamID.Nil)) { Lobbies.Add(new LobbyRow { Id = lobbyByIndex, Name = SteamMatchmaking.GetLobbyData(lobbyByIndex, "name"), Players = SteamMatchmaking.GetNumLobbyMembers(lobbyByIndex), Max = SteamMatchmaking.GetLobbyMemberLimit(lobbyByIndex), HasPw = (SteamMatchmaking.GetLobbyData(lobbyByIndex, "pw") == "1"), Ver = SteamMatchmaking.GetLobbyData(lobbyByIndex, "coopver") }); } } OnListUpdated?.Invoke(); } }); _cbEnter = Callback.Create((DispatchDelegate)delegate(LobbyEnter_t e) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (_joining) { _joining = false; LobbyId = new CSteamID(e.m_ulSteamIDLobby); CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(LobbyId); OnEnteredLobby?.Invoke(lobbyOwner); } }); _cbJoinRequested = Callback.Create((DispatchDelegate)delegate(GameLobbyJoinRequested_t e) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) OnInviteAccepted?.Invoke(e.m_steamIDLobby); }); } public bool SteamAvailable() { try { return SteamAPI.IsSteamRunning(); } catch { return false; } } public void Host(bool isPublic, string lobbyName, bool hasPassword) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) _pendingPublic = isPublic; _pendingName = lobbyName ?? ""; _pendingHasPw = hasPassword; SteamMatchmaking.CreateLobby((ELobbyType)((!isPublic) ? 1 : 2), 4); } public void RefreshList() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!ListRefreshing) { ListRefreshing = true; SteamMatchmaking.AddRequestLobbyListStringFilter("coopmod", "cardshopcoop", (ELobbyComparison)0); SteamMatchmaking.AddRequestLobbyListResultCountFilter(100); SteamMatchmaking.AddRequestLobbyListDistanceFilter((ELobbyDistanceFilter)3); SteamAPICall_t val = SteamMatchmaking.RequestLobbyList(); _lobbyList.Set(val, (APIDispatchDelegate)null); } } public void Join(CSteamID lobby) { //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) _joining = true; SteamMatchmaking.JoinLobby(lobby); } public void OpenInviteDialog() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (LobbyId != CSteamID.Nil) { SteamFriends.ActivateGameOverlayInviteDialog(LobbyId); } } public void Leave() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (LobbyId != CSteamID.Nil) { try { SteamMatchmaking.LeaveLobby(LobbyId); } catch { } LobbyId = CSteamID.Nil; } _joining = false; } } public class Transport : ICoopTransport, IDisposable { private class Conn { public int Id; public TcpClient Tcp; public NetworkStream Stream; public Thread ReadThread; public Thread WriteThread; public readonly ConcurrentQueue SendQueue = new ConcurrentQueue(); public readonly AutoResetEvent SendSignal = new AutoResetEvent(initialState: false); public volatile bool Alive = true; public long LastRecvTicksUtc = DateTime.UtcNow.Ticks; } private const int MaxFrame = 67108864; public byte[] KeepaliveFrame; private TcpListener _listener; private Thread _acceptThread; private volatile bool _running; private readonly Dictionary _conns = new Dictionary(); private readonly object _connsLock = new object(); private int _nextConnId = 1; public ConcurrentQueue Incoming { get; } = new ConcurrentQueue(); public ConcurrentQueue Disconnects { get; } = new ConcurrentQueue(); public ConcurrentQueue Connects { get; } = new ConcurrentQueue(); public double TimeoutSeconds => 60.0; public bool IsListening { get; private set; } public int ConnectionCount { get { lock (_connsLock) { return _conns.Count; } } } public void PumpMainThread() { } public void SendTransient(int connId, byte[] frame) { Send(connId, frame); } public void BroadcastTransient(byte[] frame) { Broadcast(frame); } public void StartHost(int port) { Stop(); _running = true; _listener = new TcpListener(IPAddress.Any, port); _listener.Start(); IsListening = true; _acceptThread = new Thread(AcceptLoop) { IsBackground = true, Name = "CoopAccept" }; _acceptThread.Start(); } private void AcceptLoop() { while (_running) { TcpClient tcpClient; try { tcpClient = _listener.AcceptTcpClient(); } catch { break; } ConfigureSocket(tcpClient); Conn conn = new Conn { Tcp = tcpClient, Stream = tcpClient.GetStream() }; lock (_connsLock) { conn.Id = _nextConnId++; _conns[conn.Id] = conn; } conn.ReadThread = new Thread((ThreadStart)delegate { ReadLoop(conn); }) { IsBackground = true, Name = "CoopRead" + conn.Id }; conn.ReadThread.Start(); conn.WriteThread = new Thread((ThreadStart)delegate { WriteLoop(conn); }) { IsBackground = true, Name = "CoopWrite" + conn.Id }; conn.WriteThread.Start(); StartKeepalive(conn); Connects.Enqueue(conn.Id); } } public int StartClient(string ip, int port, int timeoutMs = 6000) { Stop(); _running = true; TcpClient tcpClient = new TcpClient(); IAsyncResult asyncResult = tcpClient.BeginConnect(ip, port, null, null); if (!asyncResult.AsyncWaitHandle.WaitOne(timeoutMs)) { tcpClient.Close(); throw new TimeoutException($"No answer from {ip}:{port} after {timeoutMs / 1000}s"); } tcpClient.EndConnect(asyncResult); ConfigureSocket(tcpClient); Conn conn = new Conn { Id = 1, Tcp = tcpClient, Stream = tcpClient.GetStream() }; lock (_connsLock) { _conns[1] = conn; } conn.ReadThread = new Thread((ThreadStart)delegate { ReadLoop(conn); }) { IsBackground = true, Name = "CoopRead1" }; conn.ReadThread.Start(); conn.WriteThread = new Thread((ThreadStart)delegate { WriteLoop(conn); }) { IsBackground = true, Name = "CoopWrite1" }; conn.WriteThread.Start(); StartKeepalive(conn); return conn.Id; } private static void ConfigureSocket(TcpClient tcp) { tcp.NoDelay = true; tcp.SendTimeout = 5000; tcp.SendBufferSize = 262144; } private void StartKeepalive(Conn conn) { Thread thread = new Thread((ThreadStart)delegate { while (_running && conn.Alive) { Thread.Sleep(2000); byte[] keepaliveFrame = KeepaliveFrame; if (keepaliveFrame != null && conn.Alive) { conn.SendQueue.Enqueue(keepaliveFrame); conn.SendSignal.Set(); } } }); thread.IsBackground = true; thread.Name = "CoopKeepalive" + conn.Id; thread.Start(); } private void WriteLoop(Conn conn) { try { while (_running && conn.Alive) { if (!conn.SendQueue.TryDequeue(out var result)) { conn.SendSignal.WaitOne(500); } else { conn.Stream.Write(result, 0, result.Length); } } } catch { } DropConn(conn.Id); } private void ReadLoop(Conn conn) { byte[] array = new byte[4]; byte[] array2 = new byte[1]; try { while (_running && conn.Alive) { ReadExact(conn.Stream, array, 4); int num = BitConverter.ToInt32(array, 0); if (num < 1 || num > 67108864) { throw new IOException("Bad frame length " + num); } ReadExact(conn.Stream, array2, 1); byte[] array3 = new byte[num - 1]; ReadExact(conn.Stream, array3, num - 1); conn.LastRecvTicksUtc = DateTime.UtcNow.Ticks; Incoming.Enqueue(new InMsg { ConnId = conn.Id, Type = (MsgType)array2[0], Payload = array3 }); } } catch { } DropConn(conn.Id); } private static void ReadExact(NetworkStream s, byte[] buf, int count) { int num; for (int i = 0; i < count; i += num) { num = s.Read(buf, i, count - i); if (num <= 0) { throw new IOException("Connection closed"); } } } public void Send(int connId, byte[] frame) { Conn value; lock (_connsLock) { if (!_conns.TryGetValue(connId, out value)) { return; } } if (value.Alive) { value.SendQueue.Enqueue(frame); value.SendSignal.Set(); } } public void Broadcast(byte[] frame) { List list; lock (_connsLock) { list = new List(_conns.Keys); } foreach (int item in list) { Send(item, frame); } } public double SecondsSinceLastRecv(int connId) { Conn value; lock (_connsLock) { if (!_conns.TryGetValue(connId, out value)) { return double.MaxValue; } } return TimeSpan.FromTicks(DateTime.UtcNow.Ticks - value.LastRecvTicksUtc).TotalSeconds; } public List ConnIds() { lock (_connsLock) { return new List(_conns.Keys); } } public void Kick(int connId) { DropConn(connId); } private void DropConn(int connId) { Conn value; lock (_connsLock) { if (!_conns.TryGetValue(connId, out value)) { return; } _conns.Remove(connId); } if (value.Alive) { value.Alive = false; try { value.SendSignal.Set(); } catch { } try { value.Stream?.Close(); } catch { } try { value.Tcp?.Close(); } catch { } Disconnects.Enqueue(connId); } } public void Stop() { _running = false; IsListening = false; try { _listener?.Stop(); } catch { } _listener = null; List list; lock (_connsLock) { list = new List(_conns.Keys); } foreach (int item in list) { DropConn(item); } } public void Dispose() { Stop(); } } }