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.EventSystems; 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("1.0.37.0")] [assembly: AssemblyInformationalVersion("1.0.37+dd37610e424c21f00c01126ab0e098dd73f7733e")] [assembly: AssemblyProduct("CardShopCoop")] [assembly: AssemblyTitle("CardShopCoop")] [assembly: AssemblyVersion("1.0.37.0")] namespace CardShopCoop { public enum CoopRole { None, Host, Client } public class CoopCore : MonoBehaviour { private struct HeldPurchase { public InMsg Msg; public double At; } private struct ChargeVerdict { public bool Accepted; public double At; } private struct PendingCard { public bool IsAdd; public int Amount; public CardData Card; } private struct MyCardPrice { public CardData Card; public float Value; public bool Acked; public double LastSend; public int Attempts; } private struct MyItemPrice { public float Value; public double At; } private enum PurchaseGate { Process, Drop, Hold } public static bool GuestBorrowedWorld; public static bool HostServeKeyEnabled = false; 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 readonly FurnBoxSync _furnBoxes = new FurnBoxSync(); 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 _priceHeal; private readonly List> _priceBuf = new List>(); private readonly HashSet _priceSeenTypes = new HashSet(); 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 float _coinHeal; private float _progressHeal; private double _pendingReduceThisFrame; private readonly List _heldPurchases = new List(); private bool _deliveringHeld; private readonly Dictionary _chargeVerdicts = new Dictionary(); private const double VerdictTtl = 1.0; private const double VerdictDeclineTtl = 10.0; private readonly Dictionary _lastDeclineToast = new Dictionary(); 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 int _lastCardResyncHash; private float _cardResyncHeal; private float _cardPriceHealTimer = -2.1f; private int _lastCardPriceHash; private float _cardPriceHealBeat; private int _lastStockResyncHash; private float _stockResyncHeal; private readonly List> _cardPriceBuf = new List>(); private float _licenseSyncTimer = -3.7f; private double _lastLicenseBuyTime = -999.0; private string _lastLightJson; private float _lightHeal; private double _lastDayMirrorAt = -999.0; 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 Action _actCardPriceRetry; private Action _actFrameCardWork; private CustomerManager _cmSweep; private CustomerManager _cmSpray; private bool _renamerHandled; private TMP_Text _shopSign; private string _lastShopNameApplied; private int _heldBoxFrame = -1; private object _heldBoxA; private object _heldBoxB; private object _heldBoxC; public static bool ClientReloading; private float _reloadGrace; private readonly List _dispatchBuf = new List(64); private readonly HashSet _dispatchSeen = new HashSet(); private const int DispatchBudget = 256; private int _autoHostSlot = -1; private string _autoJoinIp; private int _autoPhase; private float _autoTimer; private static bool _enumLendWarned; public string HostPassword = ""; private string _joinPassword = ""; public CSteamID LastFailedLobby = CSteamID.Nil; private const int EnumBlobCap = 262144; private readonly Dictionary _enumSyncSentTo = new Dictionary(); private readonly Dictionary _enumSyncSentToPeer = new Dictionary(); private const int EnumSyncMaxSends = 2; private const int EnumSyncMaxSendsPerPeer = 5; private readonly List> _pendingKicks = new List>(); private readonly List _pendingCardDeltas = new List(); private readonly List> _pendingCardPrices = new List>(); private readonly List _cardDeltaOutbox = new List(); private const int CardDeltaBatchMax = 200; private bool _flushingCardDeltas; private readonly List _batchRelayBuf = new List(); private static bool _binderRefreshPending; private static readonly List _deltaLogBuf = new List(); private static int _deltaAppliedThisFrame; private readonly Dictionary _myCardPrices = new Dictionary(); private readonly List _cardPriceRetryKeys = new List(); private float _cardPriceRetryTimer; private const int MyCardPriceMax = 1024; private const int CardPriceMaxAttempts = 12; private const float CardPriceEpsilon = 0.0075f; private readonly Dictionary _myItemPriceEdits = new Dictionary(); private const int MyItemPriceMax = 256; private const double ItemPriceHoldSeconds = 6.0; private static InteractionPlayerController _deltaIpc; private static readonly Dictionary> _shownMonsters = new Dictionary>(); private static readonly HashSet _priceWarnedKeys = new HashSet(); private static readonly MethodInfo MiBinderResort = AccessTools.Method(typeof(CollectionBinderFlipAnimCtrl), "OnSortingMethodUpdated", (Type[])null, (Type[])null); private static readonly FieldInfo FiBinderIsBookOpen = AccessTools.Field(typeof(CollectionBinderFlipAnimCtrl), "m_IsBookOpen"); private static readonly FieldInfo FiBinderUI = AccessTools.Field(typeof(CollectionBinderFlipAnimCtrl), "m_CollectionBinderUI"); private static readonly FieldInfo FiBinderIsGradedAlbum = AccessTools.Field(typeof(CollectionBinderFlipAnimCtrl), "m_IsGradedCardAlbum"); private static readonly FieldInfo FiBinderExpansionType = AccessTools.Field(typeof(CollectionBinderFlipAnimCtrl), "m_ExpansionType"); private int _selfId = -1; private readonly HashSet _relayIds = new HashSet(); private static InventoryBase _inventory; 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 static readonly FieldInfo FiIsHoldBoxMode = AccessTools.Field(typeof(InteractionPlayerController), "m_IsHoldBoxMode"); 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 bool _eplProbed; private static PropertyInfo _eplAssetsProp; private static PropertyInfo _eplItemLibProp; private static PropertyInfo _eplRestockProp; 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 HashSet _catalogWarnedConns = new HashSet(); 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; } private bool ClientPreloadHold { get { if (ClientReloading) { return _reloadGrace <= 0f; } return false; } } 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 static int DispatchCost(InMsg m) { if (m.Type != MsgType.CardDeltaBatch) { return 1; } byte[] payload = m.Payload; if (payload == null || payload.Length < 4) { return 1; } int num = payload[0] | (payload[1] << 8) | (payload[2] << 16) | (payload[3] << 24); if (num < 1) { return 1; } if (num <= 200) { return num; } return 200; } 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() && !ClientReloading) { 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); Msg.WriteItemType(bw, (EItemType)type); }); }; PopulationSync.OnClientStructureChanged = delegate(int kind) { if (Role == CoopRole.Client && (kind == 2 || kind == 3)) { _cardShelves.InvalidateBaseline(); } }; _actCardPriceRetry = CardPriceRetryTick; _actFrameCardWork = FlushFrameCardWork; _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 && !ClientPreloadHold); } }; _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); }; _containers.RequestBoxResync = delegate { _boxes.ForceBroadcastNextTick(); }; _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); }; _furnBoxes.SendOp = delegate(Action w) { Send(1, MsgType.FurnBoxOp, w); }; _furnBoxes.BroadcastState = delegate(Action w) { Broadcast(MsgType.FurnBoxState, w); }; FurnBoxSync.IsLocallyCarried = delegate(InteractablePackagingBox_Shelf 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 _heldBoxB == box; } catch { return false; } }; _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); try { EnumLendState(); } catch { } } public static string EnumLendState() { try { if (!ModParity.HostEnumInstalled()) { return null; } if (!_enumLendWarned) { _enumLendWarned = true; CoopPlugin.Log.LogWarning((object)"CardShopCoop: your custom-card database is currently the HOST's synced copy from a co-op session. Your OWN solo modded saves may not load until you restore it (restore via the co-op window) and RESTART the game."); } return "custom-card database is the HOST's copy (co-op sync) - solo modded saves may not load; restore via the co-op window"; } catch { return null; } } public void JoinSteam(CSteamID lobby, string password = "") { //IL_0082: 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_00b3: 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; } if (ModParity.RestartRequiredForJoin) { ErrorLine = "the host's card database was installed on this PC - RESTART the game before joining"; return; } Role = CoopRole.Client; GuestBorrowedWorld = true; 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; } CardShopCoop.Util.EnumMap.Clear(); 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.37"); bw.Write(CoopPlugin.PlayerName.Value); bw.Write(_joinPassword ?? ""); bw.Write(ModParity.PluginHash()); bw.Write(ModParity.EnumHash()); bw.Write(ModParity.CardsHash()); WriteCappedList(bw, ModParity.PluginList()); WriteCappedList(bw, ModParity.CardsList()); byte[] array; try { array = Msg.Gzip(Encoding.UTF8.GetBytes(string.Join("\n", ModParity.EnumLines()))); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("enum lines for Hello: " + ex.Message)); array = Msg.Gzip(new byte[0]); } bw.Write(array.Length); bw.Write(array); }); } private static List SafeEnumLines() { try { return ModParity.EnumLines() ?? new List(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("enum lines: " + ex.Message)); return new List(); } } private static List SafeCardsList() { try { return ModParity.CardsList() ?? new List(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("cards list: " + ex.Message)); return new List(); } } private static byte[] GzipLines(List lines) { try { string[] array = (lines ?? new List()).ToArray(); byte[] bytes = Encoding.UTF8.GetBytes(string.Join("\n", array)); if (bytes.Length > 262144) { CoopPlugin.Log.LogWarning((object)("registry blob is OVER THE WIRE CAP: " + array.Length + " ids, " + bytes.Length + " bytes uncompressed vs a " + 262144 + "-byte cap - the other PC will IGNORE it and modded ids will not be translated this session (ids must already match)")); } return Msg.Gzip(bytes); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("registry blob: " + ex.Message)); return Msg.Gzip(new byte[0]); } } private static List ReadCappedEnumBlob(BinaryReader br, out string digest) { digest = "none"; List list = new List(); try { int num = br.ReadInt32(); if (num <= 0) { return list; } if (num > 262144) { int num2 = num; while (num2 > 0) { int num3 = br.ReadBytes(Math.Min(num2, 8192)).Length; if (num3 <= 0) { break; } num2 -= num3; } CoopPlugin.Log.LogWarning((object)("registry blob over cap (" + num + " bytes compressed, cap " + 262144 + ") - ignored; modded ids will not be translated from it")); return list; } byte[] array = br.ReadBytes(num); if (array.Length != num) { return list; } string text = GunzipCapped(array, 262144); if (text == null) { return list; } digest = Fnv(text).ToString("X8"); string[] array2 = text.Split(new char[1] { '\n' }); for (int i = 0; i < array2.Length; i++) { string text2 = array2[i].Trim(); if (text2.Length > 0) { list.Add(text2); } } } catch { } return list; } private static string GunzipCapped(byte[] data, int cap) { try { using MemoryStream stream = new MemoryStream(data, writable: false); using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(); byte[] array = new byte[8192]; int num; while ((num = gZipStream.Read(array, 0, array.Length)) > 0) { if (memoryStream.Length + num > cap) { CoopPlugin.Log.LogWarning((object)("registry blob unpacked OVER CAP (more than " + cap + " bytes from " + data.Length + " compressed) - ignored, NOT a vanilla peer")); return null; } memoryStream.Write(array, 0, num); } return Encoding.UTF8.GetString(memoryStream.ToArray()); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("registry blob could not be unpacked (" + data.Length + " bytes, not valid gzip: " + ex.Message + ") - ignored")); return null; } } private static List EnumConflicts(List theirs, List ours) { List list = new List(); if (theirs == null || theirs.Count == 0 || ours == null || ours.Count == 0) { return list; } Dictionary dictionary = EnumMap(theirs); Dictionary dictionary2 = EnumMap(ours); foreach (KeyValuePair item in dictionary) { if (dictionary2.TryGetValue(item.Key, out var value) && value != item.Value) { list.Add(item.Key + " -> yours " + item.Value + ", host " + value); } } list.Sort(StringComparer.Ordinal); return list; } private static Dictionary EnumMap(List lines) { Dictionary dictionary = new Dictionary(); foreach (string line in lines) { if (!string.IsNullOrEmpty(line)) { int num = line.LastIndexOf('='); if (num > 0 && num != line.Length - 1) { dictionary[line.Substring(0, num)] = line.Substring(num + 1); } } } return dictionary; } private static string DescribeConflicts(List conflicts) { int num = Math.Min(conflicts.Count, 5); string text = string.Join("; ", conflicts.GetRange(0, num).ToArray()); if (conflicts.Count > num) { text += $" (+{conflicts.Count - num} more)"; } if (text.Length > 400) { text = text.Substring(0, 397) + "..."; } return text; } private static string PeerSyncKey(string name, string enumDigest) { string text = (name ?? "").Trim().ToLowerInvariant(); string text2 = (string.IsNullOrEmpty(enumDigest) ? "none" : enumDigest); return ((text.Length > 0) ? ("n:" + text) : "anon") + "|" + text2; } private static void WriteCappedList(BinaryWriter bw, List list) { int num = ((list != null) ? Math.Min(list.Count, 256) : 0); bw.Write(num); for (int i = 0; i < num; i++) { bw.Write(list[i] ?? ""); } } private static List ReadCappedList(BinaryReader br) { List list = new List(); try { int num = br.ReadInt32(); if (num < 0) { num = 0; } if (num > 256) { num = 256; } for (int i = 0; i < num; i++) { list.Add(br.ReadString()); } } catch { } return list; } private static string DescribeModDiff(List theirs, List ours, string head, string diffLabel) { if (theirs == null || theirs.Count == 0 || ours == null || ours.Count == 0) { return null; } Dictionary dictionary = DiffMap(theirs); Dictionary dictionary2 = DiffMap(ours); List list = new List(); List list2 = new List(); List list3 = new List(); foreach (KeyValuePair item in dictionary2) { if (!dictionary.ContainsKey(item.Key)) { list.Add(item.Key); } } foreach (KeyValuePair item2 in dictionary) { if (!dictionary2.TryGetValue(item2.Key, out var value)) { list2.Add(item2.Key); } else if (value != item2.Value) { list3.Add(item2.Key + " (host " + value + " vs yours " + item2.Value + ")"); } } if (list.Count == 0 && list2.Count == 0 && list3.Count == 0) { return null; } list.Sort(StringComparer.Ordinal); list2.Sort(StringComparer.Ordinal); list3.Sort(StringComparer.Ordinal); List list4 = new List(); if (list.Count > 0) { list4.Add("you are missing: " + JoinCapped(list)); } if (list2.Count > 0) { list4.Add("you have extra: " + JoinCapped(list2)); } if (list3.Count > 0) { list4.Add(diffLabel + ": " + JoinCapped(list3)); } string text = head + string.Join(" | ", list4); if (text.Length > 700) { text = text.Substring(0, 697) + "..."; } return text; } private static Dictionary DiffMap(List entries) { Dictionary dictionary = new Dictionary(); foreach (string entry in entries) { if (!string.IsNullOrEmpty(entry)) { int num = entry.IndexOf('='); string key = ((num > 0) ? entry.Substring(0, num) : entry); string value = ((num > 0) ? entry.Substring(num + 1) : ""); dictionary[key] = value; } } return dictionary; } private static string JoinCapped(List items) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; for (int i = 0; i < items.Count; i++) { string text = ((num > 0) ? ", " : "") + items[i]; if (num > 0 && stringBuilder.Length + text.Length > 220) { break; } stringBuilder.Append(text); num++; } if (num < items.Count) { stringBuilder.Append($" (+{items.Count - num} more)"); } return stringBuilder.ToString(); } 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) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected I4, but got Unknown 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((int)Msg.ReadItemType(br)); } } } 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) { Msg.WriteItemType(bw, (EItemType)type); } } private static bool ApplyCardDelta(bool isAdd, int amount, CardData card, out bool relayAnyway) { relayAnyway = false; if (card.cardGrade != 0 && (card.cardGrade < 1 || card.cardGrade > 10) && !GradingInterop.Present) { CoopPlugin.Log.LogWarning((object)$"card delta: dropping corrupt graded card {CardIdent(card)} (grade {card.cardGrade}) - not applied (Grading Overhaul absent)"); return false; } GamePatches.ApplyingRemoteCards = true; try { if (!CardSetInstalledHere(card)) { relayAnyway = true; if (_priceWarnedKeys.Add("delta:" + CardPriceKey(card))) { CoopPlugin.Log.LogWarning((object)("card delta: " + CardIdent(card) + " is from a card set you don't have installed - skipped")); } return false; } if (isAdd) { if (card.cardGrade > 10) { GradingInterop.Remember(card); } CPlayerData.AddCard(card, amount); } else if (card.cardGrade > 0) { int num = 0; for (int i = 0; i < amount; i++) { if (!CPlayerData.HasGradedCardInAlbum(card)) { break; } CPlayerData.RemoveGradedCard(card, true); num++; } if (num == 0) { CoopPlugin.Log.LogWarning((object)$"graded remove: {CardIdent(card)} (grade {card.cardGrade}) not in this album - skipped (album mismatch?)"); return false; } } else { int cardAmount = CPlayerData.GetCardAmount(card); if (cardAmount < amount) { CoopPlugin.Log.LogWarning((object)$"card delta would drive {CardIdent(card)} negative (have {cardAmount}, remove {amount}) - skipped (card registry mismatch?)"); return false; } CPlayerData.ReduceCard(card, amount); } } finally { GamePatches.ApplyingRemoteCards = false; } _deltaAppliedThisFrame++; if (_deltaLogBuf.Count < 5) { _deltaLogBuf.Add(new PendingCard { IsAdd = isAdd, Amount = amount, Card = SnapshotCard(card) }); } _binderRefreshPending = true; return true; } private static void ReadCardDelta(BinaryReader br, out bool isAdd, out int amount, out CardData card) { isAdd = br.ReadBoolean(); amount = br.ReadInt32(); card = Msg.ReadCard(br); } private bool ApplyOrHoldCardDelta(bool isAdd, int amount, CardData card, out bool relayAnyway) { relayAnyway = false; if (!InGameLevel()) { _pendingCardDeltas.Add(new PendingCard { IsAdd = isAdd, Amount = amount, Card = card }); return false; } return ApplyCardDelta(isAdd, amount, card, out relayAnyway); } private static CardData SnapshotCard(CardData c) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_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_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_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 = c.expansionType, monsterType = c.monsterType, borderType = c.borderType, isFoil = c.isFoil, isDestiny = c.isDestiny, isChampionCard = c.isChampionCard, isNew = c.isNew, cardGrade = c.cardGrade, gradedCardIndex = c.gradedCardIndex }; } private static string CardPriceKey(CardData card) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected I4, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected I4, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected I4, but got Unknown if (card == null) { return null; } return (int)card.expansionType + ":" + (int)card.monsterType + ":" + (int)card.borderType + ":" + (card.isFoil ? 1 : 0) + (card.isDestiny ? 1 : 0) + (card.isChampionCard ? 1 : 0) + ":" + card.cardGrade; } internal static void ClearCardSetCache() { _shownMonsters.Clear(); } private static bool MonsterHasDataRowHere(ECardExpansionType expansion, EMonsterType monster) { //IL_0014: 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_001e: 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) if ((Object)(object)Inv() == (Object)null) { return false; } if (!_shownMonsters.TryGetValue(expansion, out var value)) { List shownMonsterList = InventoryBase.GetShownMonsterList(expansion); if (shownMonsterList == null || shownMonsterList.Count == 0) { return false; } value = new HashSet(shownMonsterList); _shownMonsters[expansion] = value; } return value.Contains(monster); } internal static bool CardSetInstalledHere(CardData card) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) try { if (card == null) { return false; } if ((int)card.expansionType == -1) { return false; } if ((int)card.monsterType == 0) { return false; } if (!Enum.IsDefined(typeof(ECardExpansionType), card.expansionType)) { return false; } if (CPlayerData.GetCardCollectedList(card.expansionType, card.isDestiny) == null) { return false; } return MonsterHasDataRowHere(card.expansionType, card.monsterType); } catch (Exception ex) { if (_priceWarnedKeys.Add("check:" + CardPriceKey(card))) { CoopPlugin.Log.LogWarning((object)("card set check: " + ex.Message)); } return false; } } private static string CardIdent(CardData c) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected I4, but got Unknown if (c == null) { return "(null card)"; } if ((int)c.expansionType < 7) { return ((object)Unsafe.As(ref c.monsterType)/*cast due to .constrained prefix*/).ToString(); } return ((object)Unsafe.As(ref c.expansionType)/*cast due to .constrained prefix*/).ToString() + "#" + (int)c.monsterType; } internal static void WarnRefusedCard(CardData c, string context) { //IL_002d: 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_0042: Expected I4, but got Unknown if (c != null && _priceWarnedKeys.Add(context + ":" + CardPriceKey(c))) { CoopPlugin.Log.LogWarning((object)$"{context}: {c.expansionType}#{(int)c.monsterType} is from a card set this PC doesn't have - the card could NOT be processed here"); } } private static bool ApplyRemoteCardPrice(CardData card, float price, string from, out float actual, out bool relayAnyway) { actual = price; relayAnyway = false; if (card == null) { return false; } string text = CardPriceKey(card); if (!CardSetInstalledHere(card)) { relayAnyway = true; if (_priceWarnedKeys.Add("set:" + text)) { CoopPlugin.Log.LogWarning((object)("card price for unknown card set skipped - other side has a content pack this PC doesn't (" + text + "; further ones logged once each)")); } return false; } if (card.cardGrade > 10) { if (!GradingInterop.Present) { relayAnyway = true; return false; } GradingInterop.Remember(card); } float num = float.NaN; try { num = CPlayerData.GetCardPrice(card); } catch { } try { CPlayerData.SetCardPrice(card, price); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("card price apply: " + ex.Message)); return false; } try { actual = CPlayerData.GetCardPrice(card); } catch (Exception ex2) { if (_priceWarnedKeys.Add("read:" + text)) { CoopPlugin.Log.LogWarning((object)("card price read-back: " + ex2.Message)); } actual = price; } if (Math.Abs(actual - price) > 0.0075f) { if (_priceWarnedKeys.Add("store:" + text)) { CoopPlugin.Log.LogWarning((object)$"card price {text}: the game's price store did not accept {price:F4} (it holds {actual:F4}) - modded expansion? (logged once per card)"); } relayAnyway = true; return false; } if (float.IsNaN(num) || Math.Abs(num - actual) > 0.0075f) { CoopPlugin.Log.LogInfo((object)$"card price applied: {text} = {actual:F2} (from {from})"); } return true; } private static void RefreshOpenBinder() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Invalid comparison between Unknown and I4 //IL_0192: 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_0182: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_deltaIpc == (Object)null) { _deltaIpc = Object.FindObjectOfType(); } CollectionBinderFlipAnimCtrl val = (((Object)(object)_deltaIpc != (Object)null) ? _deltaIpc.m_CollectionBinderFlipAnimCtrl : null); if ((Object)(object)val == (Object)null) { return; } val.SetCanUpdateSort(true); bool flag = FiBinderIsBookOpen != null && (bool)FiBinderIsBookOpen.GetValue(val); if (flag && MiBinderResort != null) { MiBinderResort.Invoke(val, new object[1] { false }); } if (!flag || !(FiBinderUI != null)) { return; } object? value = FiBinderUI.GetValue(val); CollectionBinderUI val2 = (CollectionBinderUI)((value is CollectionBinderUI) ? value : null); if (!((Object)(object)val2 != (Object)null)) { return; } bool num = FiBinderIsGradedAlbum != null && (bool)FiBinderIsGradedAlbum.GetValue(val); ECardExpansionType val3 = (ECardExpansionType)((!(FiBinderExpansionType != null)) ? (-1) : ((int)(ECardExpansionType)FiBinderExpansionType.GetValue(val))); if (num) { float num2 = 0f; for (int i = 0; i < CPlayerData.m_GradedCardInventoryList.Count; i++) { if (CPlayerData.m_GradedCardInventoryList[i].amount > 10) { CPlayerData.m_GradedCardInventoryList[i].amount = 10; } num2 += CPlayerData.GetCardMarketPrice(CPlayerData.GetGradedCardData(CPlayerData.m_GradedCardInventoryList[i])); } val2.SetTotalValue(num2); } else if ((int)val3 == 2) { val2.SetTotalValue(CPlayerData.GetCardAlbumTotalValue(val3, false) + CPlayerData.GetCardAlbumTotalValue(val3, true)); } else { val2.SetTotalValue(CPlayerData.GetCardAlbumTotalValue(val3, false)); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("binder relayout after card change failed: " + ex.Message)); } } 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, out var _); } if (_pendingCardDeltas.Count > 0) { CoopPlugin.Log.LogInfo((object)$"applied {_pendingCardDeltas.Count} card change(s) held during loading"); } _pendingCardDeltas.Clear(); bool flag = Role == CoopRole.Host; List> list = (flag ? new List>() : null); GamePatches.ApplyingRemotePrice = true; try { foreach (KeyValuePair pendingCardPrice in _pendingCardPrices) { float actual; bool relayAnyway2; bool flag2 = ApplyRemoteCardPrice(pendingCardPrice.Key, pendingCardPrice.Value, "load queue", out actual, out relayAnyway2); if (flag) { if (flag2) { list.Add(new KeyValuePair(pendingCardPrice.Key, actual)); } else if (relayAnyway2) { list.Add(pendingCardPrice); } } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("pending card price apply: " + ex.Message)); } finally { GamePatches.ApplyingRemotePrice = false; } _pendingCardPrices.Clear(); if (list != null) { for (int i = 0; i < list.Count; i++) { KeyValuePair kv = list[i]; Broadcast(MsgType.CardPriceSet, delegate(BinaryWriter bw) { Msg.WriteCard(bw, kv.Key); bw.Write(kv.Value); }); } } }); } private void RelayRawToOthers(int senderConn, MsgType type, byte[] payload) { if (Role != CoopRole.Host || _net == null || _net.ConnectionCount <= 1) { return; } FlushCardDeltaOutbox(); byte[] frame = Msg.Build(type, delegate(BinaryWriter bw) { if (payload != null) { bw.Write(payload); } }); foreach (int item in _net.ConnIds()) { if (item != senderConn) { _net.Send(item, frame); } } } private void RelayCardDeltaBatchToOthers(int senderConn, List deltas) { if (Role != CoopRole.Host || _net == null || _net.ConnectionCount <= 1 || deltas.Count == 0) { return; } FlushCardDeltaOutbox(); byte[] frame = Msg.Build(MsgType.CardDeltaBatch, delegate(BinaryWriter bw) { bw.Write(deltas.Count); for (int i = 0; i < deltas.Count; i++) { bw.Write(deltas[i].IsAdd); bw.Write(deltas[i].Amount); Msg.WriteCard(bw, deltas[i].Card); } }); foreach (int item in _net.ConnIds()) { if (item != senderConn) { _net.Send(item, frame); } } } private void FlushCardDeltaOutbox() { if (_cardDeltaOutbox.Count == 0 || _flushingCardDeltas) { return; } if (_net == null) { _cardDeltaOutbox.Clear(); return; } _flushingCardDeltas = true; try { int count = _cardDeltaOutbox.Count; int n; for (int i = 0; i < count; i += n) { int start = i; n = Math.Min(200, count - start); Broadcast(MsgType.CardDeltaBatch, delegate(BinaryWriter bw) { bw.Write(n); for (int j = start; j < start + n; j++) { PendingCard pendingCard = _cardDeltaOutbox[j]; bw.Write(pendingCard.IsAdd); bw.Write(pendingCard.Amount); Msg.WriteCard(bw, pendingCard.Card); } }); } if (count > 200) { CoopPlugin.Log.LogInfo((object)$"card deltas: {count} sent as {(count + 200 - 1) / 200} batch(es)"); } _cardDeltaOutbox.Clear(); } finally { _flushingCardDeltas = false; } } private void FlushFrameCardWork() { if (_deltaAppliedThisFrame > 0) { if (_deltaAppliedThisFrame <= 5) { for (int i = 0; i < _deltaLogBuf.Count; i++) { PendingCard pendingCard = _deltaLogBuf[i]; CoopPlugin.Log.LogInfo((object)string.Format("card delta applied: {0}{1} {2}{3}", pendingCard.IsAdd ? "+" : "-", pendingCard.Amount, CardIdent(pendingCard.Card), (pendingCard.Card.cardGrade > 0) ? $" (grade {pendingCard.Card.cardGrade})" : (pendingCard.Card.isFoil ? " (foil)" : ""))); } } else { CoopPlugin.Log.LogInfo((object)$"applied {_deltaAppliedThisFrame} card deltas"); } _deltaLogBuf.Clear(); _deltaAppliedThisFrame = 0; } if (_binderRefreshPending) { _binderRefreshPending = false; RefreshOpenBinder(); } FlushCardDeltaOutbox(); } 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); Msg.WriteItemType(bw, (EItemType)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); Msg.WriteItemType(bw, (EItemType)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; _cmSpray = null; _inventory = null; _renamerHandled = false; _catalogSent = false; if (ClientReloading) { _reloadGrace = 10f; } _playerTf = null; _playerCamTf = null; _playerIpc = null; if (((Scene)(ref scene)).name == "Title" && Role == CoopRole.Client && _net != null) { Shutdown("left the session"); } else if (((Scene)(ref scene)).name != "Title" && Role != CoopRole.None && _net != null && !ClientReloading) { Shutdown("left the session (world reloaded)"); } } private bool InGameLevel() { CGameManager instance = CSingleton.Instance; if ((Object)(object)instance != (Object)null) { return instance.m_IsGameLevel; } return false; } private static InventoryBase Inv() { if ((Object)(object)_inventory == (Object)null) { _inventory = Object.FindObjectOfType(); } return _inventory; } 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); _furnBoxes.HostTick(_dt, flag); } else { if (Role != CoopRole.Client) { return; } _trades.ClientTick(_dt, flag); _cardBoxes.ClientTick(_dt, flag && !ClientPreloadHold); _furnBoxes.ClientTick(_dt, flag && !ClientPreloadHold); _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_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_002c: 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 try { int num = CatalogCount(); int num2 = 17; for (int i = 0; i < num; i++) { RestockData val = CatalogAt(i); if (val != null) { num2 = num2 * 31 + ((val.itemType << 1) | val.isBigBox); } } return num2; } 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(); _furnBoxes.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(); _furnBoxes.ForceResend(); } private void RegisterMirrorTick() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) _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)); string text = _trades.PromptFor(nearestCounter) ?? _registerMirror.PromptFor(nearestCounter); if (text == null && Role == CoopRole.Client && _trades.AnyKnownOffer()) { text = $"a customer wants to trade - go to the counter and press {CoopPlugin.ServeKey.Value}"; } PromptLine = text ?? ""; } } private void NpcSweepTick() { if (!_renamerHandled) { _renamerHandled = true; ShopRenamer val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) { try { _shopSign = (TMP_Text)(object)val.m_ShopName; } catch { } ((Component)val).gameObject.SetActive(false); CoopPlugin.Log.LogInfo((object)"disabled shop-renamer trigger (host names the shop)"); if ((Object)(object)_shopSign != (Object)null && !string.IsNullOrEmpty(_lastShopNameApplied)) { try { _shopSign.text = _lastShopNameApplied; } catch { } } } } 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) { Msg.WriteItemType(bw, (EItemType)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; } public static void ForceExitHoldBox(Object heldBox) { CoopCore instance = Instance; InteractionPlayerController val = (((Object)(object)instance != (Object)null) ? instance._playerIpc : null); if ((Object)(object)val == (Object)null) { return; } try { object? obj = FiHoldItemBox?.GetValue(val); object obj2 = FiHoldBox?.GetValue(val); object obj3 = FiHoldBoxCard?.GetValue(val); if (obj == heldBox || obj2 == heldBox || obj3 == heldBox) { val.OnExitHoldBoxMode(); CoopPlugin.Log.LogInfo((object)"ForceExitHoldBox: released hold-box mode for a box being retired by reconcile"); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ForceExitHoldBox: " + ex.Message)); } } private void RecoverStuckHoldBox() { if ((Object)(object)_playerIpc == (Object)null) { return; } try { object obj = FiIsHoldBoxMode?.GetValue(_playerIpc); 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) { object? obj2 = FiHoldBox?.GetValue(_playerIpc); object? obj3 = ((obj2 is Object) ? obj2 : null); object? obj4 = FiHoldItemBox?.GetValue(_playerIpc); Object val = (Object)((obj4 is Object) ? obj4 : null); object? obj5 = FiHoldBoxCard?.GetValue(_playerIpc); Object val2 = (Object)((obj5 is Object) ? obj5 : null); if ((Object)obj3 == (Object)null && val == (Object)null && val2 == (Object)null) { _playerIpc.OnExitHoldBoxMode(); CoopPlugin.Log.LogInfo((object)"RecoverStuckHoldBox: cleared a stranded hold-box lock (no live held box)"); } } } catch { } } 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; } CardShopCoop.Util.EnumMap.Clear(); 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; } if (ModParity.RestartRequiredForJoin) { ErrorLine = "the host's card database was installed on this PC - RESTART the game before joining"; return; } CoopPlugin.LastJoinIP.Value = ip; Role = CoopRole.Client; GuestBorrowedWorld = true; 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 ForwardSprayHit(Vector3 pos, float range, int potency) { //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.SprayHit, delegate(BinaryWriter bw) { bw.Write(pos.x); bw.Write(pos.y); bw.Write(pos.z); bw.Write(range); bw.Write(potency); }); } } public void ForwardCardDelta(CardData card, int amount, bool isAdd) { if (Role != CoopRole.None && _net != null && card != null && amount > 0) { _cardDeltaOutbox.Add(new PendingCard { IsAdd = isAdd, Amount = amount, Card = SnapshotCard(card) }); } } public void SendCardDeltaTo(int connId, CardData card, int amount, bool isAdd) { if (Role == CoopRole.Host && _net != null && card != null && amount > 0) { Send(connId, MsgType.CardDelta, delegate(BinaryWriter bw) { bw.Write(isAdd); bw.Write(amount); Msg.WriteCard(bw, card); }); } } public void ForwardGradedRemoval(CardData card) { if (Role != CoopRole.None && _net != null && card != null && card.cardGrade > 0) { Broadcast(MsgType.GradedRemove, delegate(BinaryWriter bw) { 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) Msg.WriteItemType(bw, 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) { Msg.WriteItemType(bw, (EItemType)itemType); bw.Write(isBig); bw.Write(rdName); }); } else { Send(1, MsgType.LicenseUnlock, delegate(BinaryWriter bw) { Msg.WriteItemType(bw, (EItemType)itemType); bw.Write(isBig); bw.Write(rdName); }); } } private static int EplExtraCount() { try { if (!_eplProbed) { _eplProbed = true; _eplAssetsProp = AccessTools.TypeByName("EnhancedPrefabLoader.Core.EplRuntimeData")?.GetProperty("Assets", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = _eplAssetsProp?.GetValue(null); _eplItemLibProp = obj?.GetType().GetProperty("ItemLibrary", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _eplRestockProp = ((obj == null) ? null : _eplItemLibProp?.GetValue(obj))?.GetType().GetProperty("RestockEntries", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); CoopPlugin.Log.LogInfo((object)((_eplRestockProp != null) ? "EPL catalog bridge active (virtual restock entries visible)" : "EPL catalog bridge inactive (EPL absent or its internals changed) - vanilla catalog only")); } object obj2 = _eplAssetsProp?.GetValue(null); object obj3 = ((obj2 == null) ? null : _eplItemLibProp?.GetValue(obj2)); return ((obj3 == null) ? null : (_eplRestockProp?.GetValue(obj3) as ICollection))?.Count ?? 0; } catch { return 0; } } private static int CatalogCount() { int num = 0; try { num = Inv().m_StockItemData_SO.m_RestockDataList.Count; } catch { } return num + EplExtraCount(); } private static RestockData CatalogAt(int i) { try { return InventoryBase.GetRestockData(i); } catch { return null; } } private static int ResolveRestockIndex(int itemType, bool isBig, string name, out bool sizeDiffers) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Invalid comparison between Unknown and I4 sizeDiffers = false; try { int num = CatalogCount(); for (int i = 0; i < num; i++) { RestockData val = CatalogAt(i); if (val != null && (int)val.itemType == itemType && val.isBigBox == isBig) { return i; } } if (!string.IsNullOrEmpty(name)) { for (int j = 0; j < num; j++) { RestockData val2 = CatalogAt(j); if (val2 != null && val2.name == name && val2.isBigBox == isBig) { return j; } } } sizeDiffers = true; for (int k = 0; k < num; k++) { RestockData val3 = CatalogAt(k); if (val3 != null && (int)val3.itemType == itemType) { return k; } } if (!string.IsNullOrEmpty(name)) { for (int l = 0; l < num; l++) { RestockData val4 = CatalogAt(l); if (val4 != null && val4.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) { continue; } bool flag = false; try { flag = CPlayerData.GetIsItemLicenseUnlocked(num); } catch { } if (flag) { object? obj3 = FiPanelLicGrp?.GetValue(obj); object? obj4 = ((obj3 is GameObject) ? obj3 : null); if (obj4 != null) { ((GameObject)obj4).SetActive(false); } object? obj5 = FiPanelUIGrp?.GetValue(obj); object? obj6 = ((obj5 is GameObject) ? obj5 : null); if (obj6 != null) { ((GameObject)obj6).SetActive(true); } } } } catch { } } private void SendCatalogDigest() { try { int num = CatalogCount(); List entries = new List(num); for (int i = 0; i < num; i++) { RestockData val = CatalogAt(i); if (val != null && !string.IsNullOrEmpty(val.name)) { entries.Add(val); } } Send(1, MsgType.CatalogDigest, delegate(BinaryWriter bw) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) int num2 = Mathf.Min(entries.Count, 65535); bw.Write((ushort)num2); for (int j = 0; j < num2; j++) { Msg.WriteItemType(bw, entries[j].itemType); bw.Write(entries[j].isBigBox); bw.Write(Fnv(entries[j].name ?? "")); } }); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("catalog digest: " + ex.Message)); } } private void CompareCatalogs(BinaryReader br, int connId) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected I4, but got Unknown int num = br.ReadUInt16(); HashSet hashSet = new HashSet(); for (int i = 0; i < num; i++) { int type = (int)Msg.ReadItemType(br); bool big = br.ReadBoolean(); int nameHash = br.ReadInt32(); hashSet.Add(CatalogKey(type, big, nameHash)); } if ((Object)(object)Inv() == (Object)null) { return; } int num2 = CatalogCount(); int num3 = 0; int num4 = 0; List list = new List(); for (int j = 0; j < num2; j++) { RestockData val = CatalogAt(j); if (val == null || string.IsNullOrEmpty(val.name)) { continue; } if (hashSet.Contains(CatalogKey((int)val.itemType, val.isBigBox, Fnv(val.name)))) { num4++; continue; } num3++; if (list.Count < 6) { list.Add(val.name); } } int num5 = hashSet.Count - num4; if (num3 == 0 && num5 == 0) { CoopPlugin.Log.LogInfo((object)$"catalog check: identical ({num4} products)"); if (_catalogWarnedConns.Remove(connId)) { RegisterLine = "catalogs match now - the earlier warning was mod startup timing, all good"; RegisterLineTimer = 8f; Send(connId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write("catalogs match now - the earlier warning was mod startup timing, all good"); }); } return; } string value; string arg = (PeerNames.TryGetValue(connId, out value) ? value : "joiner"); string summary = $"heads-up: product catalogs differ ({num3} only on host, {num5} 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())) : ""))); _catalogWarnedConns.Add(connId); RegisterLine = summary; RegisterLineTimer = 10f; Send(connId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write(summary); }); } private void LogCatalogCandidates(string name) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected I4, but got Unknown try { if (string.IsNullOrEmpty(name)) { return; } string text = name.Split(new char[1] { ' ' })[0]; int num = CatalogCount(); List list = new List(); for (int i = 0; i < num; i++) { if (list.Count >= 8) { break; } RestockData val = CatalogAt(i); if (val != null && val.name != null && val.name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { list.Add($"{val.name} (type {(int)val.itemType}, big={val.isBigBox})"); } } 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) { Msg.WriteObjType(bw, (EObjectType)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) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected I4, but got Unknown 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) Msg.WriteItemType(bw, itemType); bw.Write(price); }); _myItemPriceEdits[(int)itemType] = new MyItemPrice { Value = price, At = Time.realtimeSinceStartupAsDouble }; TrimMyItemPriceEdits(); } } public void ForwardCardPrice(CardData card, float price) { if (Role == CoopRole.None || _net == null || card == null) { return; } Broadcast(MsgType.CardPriceSet, delegate(BinaryWriter bw) { Msg.WriteCard(bw, card); bw.Write(price); }); if (Role == CoopRole.Client) { string text = CardPriceKey(card); if (text != null) { _myCardPrices[text] = new MyCardPrice { Card = SnapshotCard(card), Value = price, Acked = false, LastSend = Time.realtimeSinceStartupAsDouble, Attempts = 1 }; TrimMyCardPrices(); } } } private void TrimMyCardPrices() { while (_myCardPrices.Count > 1024) { string text = null; double num = double.MaxValue; foreach (KeyValuePair myCardPrice in _myCardPrices) { if (myCardPrice.Value.Acked && myCardPrice.Value.LastSend < num) { num = myCardPrice.Value.LastSend; text = myCardPrice.Key; } } if (text == null) { foreach (KeyValuePair myCardPrice2 in _myCardPrices) { if (myCardPrice2.Value.LastSend < num) { num = myCardPrice2.Value.LastSend; text = myCardPrice2.Key; } } } if (text != null) { _myCardPrices.Remove(text); continue; } break; } } private void TrimMyItemPriceEdits() { while (_myItemPriceEdits.Count > 256) { int key = 0; bool flag = false; double num = double.MaxValue; foreach (KeyValuePair myItemPriceEdit in _myItemPriceEdits) { if (!flag || myItemPriceEdit.Value.At < num) { num = myItemPriceEdit.Value.At; key = myItemPriceEdit.Key; flag = true; } } if (flag) { _myItemPriceEdits.Remove(key); continue; } break; } } private bool HeldLocalItemPrice(int itemType, float incoming) { if (_myItemPriceEdits.Count == 0) { return false; } if (!_myItemPriceEdits.TryGetValue(itemType, out var value)) { return false; } if (Time.realtimeSinceStartupAsDouble - value.At >= 6.0) { _myItemPriceEdits.Remove(itemType); return false; } return Math.Abs(value.Value - incoming) > 0.0075f; } private void CardPriceRetryTick() { if (Role != CoopRole.Client || _net == null || _myCardPrices.Count == 0 || !InGameLevel()) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; _cardPriceRetryKeys.Clear(); foreach (KeyValuePair myCardPrice in _myCardPrices) { if (!myCardPrice.Value.Acked && realtimeSinceStartupAsDouble - myCardPrice.Value.LastSend >= 3.0) { _cardPriceRetryKeys.Add(myCardPrice.Key); } } for (int i = 0; i < _cardPriceRetryKeys.Count; i++) { string text = _cardPriceRetryKeys[i]; if (!_myCardPrices.TryGetValue(text, out var value)) { continue; } if (value.Attempts >= 12) { _myCardPrices.Remove(text); CoopPlugin.Log.LogWarning((object)("card price for " + text + " never confirmed - keeping the local value until the host's next price sync")); continue; } CardData card = value.Card; float value2 = value.Value; Broadcast(MsgType.CardPriceSet, delegate(BinaryWriter bw) { Msg.WriteCard(bw, card); bw.Write(value2); }); value.LastSend = realtimeSinceStartupAsDouble; value.Attempts++; _myCardPrices[text] = value; } } private void Shutdown(string reason) { if (_net != null) { try { Broadcast(MsgType.Bye, null); } catch { } _net.Stop(); _net = null; } _avatars.Clear(); PeerNames.Clear(); _heldPurchases.Clear(); _deliveringHeld = false; _chargeVerdicts.Clear(); _lastDeclineToast.Clear(); _enumSyncSentTo.Clear(); _enumSyncSentToPeer.Clear(); CardShopCoop.Util.EnumMap.Clear(); ClearCardSetCache(); _clientPriced.Clear(); _incomingPriced.Clear(); _cardDeltaOutbox.Clear(); _batchRelayBuf.Clear(); _flushingCardDeltas = false; _binderRefreshPending = false; _deltaLogBuf.Clear(); _deltaAppliedThisFrame = 0; _pendingCardDeltas.Clear(); _pendingCardPrices.Clear(); _myCardPrices.Clear(); _cardPriceRetryKeys.Clear(); _cardPriceRetryTimer = 0f; _myItemPriceEdits.Clear(); _priceWarnedKeys.Clear(); _dispatchBuf.Clear(); _dispatchSeen.Clear(); _saveBuf = null; _saveExpected = -1; _pendingSave = null; _bundleBuf = null; _bundleExpected = -1; _worldRequested = false; _hasLastPos = false; _lastCoinSent = double.MinValue; _lastPriceHash = 0; _cardPriceBuf.Clear(); _lastCardPriceHash = 0; _cardPriceHealBeat = 0f; _cardPriceHealTimer = -2.1f; _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 (!InGameLevel()) { GuestBorrowedWorld = false; } if (reason != null) { StatusLine = "Not connected (" + reason + ")"; CoopPlugin.Log.LogInfo((object)("Session ended: " + reason)); } } private void Send(int connId, MsgType type, Action write) { FlushCardDeltaOutbox(); _net?.Send(connId, Msg.Build(type, write)); } private void Broadcast(MsgType type, Action write) { FlushCardDeltaOutbox(); _net?.Broadcast(Msg.Build(type, write)); } private void BroadcastTransient(MsgType type, Action write) { FlushCardDeltaOutbox(); _net?.BroadcastTransient(Msg.Build(type, write)); } private void ResolveHeldPurchases(int connId, bool deliver) { if (_heldPurchases.Count == 0) { return; } bool flag = false; int num = 0; while (num < _heldPurchases.Count) { if (_heldPurchases[num].Msg.ConnId != connId) { num++; continue; } InMsg msg = _heldPurchases[num].Msg; _heldPurchases.RemoveAt(num); if (deliver) { _deliveringHeld = true; try { Dispatch(msg); } finally { _deliveringHeld = false; } continue; } CoopPlugin.Log.LogInfo((object)$"purchase ({msg.Type}) from conn {connId} cancelled - its charge was declined (shared wallet short)"); if (!flag) { flag = true; _lastDeclineToast[connId] = Time.realtimeSinceStartupAsDouble; Send(connId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write("not enough money - the purchase was cancelled"); }); } } } private PurchaseGate GateProduct(InMsg msg) { if (_deliveringHeld) { return PurchaseGate.Process; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (_chargeVerdicts.TryGetValue(msg.ConnId, out var value) && realtimeSinceStartupAsDouble - value.At < (value.Accepted ? 1.0 : 10.0)) { if (value.Accepted) { return PurchaseGate.Process; } CoopPlugin.Log.LogInfo((object)$"purchase ({msg.Type}) from conn {msg.ConnId} dropped - its charge was declined (shared wallet short)"); if (!_lastDeclineToast.TryGetValue(msg.ConnId, out var value2) || realtimeSinceStartupAsDouble - value2 >= 1.0) { _lastDeclineToast[msg.ConnId] = realtimeSinceStartupAsDouble; Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write("not enough money - the purchase was cancelled"); }); } return PurchaseGate.Drop; } _heldPurchases.Add(new HeldPurchase { Msg = msg, At = realtimeSinceStartupAsDouble }); return PurchaseGate.Hold; } private void PumpHeldPurchases() { if (_heldPurchases.Count == 0) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; while (_heldPurchases.Count > 0 && realtimeSinceStartupAsDouble - _heldPurchases[0].At > 1.5) { InMsg msg = _heldPurchases[0].Msg; _heldPurchases.RemoveAt(0); CoopPlugin.Log.LogInfo((object)$"held purchase ({msg.Type}) from conn {msg.ConnId} saw no charge within 1.5s - delivering (fail-open)"); _deliveringHeld = true; try { Dispatch(msg); } finally { _deliveringHeld = false; } } } internal static bool NativeTextInputFocused() { EventSystem current = EventSystem.current; GameObject val = ((current != null) ? current.currentSelectedGameObject : null); if ((Object)(object)val == (Object)null) { return false; } TMP_InputField component = val.GetComponent(); if ((Object)(object)component != (Object)null) { return component.isFocused; } return false; } private void Update() { //IL_0077: 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_011d: Unknown result type (might be due to invalid IL or missing references) //IL_08ce: Unknown result type (might be due to invalid IL or missing references) //IL_08df: Unknown result type (might be due to invalid IL or missing references) //IL_08f0: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: 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) Action result; while (_mainThread.TryDequeue(out result)) { try { result(); } catch (Exception ex) { CoopPlugin.Log.LogError((object)ex); } } if (GuestBorrowedWorld && Role == CoopRole.None && !InGameLevel()) { GuestBorrowedWorld = false; } if (Role == CoopRole.Client && InGameLevel()) { RecoverStuckHoldBox(); } 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 (serveTap && Role == CoopRole.Client && (CoopUI.TextFieldFocused || NativeTextInputFocused())) { CoopPlugin.Log.LogInfo((object)("serve key ignored (a text field has focus - " + (CoopUI.TextFieldFocused ? "co-op window" : "game input") + ")")); } if (Role == CoopRole.Client && _serveThrottle <= 0f && InGameLevel() && (serveTap || Input.GetKey(CoopPlugin.ServeKey.Value)) && !CoopUI.TextFieldFocused && !NativeTextInputFocused()) { _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.Host && HostServeKeyEnabled && _serveThrottle <= 0f && InGameLevel() && (serveTap || Input.GetKey(CoopPlugin.ServeKey.Value)) && !CoopUI.TextFieldFocused && !NativeTextInputFocused()) { _serveThrottle = 0.25f; Guarded("host-serve", delegate { //IL_0019: Unknown result type (might be due to invalid IL or missing references) Transform val2 = ResolvePlayer(); int num10 = (((Object)(object)val2 != (Object)null) ? RegisterServe.FindNearestCounter(val2.position, CoopPlugin.ServeReach.Value, !serveTap) : (-1)); if (num10 < 0 || !_trades.HasOffer(num10)) { if (num10 < 0) { if (serveTap) { RegisterLine = "walk up to the register first"; RegisterLineTimer = 2f; } } else { byte[] scanEcho; string text4 = RegisterServe.Serve(num10, CoopPlugin.PlayerName.Value, out scanEcho); if (!string.IsNullOrEmpty(text4)) { RegisterLine = text4; RegisterLineTimer = 8f; } } } }); } 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(); _lastCoinSent = double.MinValue; _lastProgressSent = long.MinValue; } } while (true) { if (!_net.Disconnects.TryDequeue(out var left)) { break; } string value; string text = (PeerNames.TryGetValue(left, out value) ? value : ("player " + left)); PeerNames.Remove(left); _avatars.Remove(left); if (Role == CoopRole.Host) { try { _boxes.HostReleaseRemoteCarried(); } catch { } try { _cardBoxes.HostReleaseRemoteCarried(); } catch { } try { _furnBoxes.HostReleaseRemoteCarried(); } catch { } _heldPurchases.RemoveAll((HeldPurchase h) => h.Msg.ConnId == left); _chargeVerdicts.Remove(left); _lastDeclineToast.Remove(left); 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; } } _pendingReduceThisFrame = 0.0; PumpHeldPurchases(); InMsg result3; while (_net != null && _net.Incoming.TryDequeue(out result3)) { _dispatchBuf.Add(result3); } 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); } } } } int num2 = 0; int num3 = 0; int num4 = 0; for (int num5 = 0; num5 < _dispatchBuf.Count; num5++) { if (_dispatchBuf[num5].Type == (MsgType)0) { num2 = num5 + 1; continue; } int num6 = DispatchCost(_dispatchBuf[num5]); if (num4 + num6 > 256 && num3 > 0) { break; } num3++; num4 += num6; num2 = num5 + 1; try { Dispatch(_dispatchBuf[num5]); } catch (Exception arg) { CoopPlugin.Log.LogError((object)$"Dispatch {_dispatchBuf[num5].Type}: {arg}"); } if (_net == null) { break; } } if (num2 >= _dispatchBuf.Count) { _dispatchBuf.Clear(); } else if (num2 > 0) { _dispatchBuf.RemoveRange(0, num2); } if (_net == null) { return; } float deltaTime = Time.deltaTime; if (_errLogCooldown > 0f) { _errLogCooldown -= deltaTime; } FlushPendingCardWork(); _dt = deltaTime; if (ClientReloading && _reloadGrace > 0f && InGameLevel()) { _reloadGrace -= deltaTime; if (_reloadGrace <= 0f) { ClientReloading = false; if ((Object)(object)_shopSign != (Object)null && !string.IsNullOrEmpty(_lastShopNameApplied)) { try { _shopSign.text = _lastShopNameApplied; } catch { } } } } 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); _cardPriceRetryTimer += deltaTime; if (_cardPriceRetryTimer >= 1f) { _cardPriceRetryTimer = 0f; Guarded("card-price-retry", _actCardPriceRetry); } _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 num7 = NpcSync.CountLocalActiveNpcs(); text3 = ((Role == CoopRole.Client) ? $" puppets={_npcs.PuppetCount} localNpcs={num7}(should be 0)" : $" liveNpcs={num7}"); } catch { } } CoopPlugin.Log.LogInfo((object)$"diag: role={Role} conns={_net.ConnectionCount} sentStates={_diagSent} recvStates={_diagRecvStates} inGame={InGameLevel()} pos={text2}{text3}"); } for (int num8 = _pendingKicks.Count - 1; num8 >= 0; num8--) { float num9 = _pendingKicks[num8].Value - deltaTime; if (num9 <= 0f) { int key = _pendingKicks[num8].Key; _pendingKicks.RemoveAt(num8); _net.Kick(key); } else { _pendingKicks[num8] = new KeyValuePair(_pendingKicks[num8].Key, num9); } } _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); } Guarded("frame-card-work", _actFrameCardWork); } 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected I4, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_09b6: Unknown result type (might be due to invalid IL or missing references) //IL_09bc: Unknown result type (might be due to invalid IL or missing references) //IL_09c7: Unknown result type (might be due to invalid IL or missing references) //IL_09c8: Unknown result type (might be due to invalid IL or missing references) //IL_09cb: Expected I4, but got Unknown //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_0538: Unknown result type (might be due to invalid IL or missing references) //IL_053b: Expected I4, but got Unknown //IL_0542: Unknown result type (might be due to invalid IL or missing references) //IL_0547: Unknown result type (might be due to invalid IL or missing references) //IL_054a: Expected I4, but got Unknown //IL_0551: Unknown result type (might be due to invalid IL or missing references) //IL_0556: Unknown result type (might be due to invalid IL or missing references) //IL_0559: 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 { _priceBuf.Clear(); HashSet priceSeenTypes = _priceSeenTypes; priceSeenTypes.Clear(); int num = CatalogCount(); int num2 = 17; for (int i = 0; i < num; i++) { RestockData val = CatalogAt(i); if (val == null) { continue; } int num3 = (int)val.itemType; if (priceSeenTypes.Add(num3)) { float num4 = 0f; try { num4 = CPlayerData.GetItemPrice(val.itemType, false); } catch { } if (num4 != 0f) { _priceBuf.Add(new KeyValuePair(num3, num4)); num2 = num2 * 31 + num3; num2 = num2 * 31 + num4.GetHashCode(); } } } _priceHeal += 3f; if (num2 != _lastPriceHash || _priceHeal >= 30f) { _lastPriceHash = num2; _priceHeal = 0f; Broadcast(MsgType.PriceList, delegate(BinaryWriter binaryWriter) { binaryWriter.Write(_priceBuf.Count); for (int j = 0; j < _priceBuf.Count; j++) { Msg.WriteItemType(binaryWriter, (EItemType)_priceBuf[j].Key); binaryWriter.Write(_priceBuf[j].Value); } }); } } 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 binaryWriter) { binaryWriter.Write(name); }); } } _econTimer += dt; if (_econTimer >= 0.5f) { _econTimer -= 0.5f; double coin = CPlayerData.m_CoinAmountDouble; _coinHeal += 0.5f; if (Math.Abs(coin - _lastCoinSent) > 0.0001 || _coinHeal >= 15f) { _lastCoinSent = coin; _coinHeal = 0f; float coinF = CPlayerData.m_CoinAmount; Broadcast(MsgType.CoinSet, delegate(BinaryWriter binaryWriter) { binaryWriter.Write(coin); binaryWriter.Write(coinF); }); } int exp = CPlayerData.m_ShopExpPoint; int level = CPlayerData.m_ShopLevel; int fame = CPlayerData.m_FamePoint; long num5 = ((long)level << 40) ^ ((long)fame << 20) ^ (uint)exp; _progressHeal += 0.5f; if (num5 != _lastProgressSent || _progressHeal >= 15f) { _lastProgressSent = num5; _progressHeal = 0f; Broadcast(MsgType.ProgressSet, delegate(BinaryWriter binaryWriter) { binaryWriter.Write(exp); binaryWriter.Write(level); binaryWriter.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 binaryWriter) { binaryWriter.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) { int num6 = 17; foreach (CardShelfSync.Entry item in full) { num6 = num6 * 31 + item.Key; num6 = num6 * 31 + (item.Occupied ? 1 : 0); CardData card = item.Card; if (item.Occupied && card != null) { num6 = num6 * 31 + card.monsterType; num6 = num6 * 31 + card.expansionType; num6 = num6 * 31 + card.borderType; num6 = num6 * 31 + card.cardGrade; num6 = num6 * 31 + card.gradedCardIndex; num6 = num6 * 31 + (card.isFoil ? 1 : 0); num6 = num6 * 31 + (card.isDestiny ? 1 : 0); num6 = num6 * 31 + (card.isChampionCard ? 1 : 0); } } _cardResyncHeal += 12f; if (num6 != _lastCardResyncHash || _cardResyncHeal >= 30f) { _lastCardResyncHash = num6; _cardResyncHeal = 0f; Broadcast(MsgType.CardShelfDelta, delegate(BinaryWriter bw2) { CardShelfSync.WriteEntries(bw2, full); }); } } } catch (Exception ex3) { CoopPlugin.Log.LogWarning((object)("card resync: " + ex3.Message)); } try { byte[] array = null; using (MemoryStream memoryStream = new MemoryStream()) { using BinaryWriter bw = new BinaryWriter(memoryStream); _world.BuildFullState(bw); array = memoryStream.ToArray(); } if (array != null && array.Length > 4) { int num7 = 17; for (int num8 = 0; num8 < array.Length; num8++) { num7 = num7 * 31 + array[num8]; } _stockResyncHeal += 12f; if (num7 != _lastStockResyncHash || _stockResyncHeal >= 36f) { _lastStockResyncHash = num7; _stockResyncHeal = 0f; byte[] bytes = array; Broadcast(MsgType.ShelfDelta, delegate(BinaryWriter binaryWriter) { binaryWriter.Write(bytes); }); } } } catch (Exception ex4) { CoopPlugin.Log.LogWarning((object)("stock resync: " + ex4.Message)); } } _cardPriceHealTimer += dt; if (_cardPriceHealTimer >= 3f && InGameLevel()) { _cardPriceHealTimer -= 3f; try { List list = _cardShelves.BuildFullState(); int num9 = 17; _cardPriceBuf.Clear(); foreach (CardShelfSync.Entry item2 in list) { if (item2.Occupied && item2.Card != null && (item2.Card.cardGrade <= 10 || GradingInterop.Present)) { float cardPrice; try { cardPrice = CPlayerData.GetCardPrice(item2.Card); } catch { continue; } if (!(cardPrice <= 0f)) { _cardPriceBuf.Add(new KeyValuePair(item2.Card, cardPrice)); num9 = num9 * 31 + item2.Key; num9 = num9 * 31 + cardPrice.GetHashCode(); } } } _cardPriceHealBeat += 3f; if ((num9 != _lastCardPriceHash || _cardPriceHealBeat >= 30f) && _cardPriceBuf.Count > 0) { _lastCardPriceHash = num9; _cardPriceHealBeat = 0f; for (int num10 = 0; num10 < _cardPriceBuf.Count; num10++) { KeyValuePair kv = _cardPriceBuf[num10]; Broadcast(MsgType.CardPriceSet, delegate(BinaryWriter binaryWriter) { Msg.WriteCard(binaryWriter, kv.Key); binaryWriter.Write(kv.Value); }); } } } catch (Exception ex5) { CoopPlugin.Log.LogWarning((object)("card price heal: " + ex5.Message)); } } _licenseSyncTimer += dt; if (_licenseSyncTimer >= 10f && InGameLevel()) { _licenseSyncTimer -= 10f; try { int num11 = CatalogCount(); List unlocked = new List(); for (int num12 = 0; num12 < num11; num12++) { bool flag = false; try { flag = CPlayerData.GetIsItemLicenseUnlocked(num12); } catch { } if (flag) { RestockData val2 = CatalogAt(num12); if (val2 != null) { unlocked.Add(val2); } } } bool scanner = CPlayerData.m_IsScannerRestockUnlocked; int num13 = 17; foreach (RestockData item3 in unlocked) { num13 = num13 * 31 + ((item3.itemType << 1) | item3.isBigBox); } num13 = num13 * 31 + (scanner ? 1 : 0); _licenseHeal += 10f; if (num13 != _lastLicenseHash || _licenseHeal >= 60f) { _lastLicenseHash = num13; _licenseHeal = 0f; Broadcast(MsgType.LicenseState, delegate(BinaryWriter binaryWriter) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) binaryWriter.Write(scanner); binaryWriter.Write((ushort)unlocked.Count); foreach (RestockData item4 in unlocked) { Msg.WriteItemType(binaryWriter, item4.itemType); binaryWriter.Write(item4.isBigBox); binaryWriter.Write(Fnv(item4.name ?? "")); } }); } } catch (Exception ex6) { CoopPlugin.Log.LogWarning((object)("license sync: " + ex6.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 binaryWriter) { binaryWriter.Write(day); binaryWriter.Write(hour); binaryWriter.Write(min); }); } private void Dispatch(InMsg msg) { //IL_1a9b: Unknown result type (might be due to invalid IL or missing references) //IL_0cec: Unknown result type (might be due to invalid IL or missing references) //IL_0cf1: Unknown result type (might be due to invalid IL or missing references) //IL_0d64: Unknown result type (might be due to invalid IL or missing references) //IL_1b32: Unknown result type (might be due to invalid IL or missing references) //IL_1ab6: Unknown result type (might be due to invalid IL or missing references) //IL_1b94: Unknown result type (might be due to invalid IL or missing references) //IL_1b9e: Expected O, but got Unknown //IL_11c5: Unknown result type (might be due to invalid IL or missing references) //IL_11cf: Expected O, but got Unknown //IL_11cf: Unknown result type (might be due to invalid IL or missing references) //IL_11d9: Expected O, but got Unknown //IL_1245: Unknown result type (might be due to invalid IL or missing references) //IL_124c: Expected I4, but got Unknown //IL_2aed: Unknown result type (might be due to invalid IL or missing references) //IL_2af4: Expected I4, but got Unknown //IL_1ff6: Unknown result type (might be due to invalid IL or missing references) //IL_2000: Expected I4, but got Unknown //IL_1b43: Unknown result type (might be due to invalid IL or missing references) //IL_1b45: Unknown result type (might be due to invalid IL or missing references) //IL_1b47: Unknown result type (might be due to invalid IL or missing references) //IL_1e1d: Unknown result type (might be due to invalid IL or missing references) //IL_1e27: Expected O, but got Unknown //IL_11e1: Unknown result type (might be due to invalid IL or missing references) //IL_11eb: Expected O, but got Unknown //IL_312d: Unknown result type (might be due to invalid IL or missing references) //IL_3137: Expected O, but got Unknown //IL_3256: Unknown result type (might be due to invalid IL or missing references) //IL_3260: Expected O, but got Unknown //IL_3269: Unknown result type (might be due to invalid IL or missing references) //IL_3273: Expected O, but got Unknown //IL_141e: Unknown result type (might be due to invalid IL or missing references) //IL_149a: Unknown result type (might be due to invalid IL or missing references) //IL_14a1: Expected I4, but got Unknown //IL_1c4b: Unknown result type (might be due to invalid IL or missing references) //IL_1c52: Expected I4, but got Unknown //IL_19f1: Unknown result type (might be due to invalid IL or missing references) //IL_19f6: Unknown result type (might be due to invalid IL or missing references) //IL_1f53: Unknown result type (might be due to invalid IL or missing references) //IL_1f5a: Expected I4, but got Unknown //IL_1af7: Unknown result type (might be due to invalid IL or missing references) //IL_1b01: Expected O, but got Unknown //IL_0f68: Unknown result type (might be due to invalid IL or missing references) //IL_0f72: Expected O, but got Unknown //IL_31fd: Unknown result type (might be due to invalid IL or missing references) //IL_3207: Expected O, but got Unknown //IL_1a7b: Unknown result type (might be due to invalid IL or missing references) //IL_332d: Unknown result type (might be due to invalid IL or missing references) //IL_30ad: Unknown result type (might be due to invalid IL or missing references) //IL_30b5: Unknown result type (might be due to invalid IL or missing references) //IL_30bd: Unknown result type (might be due to invalid IL or missing references) switch (msg.Type) { case MsgType.Hello: { if (Role != CoopRole.Host) { break; } using BinaryReader binaryReader15 = Msg.Reader(msg.Payload); string text6 = binaryReader15.ReadString(); if (text6 != "1.0.37") { RejectConn(msg.ConnId, "version mismatch - host runs CardShopCoop 1.0.37, you have " + text6); break; } string text7 = binaryReader15.ReadString(); string text8 = binaryReader15.ReadString(); string text9 = binaryReader15.ReadString(); string text10 = binaryReader15.ReadString(); string text11 = binaryReader15.ReadString(); List theirs = ReadCappedList(binaryReader15); List theirs2 = ReadCappedList(binaryReader15); string digest; List theirs3 = ReadCappedEnumBlob(binaryReader15, out digest); if (HostPassword.Length > 0 && text8 != HostPassword) { RejectConn(msg.ConnId, "wrong password"); break; } if (text9 != ModParity.PluginHash()) { string text12 = DescribeModDiff(theirs, ModParity.PluginList(), "mod set differs - ", "version differs"); RejectConn(msg.ConnId, text12 ?? "your mod set differs from the host's - both players need identical mods (same versions)"); break; } List list = SafeEnumLines(); if (list.Count == 0) { CoopPlugin.Log.LogWarning((object)("enum check: the host has NO modded enum ids to compare against, so " + text7 + " was not ID-checked at all (expected on a vanilla host; on a modded one see the 'enum identity source' line at startup)")); } List list2 = EnumConflicts(theirs3, list); if (list2.Count > 0) { CoopPlugin.Log.LogInfo((object)$"enum check: {text7} hash {text10} vs host {ModParity.EnumHash()} - {list2.Count} real conflict(s)"); if (!ModParity.RegistryFileMatchesRuntime()) { RejectConn(msg.ConnId, "your custom-card database conflicts with the host's, and the host's card-database FILE was changed this session so it no longer matches what the host is running - the HOST has to RESTART the game before it can be auto-synced to you (conflicting: " + DescribeConflicts(list2) + ")"); break; } string key = PeerSyncKey(text7, digest); _enumSyncSentTo.TryGetValue(key, out var value8); string key2 = PeerSyncKey(text7, null); _enumSyncSentToPeer.TryGetValue(key2, out var value9); if (value8 < 2 && value9 < 5) { bool flag6 = false; string text13 = null; try { string path = ModParity.EnumFilePath(); if (!File.Exists(path)) { text13 = "the host has no card-database file on disk to send"; } else { byte[] data = File.ReadAllBytes(path); byte[] gz = Msg.Gzip(data); Send(msg.ConnId, MsgType.EnumSync, delegate(BinaryWriter bw) { bw.Write(gz.Length); bw.Write(gz); }); _enumSyncSentTo[key] = value8 + 1; _enumSyncSentToPeer[key2] = value9 + 1; flag6 = true; } } catch (Exception ex5) { text13 = ex5.Message; CoopPlugin.Log.LogWarning((object)("enum sync send: " + ex5.Message)); } string text14 = DescribeConflicts(list2); RejectConn(reason: (!flag6) ? ("your custom-card database conflicts with the host's, and the host could not send its card database" + ((text13 != null) ? (" (" + text13 + ")") : "") + " - ask the host to check that AppData\\LocalLow\\OPNeonGames\\Card Shop Simulator\\PrefabLoader\\enum_values.json exists and is readable, or copy it across by hand (conflicting: " + text14 + ")") : ((value8 != 0) ? ("your card database is UNCHANGED since the last sync, so the host's copy never took effect - usually because the game was not fully closed (returning to the title screen is not enough), or because auto-sync is switched off on your side. It has been sent again: QUIT TO DESKTOP, start the game, then join (conflicting: " + text14 + ")") : ("your custom-card database conflicts with the host's - the host's copy has just been sent to you, and (unless you switched auto-sync off) saved on your PC with your old file backed up first. Now QUIT THE GAME TO DESKTOP, start it again, then join: the ids are only read while the game is booting, so nothing changes until you do (conflicting: " + text14 + ")")), connId: msg.ConnId); } else { bool flag7 = value8 >= 2; int num26 = (flag7 ? value8 : value9); RejectConn(msg.ConnId, "your card database still conflicts after " + num26 + " sync" + ((num26 == 1) ? "" : "s") + " from the host" + (flag7 ? " and has not changed at all" : " and keeps coming back DIFFERENT each time (your card ids are being re-minted every boot)") + ", so the host's copy is not being applied on your PC. Syncing normally DOES fix this - the ids usually differ only because the same content packs were installed in a different order. Check, in this order: (1) you fully quit the game to DESKTOP after the sync and started it again (returning to the title screen is not enough); (2) CardShopCoop's AutoSyncCardDatabase option is ON on YOUR side - with it off nothing is ever written to your PC; (3) failing that, copy the host's enum_values.json from AppData\\LocalLow\\OPNeonGames\\Card Shop Simulator\\PrefabLoader into the same folder on your PC by hand and restart (conflicting: " + DescribeConflicts(list2) + ")"); } } else if (text11 != ModParity.CardsHash()) { string text15 = DescribeModDiff(theirs2, ModParity.CardsList(), "custom cards differ - ", "ID differs"); RejectConn(msg.ConnId, text15 ?? "your custom cards differ from the host's - both players need the same custom cards installed (identical files + IDs), then restart. Share the exact card package (e.g. from CardForge)."); } else { PeerNames[msg.ConnId] = text7; _avatars.SetName(msg.ConnId, text7); StatusLine = "Hosting - " + text7 + " joined!"; CoopPlugin.Log.LogInfo((object)(text7 + " joined, sending world...")); SendWorldTo(msg.ConnId); BroadcastRoster(); } break; } case MsgType.Welcome: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader20 = Msg.Reader(msg.Payload); binaryReader20.ReadString(); string text17 = binaryReader20.ReadString(); _saveExpected = binaryReader20.ReadInt32(); _hostSlot = binaryReader20.ReadInt32(); _bundleExpected = binaryReader20.ReadInt32(); _selfId = binaryReader20.ReadByte(); string digest2; List list3 = ReadCappedEnumBlob(binaryReader20, out digest2); List list4 = ReadCappedEnumBlob(binaryReader20, out digest2); if (list3.Count == 0 && list4.Count == 0) { CoopPlugin.Log.LogWarning((object)"no registry from the host - modded ids will NOT be translated this session. Causes, in order of likelihood: the host is vanilla (fine, nothing to translate); or the host's registry was too big for the wire cap or could not be read/unpacked (see the host's log for 'registry blob over cap' / 'over-cap' - in that case ids must already match on both PCs)"); } CardShopCoop.Util.EnumMap.Build(list3, list4); _clientPriced.Clear(); _incomingPriced.Clear(); PeerNames[msg.ConnId] = text17; _avatars.SetName(msg.ConnId, text17); _saveBuf = new MemoryStream((_saveExpected > 0) ? _saveExpected : 1024); _bundleBuf = new MemoryStream((_bundleExpected > 0) ? _bundleExpected : 16); StatusLine = $"Downloading {text17}'s shop ({(_saveExpected + _bundleExpected) / 1024} KB)..."; break; } case MsgType.SaveChunk: { if (Role != CoopRole.Client || _saveBuf == null) { break; } using BinaryReader binaryReader30 = Msg.Reader(msg.Payload); binaryReader30.ReadInt32(); int count3 = binaryReader30.ReadInt32(); byte[] array4 = binaryReader30.ReadBytes(count3); _saveBuf.Write(array4, 0, array4.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[] array2 = _saveBuf.ToArray(); _saveBuf = null; if (_saveExpected >= 0 && array2.Length != _saveExpected) { ErrorLine = $"World download looked corrupted ({array2.Length}/{_saveExpected} bytes) - try again."; Shutdown("bad download"); break; } try { array2 = Msg.Gunzip(array2); } catch { ErrorLine = "World download could not be unpacked - try again."; Shutdown("bad download"); break; } if (array2.Length < 1024 || array2[0] != 123) { ErrorLine = "World download looked corrupted - try again."; Shutdown("bad download"); } else { _pendingSave = array2; StatusLine = "shop received - downloading mod data..."; } break; } case MsgType.BundleChunk: { if (Role != CoopRole.Client || _bundleBuf == null) { break; } using BinaryReader binaryReader23 = Msg.Reader(msg.Payload); binaryReader23.ReadInt32(); int count = binaryReader23.ReadInt32(); byte[] array3 = binaryReader23.ReadBytes(count); _bundleBuf.Write(array3, 0, array3.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[] array = ((_bundleBuf != null) ? _bundleBuf.ToArray() : new byte[0]); _bundleBuf = null; _worldRequested = true; StatusLine = "World received - loading..."; try { if (array.Length != 0) { array = Msg.Gunzip(array); } SidecarTransfer.ApplyBundle(array, _hostSlot, SaveTransfer.CoopSlot); } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("Sidecar apply failed (continuing): " + ex2.Message)); } ClientReloading = true; _reloadGrace = 0f; SaveTransfer.ApplyAndLoad(_pendingSave); _pendingSave = null; break; } case MsgType.ShelfDelta: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br2 = Msg.Reader(msg.Payload)) { _world.ApplyRemote(WorldSync.ReadEntries(br2)); break; } } break; case MsgType.ShelfRequest: { if (Role != CoopRole.Host || !InGameLevel()) { break; } using BinaryReader br19 = Msg.Reader(msg.Payload); List entries3 = WorldSync.ReadEntries(br19); _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 binaryReader12 = Msg.Reader(msg.Payload); int num16 = binaryReader12.ReadInt32(); GamePatches.ApplyingRemotePrice = true; try { _incomingPriced.Clear(); int num17 = 0; for (int num18 = 0; num18 < num16; num18++) { int localId; bool num19 = CardShopCoop.Util.EnumMap.TryFromWire(EnumKind.ItemType, binaryReader12.ReadInt32(), out localId); float num20 = binaryReader12.ReadSingle(); if (!num19) { continue; } _incomingPriced.Add(localId); if (localId < 0 || localId > 500000 || HeldLocalItemPrice(localId, num20)) { continue; } float num21 = 0f; try { num21 = CPlayerData.GetItemPrice((EItemType)localId, false); } catch { } if (Math.Abs(num21 - num20) > 0.0001f) { try { CPlayerData.SetItemPrice((EItemType)localId, num20); num17++; } catch { } } } if (num17 > 0) { CoopPlugin.Log.LogInfo((object)$"price apply: {num17} price(s) updated from host"); } foreach (int item in _clientPriced) { if (_incomingPriced.Contains(item) || item < 0 || item > 500000 || HeldLocalItemPrice(item, 0f)) { continue; } float num22 = 0f; try { num22 = CPlayerData.GetItemPrice((EItemType)item, false); } catch { } if (num22 != 0f) { try { CPlayerData.SetItemPrice((EItemType)item, 0f); } catch { } } } HashSet clientPriced = _clientPriced; _clientPriced = _incomingPriced; _incomingPriced = clientPriced; break; } finally { GamePatches.ApplyingRemotePrice = false; } } case MsgType.PlayerState: { using BinaryReader binaryReader10 = Msg.Reader(msg.Payload); Vector3 pos = new Vector3(binaryReader10.ReadSingle(), binaryReader10.ReadSingle(), binaryReader10.ReadSingle()); float yaw = binaryReader10.ReadSingle(); float speed = binaryReader10.ReadSingle(); byte hold = binaryReader10.ReadByte(); ReadHoldPayload(binaryReader10, 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 value3)) { _avatars.SetName(msg.ConnId, value3); } 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 value4; string text3 = (PeerNames.TryGetValue(msg.ConnId, out value4) ? value4 : ("player " + msg.ConnId)); CoopPlugin.Log.LogInfo((object)("Position link active with " + text3)); if (Role == CoopRole.Host) { StatusLine = "Hosting - " + text3 + " is in your shop!"; } } break; } case MsgType.CoinSet: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader6 = Msg.Reader(msg.Payload); double num13 = binaryReader6.ReadDouble(); float num14 = binaryReader6.ReadSingle(); if (!_loggedEconLink) { _loggedEconLink = true; CoopPlugin.Log.LogInfo((object)"Economy link active (host wallet mirrored)"); } if (Math.Abs(CPlayerData.m_CoinAmountDouble - num13) > 0.0001) { CEventManager.QueueEvent((CEvent)new CEventPlayer_SetCoin(num14, num13)); } break; } case MsgType.DayTime: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader3 = Msg.Reader(msg.Payload); int num5 = binaryReader3.ReadInt32(); int num6 = binaryReader3.ReadInt32(); int num7 = binaryReader3.ReadInt32(); if (!_loggedTimeLink) { _loggedTimeLink = true; CoopPlugin.Log.LogInfo((object)$"Time link active (Day {num5} {num6:00}:{num7:00})"); } HostTimeLine = $"Day {num5 + 1} {num6:00}:{num7:00}"; bool flag2 = num5 != CPlayerData.m_CurrentDay; CPlayerData.m_CurrentDay = num5; CPlayerData.m_IsShopOnceOpen = true; try { if ((Object)(object)_lightManager == (Object)null) { _lightManager = Object.FindObjectOfType(); } if (!((Object)(object)_lightManager != (Object)null)) { break; } if (flag2 && InGameLevel() && MiDayReset != null) { try { ReportSync.CloseClientReport(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("day change: closing stale report: " + ex.Message)); } GamePatches.AllowNextDayStarted = true; _lastDayMirrorAt = Time.realtimeSinceStartupAsDouble; ((MonoBehaviour)_lightManager).StartCoroutine((IEnumerator)MiDayReset.Invoke(_lightManager, null)); CoopPlugin.Log.LogInfo((object)$"Mirroring host day change -> Day {num5}"); } else { FiTimeHour?.SetValue(_lightManager, num6); FiTimeMin?.SetValue(_lightManager, num7); FiTimeMinFloat?.SetValue(_lightManager, (float)num7); FiHasDayEnded?.SetValue(_lightManager, false); } break; } catch { break; } } case MsgType.ProgressSet: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader29 = Msg.Reader(msg.Payload); int num50 = binaryReader29.ReadInt32(); int num51 = binaryReader29.ReadInt32(); int num52 = binaryReader29.ReadInt32(); int shopLevel = CPlayerData.m_ShopLevel; CPlayerData.m_ShopLevel = num51; CEventManager.QueueEvent((CEvent)new CEventPlayer_SetShopExp(num50)); CEventManager.QueueEvent((CEvent)new CEventPlayer_SetFame(num52)); if (num51 > shopLevel) { CEventManager.QueueEvent((CEvent)new CEventPlayer_ShopLeveledUp(num51)); } break; } case MsgType.Emote: _avatars.ShowEmote(msg.ConnId); RelayTagToOthers(msg.ConnId, 0); break; case MsgType.Activity: { int num45 = -1; try { using BinaryReader binaryReader27 = Msg.Reader(msg.Payload); binaryReader27.ReadByte(); num45 = (int)Msg.ReadItemType(binaryReader27); } catch { } _avatars.ShowTag(msg.ConnId, "opening a pack!", 3f); _avatars.ShowPackOpen(msg.ConnId, num45); RelayTagToOthers(msg.ConnId, 1, num45); break; } case MsgType.Roster: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader21 = Msg.Reader(msg.Payload); int num36 = binaryReader21.ReadByte(); HashSet seen = new HashSet(); for (int num37 = 0; num37 < num36; num37++) { int num38 = binaryReader21.ReadByte(); string text18 = binaryReader21.ReadString(); if (num38 != _selfId) { seen.Add(num38); _rosterNames[num38] = text18; if (_relayIds.Add(num38)) { CoopPlugin.Log.LogInfo((object)("peer in shop: " + text18)); } _avatars.SetName(1000 + num38, text18); } } _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 binaryReader19 = Msg.Reader(msg.Payload); int num35 = binaryReader19.ReadByte(); Vector3 pos2 = default(Vector3); ((Vector3)(ref pos2))..ctor(binaryReader19.ReadSingle(), binaryReader19.ReadSingle(), binaryReader19.ReadSingle()); float yaw2 = binaryReader19.ReadSingle(); float speed2 = binaryReader19.ReadSingle(); byte b3 = binaryReader19.ReadByte(); ReadHoldPayload(binaryReader19, b3, out var types, out var cards); if (num35 != _selfId) { _avatars.UpdateState(1000 + num35, pos2, yaw2, speed2, b3, types, cards); if (_rosterNames.TryGetValue(num35, out var value10)) { _avatars.SetName(1000 + num35, value10); } } break; } case MsgType.RelayTag: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader17 = Msg.Reader(msg.Payload); int num27 = binaryReader17.ReadByte(); byte b2 = binaryReader17.ReadByte(); int packIndex = -1; try { packIndex = (int)Msg.ReadItemType(binaryReader17); } catch { } if (num27 != _selfId) { if (b2 == 0) { _avatars.ShowEmote(1000 + num27); break; } _avatars.ShowTag(1000 + num27, "opening a pack!", 3f); _avatars.ShowPackOpen(1000 + num27, packIndex); } break; } case MsgType.CardDelta: { using (BinaryReader br12 = Msg.Reader(msg.Payload)) { ReadCardDelta(br12, out var isAdd, out var amount, out var card2); if (!ApplyOrHoldCardDelta(isAdd, amount, card2, out var relayAnyway2) && !relayAnyway2) { break; } } RelayRawToOthers(msg.ConnId, msg.Type, msg.Payload); break; } case MsgType.CardDeltaBatch: { bool flag10 = Role == CoopRole.Host && _net != null && _net.ConnectionCount > 1; _batchRelayBuf.Clear(); int num46 = 0; int num47 = 0; int num48; using (BinaryReader binaryReader28 = Msg.Reader(msg.Payload)) { num48 = binaryReader28.ReadInt32(); if (num48 < 0 || num48 > 200) { CoopPlugin.Log.LogWarning((object)$"card delta batch: bogus count {num48} - dropped"); break; } for (int num49 = 0; num49 < num48; num49++) { bool isAdd2 = false; int amount2 = 0; CardData card3 = null; try { ReadCardDelta(binaryReader28, out isAdd2, out amount2, out card3); } catch (Exception ex8) { CoopPlugin.Log.LogWarning((object)$"card delta batch: payload unreadable at delta {num49 + 1}/{num48} ({ex8.Message}) - the rest of the batch is lost"); break; } CardData card4 = (flag10 ? SnapshotCard(card3) : null); bool relayAnyway4 = false; bool flag11; try { flag11 = ApplyOrHoldCardDelta(isAdd2, amount2, card3, out relayAnyway4); } catch (Exception ex9) { CoopPlugin.Log.LogWarning((object)$"card delta batch: delta {num49 + 1}/{num48} failed to apply ({ex9.Message}) - skipped"); continue; } if (flag11 || relayAnyway4) { if (flag11) { num46++; } else { num47++; } if (flag10) { _batchRelayBuf.Add(new PendingCard { IsAdd = isAdd2, Amount = amount2, Card = card4 }); } } } } if (num46 == num48 && num47 == 0 && num48 > 0) { RelayRawToOthers(msg.ConnId, msg.Type, msg.Payload); } else if (_batchRelayBuf.Count > 0) { RelayCardDeltaBatchToOthers(msg.ConnId, _batchRelayBuf); } break; } case MsgType.GradedRemove: { using (BinaryReader br29 = Msg.Reader(msg.Payload)) { CardData val8 = Msg.ReadCard(br29); if (val8 == null) { break; } if (!InGameLevel()) { _pendingCardDeltas.Add(new PendingCard { IsAdd = false, Amount = 1, Card = val8 }); break; } if (!ApplyCardDelta(isAdd: false, 1, val8, out var relayAnyway3) && !relayAnyway3) { break; } } RelayRawToOthers(msg.ConnId, msg.Type, msg.Payload); break; } case MsgType.NpcState: if (Role == CoopRole.Client) { using (BinaryReader br27 = Msg.Reader(msg.Payload)) { _npcs.ApplyBatch(br27, InGameLevel()); break; } } break; case MsgType.CardShelfDelta: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br22 = Msg.Reader(msg.Payload)) { _cardShelves.ApplyRemote(CardShelfSync.ReadEntries(br22)); 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 br14 = Msg.Reader(msg.Payload)) { _boxes.ClientApply(BoxSync.ReadEntries(br14)); 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()) { break; } using BinaryReader binaryReader9 = Msg.Reader(msg.Payload); EObjectType val3 = Msg.ReadObjType(binaryReader9); Vector3 val4 = default(Vector3); ((Vector3)(ref val4))..ctor(binaryReader9.ReadSingle(), binaryReader9.ReadSingle(), binaryReader9.ReadSingle()); Quaternion val5 = default(Quaternion); ((Quaternion)(ref val5))..ctor(binaryReader9.ReadSingle(), binaryReader9.ReadSingle(), binaryReader9.ReadSingle(), binaryReader9.ReadSingle()); string value2; string arg = (PeerNames.TryGetValue(msg.ConnId, out value2) ? value2 : "player"); if (GateProduct(msg) != PurchaseGate.Process) { break; } float refund = 0f; try { FurniturePurchaseData furniturePurchaseData = InventoryBase.GetFurniturePurchaseData(val3); if (furniturePurchaseData != null) { refund = furniturePurchaseData.price; } } catch { } if ((Object)(object)InventoryBase.GetSpawnInteractableObjectPrefab(val3) == (Object)null) { CoopPlugin.Log.LogWarning((object)$"{arg} bought furniture {val3} not in host catalog - refunding {refund:F0}"); if (refund > 0f && refund < 100000f) { CEventManager.QueueEvent((CEvent)new CEventPlayer_AddCoin(refund, false)); } Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write((refund > 0f) ? $"that furniture isn't in the host's catalog - refunded ${refund:F0}" : "that furniture isn't in the host's catalog - nothing was delivered"); }); break; } CoopPlugin.Log.LogInfo((object)$"{arg} bought furniture: {val3}"); try { ShelfManager.SpawnInteractableObjectInPackageBox(val3, val4, val5); break; } catch (Exception ex3) { CoopPlugin.Log.LogWarning((object)("furniture spawn failed on host: " + ex3.Message)); if (refund > 0f && refund < 100000f) { CEventManager.QueueEvent((CEvent)new CEventPlayer_AddCoin(refund, false)); } Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write((refund > 0f) ? $"furniture failed to deliver on the host - refunded ${refund:F0}" : "furniture failed to deliver on the host"); }); break; } } case MsgType.BoxRequest: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br4 = Msg.Reader(msg.Payload)) { _boxes.HostApplyRequest(BoxSync.ReadEntries(br4)); break; } } break; case MsgType.OrderRequest: { if (Role != CoopRole.Host || !InGameLevel()) { break; } using BinaryReader binaryReader2 = Msg.Reader(msg.Payload); int num = (int)Msg.ReadItemType(binaryReader2); 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"); if (GateProduct(msg) != PurchaseGate.Process) { break; } 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 = CatalogCount(); } 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 content packs don't include this product - match your pack files" : "the host's modded catalog hasn't finished loading (or EPL is missing on the host) - wait a minute and try again, and check the host's BepInEx log"); 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 binaryReader31 = Msg.Reader(msg.Payload)) { RegisterLine = binaryReader31.ReadString(); RegisterLineTimer = 8f; CoopPlugin.Log.LogInfo((object)("host says: " + RegisterLine)); break; } } break; case MsgType.CatalogDigest: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br32 = Msg.Reader(msg.Payload)) { CompareCatalogs(br32, msg.ConnId); break; } } break; case MsgType.BoxRemoved: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader binaryReader26 = Msg.Reader(msg.Payload)) { int num43 = binaryReader26.ReadInt32(); int num44 = (int)Msg.ReadItemType(binaryReader26); string value11; string arg2 = (PeerNames.TryGetValue(msg.ConnId, out value11) ? value11 : "player"); CoopPlugin.Log.LogInfo((object)$"{arg2} trashed box id {num43} ({(object)(EItemType)num44})"); _boxes.HostApplyRemoval(num43, num44, msg.ConnId); break; } } break; case MsgType.LicenseUnlock: { if (!InGameLevel()) { break; } using BinaryReader binaryReader25 = Msg.Reader(msg.Payload); int itemType2 = (int)Msg.ReadItemType(binaryReader25); bool isBig = binaryReader25.ReadBoolean(); string rdName2 = binaryReader25.ReadString(); if (Role == CoopRole.Host && GateProduct(msg) != PurchaseGate.Process) { break; } bool flag9 = ApplyLicenseUnlock(itemType2, isBig, rdName2); if (Role != CoopRole.Host) { break; } if (flag9) { Broadcast(MsgType.LicenseUnlock, delegate(BinaryWriter bw) { Msg.WriteItemType(bw, (EItemType)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 binaryReader22 = Msg.Reader(msg.Payload); bool scanner = binaryReader22.ReadBoolean(); int num39 = binaryReader22.ReadUInt16(); HashSet wanted = new HashSet(); HashSet wantedNames = new HashSet(); for (int num40 = 0; num40 < num39; num40++) { int localId2; bool num41 = CardShopCoop.Util.EnumMap.TryFromWire(EnumKind.ItemType, binaryReader22.ReadInt32(), out localId2); bool flag8 = binaryReader22.ReadBoolean(); int num42 = binaryReader22.ReadInt32(); if (num41) { wanted.Add(((long)localId2 << 1) | (flag8 ? 1 : 0)); } wantedNames.Add((long)((ulong)(uint)num42 << 1) | (long)(flag8 ? 1 : 0)); } bool allowLock = Time.realtimeSinceStartupAsDouble - _lastLicenseBuyTime > 12.0; Guarded("license-apply", delegate { //IL_005b: 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_00ce: Invalid comparison between Unknown and I4 CPlayerData.m_IsScannerRestockUnlocked = CPlayerData.m_IsScannerRestockUnlocked || scanner; List restockDataList = Inv().m_StockItemData_SO.m_RestockDataList; List isItemLicenseUnlocked = CPlayerData.m_IsItemLicenseUnlocked; bool flag12 = false; for (int i = 0; i < restockDataList.Count && i < isItemLicenseUnlocked.Count; i++) { if (restockDataList[i] != null) { long num53 = (restockDataList[i].isBigBox ? 1 : 0); bool flag13 = wanted.Contains(((long)restockDataList[i].itemType << 1) | num53) || wantedNames.Contains((long)((ulong)(uint)Fnv(restockDataList[i].name ?? "") << 1) | num53); if (flag13 && !isItemLicenseUnlocked[i]) { GamePatches.ApplyingRemoteLicense = true; try { CPlayerData.SetUnlockItemLicense(i); } finally { GamePatches.ApplyingRemoteLicense = false; } flag12 = true; try { if ((int)restockDataList[i].itemType == 1) { TutorialManager.AddTaskValue((ETutorialTaskCondition)14, 1f); } } catch { } } else if (!flag13 && isItemLicenseUnlocked[i] && i != 0 && allowLock) { isItemLicenseUnlocked[i] = false; } } } if (flag12) { try { GameInstance.m_IsItemLicenseUnlocked = true; } catch { } RefreshLicensePanels(); } }); break; } case MsgType.StaffOp: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br24 = Msg.Reader(msg.Payload)) { _staff.HostApplyOp(br24); break; } } break; case MsgType.StaffState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br20 = Msg.Reader(msg.Payload)) { _staff.ClientApplyState(br20); break; } } break; case MsgType.ShopOp: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br16 = Msg.Reader(msg.Payload)) { _shopState.HostApplyOp(br16); break; } } break; case MsgType.ShopState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br15 = Msg.Reader(msg.Payload)) { _shopState.ClientApplyState(br15); break; } } break; case MsgType.SettingsOp: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br11 = Msg.Reader(msg.Payload)) { _settings.HostApplyOp(br11); break; } } break; case MsgType.SettingsState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br7 = Msg.Reader(msg.Payload)) { _settings.ClientApplyState(br7); break; } } break; case MsgType.MarketState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br6 = Msg.Reader(msg.Payload)) { _market.ClientApplyState(br6); break; } } break; case MsgType.ReportState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br5 = Msg.Reader(msg.Payload)) { _report.ClientApplyState(br5); break; } } break; case MsgType.ContainerOp: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br3 = Msg.Reader(msg.Payload)) { _containers.HostApplyOp(br3); break; } } break; case MsgType.ContainerState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br = Msg.Reader(msg.Payload)) { _containers.ClientApplyState(br); break; } } break; case MsgType.TournamentState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br34 = Msg.Reader(msg.Payload)) { _tournament.ClientApplyState(br34); break; } } break; case MsgType.CardBoxOp: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br33 = Msg.Reader(msg.Payload)) { _cardBoxes.HostApplyOp(br33, msg.ConnId); break; } } break; case MsgType.CardBoxState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br31 = Msg.Reader(msg.Payload)) { _cardBoxes.ClientApplyState(br31); break; } } break; case MsgType.FurnBoxOp: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br30 = Msg.Reader(msg.Payload)) { _furnBoxes.HostApplyOp(br30, msg.ConnId); break; } } break; case MsgType.FurnBoxState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br28 = Msg.Reader(msg.Payload)) { _furnBoxes.ClientApplyState(br28); break; } } break; case MsgType.EnumSync: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader24 = Msg.Reader(msg.Payload); int count2 = binaryReader24.ReadInt32(); byte[] hostBytes = Msg.Gunzip(binaryReader24.ReadBytes(count2)); 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 br26 = Msg.Reader(msg.Payload)) { _grading.HostApplyOp(br26, msg.ConnId); break; } } break; case MsgType.GradingState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br25 = Msg.Reader(msg.Payload)) { _grading.ClientApplyState(br25); break; } } break; case MsgType.TradeOp: if (Role == CoopRole.Host && InGameLevel()) { using (BinaryReader br23 = Msg.Reader(msg.Payload)) { _trades.HostApplyOp(br23); break; } } break; case MsgType.TradeState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br21 = Msg.Reader(msg.Payload)) { _trades.ClientApplyState(br21); break; } } break; case MsgType.TableState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br18 = Msg.Reader(msg.Payload)) { _tables.ClientApplyState(br18); break; } } break; case MsgType.LightState: { if (Role != CoopRole.Client || !InGameLevel()) { break; } using BinaryReader binaryReader18 = Msg.Reader(msg.Payload); LightTimeData val7 = JsonUtility.FromJson(binaryReader18.ReadString()); if (val7 == null) { break; } try { if ((Object)(object)_lightManager == (Object)null) { _lightManager = Object.FindObjectOfType(); } if ((Object)(object)_lightManager == (Object)null) { break; } int num28 = ((FiTimeOfDayIdx?.GetValue(_lightManager) is int num29) ? num29 : (-1)); int num30 = ((FiTimeHour?.GetValue(_lightManager) is int num31) ? num31 : (-1)); int num32 = ((FiTimeMin?.GetValue(_lightManager) is int num33) ? num33 : 0); int num34 = Math.Abs(val7.m_TimeHour * 60 + val7.m_TimeMin - (num30 * 60 + num32)); if (num34 > 600 || Time.realtimeSinceStartupAsDouble - _lastDayMirrorAt < 10.0) { break; } try { if (LightManager.IsShopLightOn() != val7.m_IsShopLightOn) { _lightManager.ToggleShopLight(); } } catch (Exception ex6) { CoopPlugin.Log.LogWarning((object)("shop-light apply: " + ex6.Message)); } if (num28 != val7.m_TImeOfDayIndex || num34 > 4) { CPlayerData.m_LightTimeData = val7; FiFinishLoading?.SetValue(_lightManager, false); MiLightInit?.Invoke(_lightManager, null); CoopPlugin.Log.LogInfo((object)$"lighting re-synced (phase {num28}->{val7.m_TImeOfDayIndex}, drift {num34}min)"); } break; } catch (Exception ex7) { CoopPlugin.Log.LogWarning((object)("light apply: " + ex7.Message)); break; } } case MsgType.ShopName: { if (Role != CoopRole.Client) { break; } using BinaryReader binaryReader16 = Msg.Reader(msg.Payload); string text16 = binaryReader16.ReadString(); if (text16.Length > 0) { if (CPlayerData.GetPlayerName() != text16) { CPlayerData.PlayerName = text16; CoopPlugin.Log.LogInfo((object)("shop name synced: " + text16)); } _lastShopNameApplied = text16; if ((Object)(object)_shopSign != (Object)null) { try { _shopSign.text = text16; break; } catch { break; } } break; } break; } case MsgType.ItemPriceContrib: { if (Role != CoopRole.Host) { break; } using BinaryReader binaryReader14 = Msg.Reader(msg.Payload); int num24 = (int)Msg.ReadItemType(binaryReader14); float num25 = binaryReader14.ReadSingle(); if (num24 < 0 || num24 > 500000) { break; } if (!Enum.IsDefined(typeof(EItemType), (object)(EItemType)num24)) { CoopPlugin.Log.LogWarning((object)$"item price for unknown item type {num24} skipped - host missing content pack?"); Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write("the host couldn't apply that price - it may be missing that product"); }); break; } GamePatches.ApplyingRemotePrice = true; bool flag5 = false; try { CPlayerData.SetItemPrice((EItemType)num24, num25); } catch (Exception ex4) { flag5 = true; CoopPlugin.Log.LogWarning((object)($"item price apply ({(object)(EItemType)num24}): " + ex4.Message)); } finally { GamePatches.ApplyingRemotePrice = false; } if (flag5) { Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter bw) { bw.Write("the host couldn't apply that price - it may be missing that product"); }); } break; } case MsgType.ObjMoveDelta: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br13 = Msg.Reader(msg.Payload)) { _objMoves.ApplyRemote(ObjMoveSync.ReadEntries(br13)); 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, dropIfHostMoving: true); if (_net.ConnectionCount > 1) { Broadcast(MsgType.ObjMoveDelta, delegate(BinaryWriter bw) { ObjMoveSync.WriteEntries(bw, entries); }); } break; } case MsgType.CardPriceSet: { using BinaryReader binaryReader13 = Msg.Reader(msg.Payload); CardData val6 = Msg.ReadCard(binaryReader13); float num23 = binaryReader13.ReadSingle(); if (!InGameLevel()) { _pendingCardPrices.Add(new KeyValuePair(val6, num23)); break; } string text4 = CardPriceKey(val6); if (text4 != null && _myCardPrices.TryGetValue(text4, out var value6)) { if (Math.Abs(value6.Value - num23) <= 0.0075f) { value6.Acked = true; _myCardPrices[text4] = value6; } else { if (!value6.Acked) { break; } value6.Value = num23; _myCardPrices[text4] = value6; } } GamePatches.ApplyingRemotePrice = true; string value7; string text5 = (PeerNames.TryGetValue(msg.ConnId, out value7) ? value7 : ("conn " + msg.ConnId)); bool flag4; float actual; bool relayAnyway; try { flag4 = ApplyRemoteCardPrice(val6, num23, text5, out actual, out relayAnyway); } finally { GamePatches.ApplyingRemotePrice = false; } if (!flag4) { if (relayAnyway && Role == CoopRole.Host) { CardData passCard = val6; float passValue = num23; Broadcast(MsgType.CardPriceSet, delegate(BinaryWriter bw) { Msg.WriteCard(bw, passCard); bw.Write(passValue); }); } } else if (Role == CoopRole.Host) { CardData echoCard = val6; float echoValue = actual; Broadcast(MsgType.CardPriceSet, delegate(BinaryWriter bw) { Msg.WriteCard(bw, echoCard); bw.Write(echoValue); }); } break; } case MsgType.RegisterState: if (Role == CoopRole.Client && InGameLevel()) { using (BinaryReader br8 = Msg.Reader(msg.Payload)) { _registerMirror.Apply(RegisterServe.ReadStates(br8)); break; } } break; case MsgType.ServeRequest: { if (Role != CoopRole.Host || !InGameLevel()) { break; } using BinaryReader binaryReader11 = Msg.Reader(msg.Payload); int counterIndex = binaryReader11.ReadInt32(); string value5; string serverName = (PeerNames.TryGetValue(msg.ConnId, out value5) ? value5 : "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) { using (BinaryReader binaryReader8 = Msg.Reader(msg.Payload)) { RegisterLine = binaryReader8.ReadString(); RegisterLineTimer = 3f; } if (RegisterLine == "sale complete!") { Guarded("reset-screens", RegisterServe.ClientResetScreens); Guarded("reset-totals", RegisterServe.ClientResetTotals); } } break; case MsgType.ScanEcho: { if (Role != CoopRole.Client || !InGameLevel()) { break; } using BinaryReader binaryReader7 = Msg.Reader(msg.Payload); int num15 = binaryReader7.ReadByte(); bool flag3 = binaryReader7.ReadBoolean(); double price = binaryReader7.ReadDouble(); double hostTotal = binaryReader7.ReadDouble(); try { ShelfManager val2 = Object.FindObjectOfType(); if (!((Object)(object)val2 == (Object)null) && num15 < val2.m_CashierCounterList.Count) { InteractableCashierCounter counter = val2.m_CashierCounterList[num15]; CardData card = (flag3 ? Msg.ReadCard(binaryReader7) : null); EItemType itemType = (EItemType)((!flag3) ? ((int)Msg.ReadItemType(binaryReader7)) : 0); RegisterServe.ApplyScanEcho(counter, flag3, 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 num11 = binaryReader5.ReadSingle(); switch (b) { case 1: CEventManager.QueueEvent((CEvent)new CEventPlayer_AddCoin(num11, false)); break; case 2: { double num12 = CPlayerData.m_CoinAmountDouble - _pendingReduceThisFrame; if ((double)num11 > num12 + 0.0001) { Send(msg.ConnId, MsgType.Toast, delegate(BinaryWriter w) { w.Write("purchase declined - the shared wallet is short"); }); _lastCoinSent = double.MinValue; ResolveHeldPurchases(msg.ConnId, deliver: false); _chargeVerdicts[msg.ConnId] = new ChargeVerdict { Accepted = false, At = Time.realtimeSinceStartupAsDouble }; } else { _pendingReduceThisFrame += num11; CEventManager.QueueEvent((CEvent)new CEventPlayer_ReduceCoin(num11, false)); ResolveHeldPurchases(msg.ConnId, deliver: true); _chargeVerdicts[msg.ConnId] = new ChargeVerdict { Accepted = true, At = Time.realtimeSinceStartupAsDouble }; } break; } case 3: CEventManager.QueueEvent((CEvent)new CEventPlayer_AddShopExp((int)num11, false)); break; case 4: CEventManager.QueueEvent((CEvent)new CEventPlayer_AddFame((int)num11, false)); break; } break; } case MsgType.SprayHit: { if (Role != CoopRole.Host || !InGameLevel()) { break; } using BinaryReader binaryReader4 = Msg.Reader(msg.Payload); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(binaryReader4.ReadSingle(), binaryReader4.ReadSingle(), binaryReader4.ReadSingle()); float num8 = binaryReader4.ReadSingle(); int num9 = binaryReader4.ReadInt32(); if ((Object)(object)_cmSpray == (Object)null) { _cmSpray = Object.FindObjectOfType(); } if (!((Object)(object)_cmSpray != (Object)null)) { break; } List customerList = _cmSpray.GetCustomerList(); for (int num10 = 0; num10 < customerList.Count; num10++) { if ((Object)(object)customerList[num10] != (Object)null) { customerList[num10].DeodorantSprayCheck(val, num8, num9); } } 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) { byte[] rawSave; int hostSlot; byte[] rawBundle; try { rawSave = SaveTransfer.BuildHostPayload(); hostSlot = 6; 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; } byte[] gzHostEnum = GzipLines(SafeEnumLines()); byte[] gzHostCards = GzipLines(SafeCardsList()); 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.37"); bw.Write(CoopPlugin.PlayerName.Value); bw.Write(payload.Length); bw.Write(hostSlot); bw.Write(bundle.Length); bw.Write((byte)connId); bw.Write(gzHostEnum.Length); bw.Write(gzHostEnum); bw.Write(gzHostCards.Length); bw.Write(gzHostCards); })); 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.37")] public class CoopPlugin : BaseUnityPlugin { public const string Guid = "com.zwhit.cardshopcoop"; public const string Name = "CardShopCoop"; public const string Version = "1.0.37"; 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; public static ConfigEntry HostServeKey; 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_0232: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Expected O, but got Unknown //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Expected O, but got Unknown //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0273: 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."); HostServeKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "HostServeKey", false, "Let the HOST also use the serve key to run the register (quick-serve, bypassing the minigame) - the same shortcut joiners get. ADDITIVE to the game's normal mouse serving; off by default."); CoopCore.HostServeKeyEnabled = HostServeKey.Value; 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.37", UiToggleKey.Value)); } } } namespace CardShopCoop.Util { public enum EnumKind { ItemType, ObjectType, DecoObject, CardExpansion, MonsterType } public static class EnumMap { private sealed class Table { public readonly Dictionary Map = new Dictionary(); public HashSet ModdedSource; public bool IsModded(int id) { if (ModdedSource != null) { return ModdedSource.Contains(id); } return id >= 200000; } } private const int KindCount = 5; private const int ModdedIdFloor = 200000; private static Table[] _outTables; private static Table[] _inTables; private static volatile bool _active; private static readonly HashSet _loggedMisses = new HashSet(); private static string _lastSummary; private static int _buildCount; private static readonly string[] EnumTypeNames = new string[5] { "EItemType", "EObjectType", "EDecoObject", "ECardExpansionType", null }; public static bool Active => _active; private static int Sentinel(EnumKind kind) { return kind switch { EnumKind.DecoObject => 0, EnumKind.MonsterType => 0, _ => -1, }; } public static int ToWire(EnumKind kind, int localId) { return Translate(_outTables, kind, localId, "local->host"); } public static int FromWire(EnumKind kind, int wireId) { return Translate(_inTables, kind, wireId, "host->local"); } public static bool TryFromWire(EnumKind kind, int wireId, out int localId) { localId = wireId; if (!_active) { return true; } Table table = TableFor(_inTables, kind); if (table == null || !table.IsModded(wireId)) { return true; } if (table.Map.TryGetValue(wireId, out localId)) { return true; } localId = Sentinel(kind); LogMissOnce(kind, wireId, "host->local"); return false; } public static void Clear() { _active = false; _outTables = null; _inTables = null; lock (_loggedMisses) { _loggedMisses.Clear(); } } public static void Reset() { Clear(); } public static void Build(List hostEnumLines, List hostCardLines) { Clear(); _buildCount++; try { Table[] array = new Table[5]; Table[] array2 = new Table[5]; List list = new List(); Dictionary[] array3 = ParseEnumLines(SafeLines(ModParity.EnumLines)); Dictionary[] array4 = ParseEnumLines(hostEnumLines); for (int i = 0; i < 5; i++) { if (EnumTypeNames[i] != null && array3[i].Count != 0 && array4[i].Count != 0) { Tuple tuple = BuildPair(array3[i], array4[i], null, null); array[i] = tuple.Item1; array2[i] = tuple.Item2; list.Add(Describe((EnumKind)i, array3[i], array4[i], tuple.Item1)); } } Dictionary dictionary = ParseCardLines(SafeLines(ModParity.CardsList)); Dictionary dictionary2 = ParseCardLines(hostCardLines); if (dictionary.Count > 0 && dictionary2.Count > 0) { int num = 4; Tuple tuple2 = BuildPair(dictionary, dictionary2, IdSet(dictionary), IdSet(dictionary2)); array[num] = tuple2.Item1; array2[num] = tuple2.Item2; list.Add(Describe(EnumKind.MonsterType, dictionary, dictionary2, tuple2.Item1)); } _outTables = array; _inTables = array2; _active = true; string text = "id translation ready (wire speaks HOST ids): " + ((list.Count > 0) ? string.Join("; ", list.ToArray()) : "nothing modded on either side - identity"); if (!string.Equals(text, _lastSummary, StringComparison.Ordinal)) { _lastSummary = text; Log(text); } else { Log("id translation ready (unchanged, join #" + _buildCount + ")"); } } catch (Exception ex) { Clear(); LogWarn("id translation could not be built (" + ex.Message + ") - running untranslated, as previous versions did"); } } private static Tuple BuildPair(Dictionary ours, Dictionary theirs, HashSet ourModded, HashSet theirModded) { Table table = new Table { ModdedSource = ourModded }; Table table2 = new Table { ModdedSource = theirModded }; foreach (KeyValuePair our in ours) { if (theirs.TryGetValue(our.Key, out var value)) { table.Map[our.Value] = value; table2.Map[value] = our.Value; } } return Tuple.Create(table, table2); } private static string Describe(EnumKind kind, Dictionary ours, Dictionary theirs, Table toHost) { int count = toHost.Map.Count; return kind.ToString() + " " + count + " mapped/" + Math.Max(0, ours.Count - count) + " ours-only/" + Math.Max(0, theirs.Count - count) + " host-only"; } private static int Translate(Table[] tables, EnumKind kind, int id, string dir) { if (!_active) { return id; } Table table = TableFor(tables, kind); if (table == null || !table.IsModded(id)) { return id; } if (table.Map.TryGetValue(id, out var value)) { return value; } LogMissOnce(kind, id, dir); return Sentinel(kind); } private static Table TableFor(Table[] tables, EnumKind kind) { if (tables == null) { return null; } if (kind < EnumKind.ItemType || (int)kind >= tables.Length) { return null; } return tables[(int)kind]; } private static void LogMissOnce(EnumKind kind, int id, string dir) { long item = ((long)kind << 32) | (uint)id; bool flag; lock (_loggedMisses) { flag = _loggedMisses.Add(item); } if (flag) { Log("no local counterpart for " + kind.ToString() + " id " + id + " (" + dir + ") - one-sided content pack; sent as None, further ones for this id are silent"); } } private static Dictionary[] ParseEnumLines(List lines) { Dictionary[] array = new Dictionary[5]; for (int i = 0; i < 5; i++) { array[i] = new Dictionary(StringComparer.Ordinal); } if (lines == null) { return array; } foreach (string line in lines) { if (string.IsNullOrEmpty(line)) { continue; } string text = line.Trim(); int num = text.IndexOf(':'); if (num <= 0) { continue; } int num2 = KindOfTypeName(text.Substring(0, num)); if (num2 >= 0) { int num3 = text.LastIndexOf('='); if (num3 > num + 1 && num3 != text.Length - 1 && int.TryParse(text.Substring(num3 + 1), out var result) && result >= 200000) { array[num2][text.Substring(num + 1, num3 - num - 1)] = result; } } } return array; } private static Dictionary ParseCardLines(List lines) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); if (lines == null) { return dictionary; } foreach (string line in lines) { if (!string.IsNullOrEmpty(line)) { string text = line.Trim(); int num = text.LastIndexOf('='); if (num > 0 && num != text.Length - 1 && int.TryParse(text.Substring(num + 1).Trim(), out var result)) { dictionary[text.Substring(0, num).Trim()] = result; } } } return dictionary; } private static HashSet IdSet(Dictionary nameToId) { HashSet hashSet = new HashSet(); foreach (KeyValuePair item in nameToId) { hashSet.Add(item.Value); } return hashSet; } private static int KindOfTypeName(string typeName) { for (int i = 0; i < 5; i++) { if (EnumTypeNames[i] != null && string.Equals(EnumTypeNames[i], typeName, StringComparison.Ordinal)) { return i; } } return -1; } private static List SafeLines(Func> f) { try { return f() ?? new List(); } catch { return new List(); } } private static void Log(string s) { try { CoopPlugin.Log.LogInfo((object)("EnumMap: " + s)); } catch { } } private static void LogWarn(string s) { try { CoopPlugin.Log.LogWarning((object)("EnumMap: " + s)); } catch { } } } 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 GradingInterop { private static readonly Type TReg = AccessTools.TypeByName("TCGCardShopSimulator.GradingOverhaul.EncodedGradeRegistry"); private static readonly Type THelper = AccessTools.TypeByName("TCGCardShopSimulator.GradingOverhaul.Helper"); private static readonly MethodInfo MiRemember = ((TReg == null) ? null : AccessTools.Method(TReg, "RememberForExternalMod", new Type[2] { typeof(CardData), typeof(int) }, (Type[])null)); private static readonly MethodInfo MiGetEncoded = ((TReg == null) ? null : AccessTools.Method(TReg, "GetEncodedOrCurrent", new Type[1] { typeof(CardData) }, (Type[])null)); private static readonly MethodInfo MiActual = ((THelper == null) ? null : AccessTools.Method(THelper, "GetActualGrade", new Type[1] { typeof(int) }, (Type[])null)); private static bool _logged; public static bool Present { get { if (MiRemember != null && !_logged) { _logged = true; CoopPlugin.Log.LogInfo((object)"Grading Overhaul detected - graded cards will sync via its encoded-grade API"); } return MiRemember != null; } } public static void Remember(CardData card) { if (card == null || card.cardGrade <= 10 || MiRemember == null) { return; } try { MiRemember.Invoke(null, new object[2] { card, card.cardGrade }); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("GradingInterop.Remember: " + ex.Message)); } } public static int Encoded(CardData card) { if (card == null) { return 0; } if (MiGetEncoded == null) { return card.cardGrade; } try { return (int)MiGetEncoded.Invoke(null, new object[1] { card }); } catch { return card.cardGrade; } } public static int Actual(int encoded) { if (encoded <= 10 || MiActual == null) { return encoded; } try { return (int)MiActual.Invoke(null, new object[1] { encoded }); } catch { return encoded; } } } public static class ModParity { private static string _plugins; private static string _enum; private static string _cards; public static bool RestartRequiredForJoin; public static bool RestartRequiredForSolo; private static readonly string[] EplSentinelTypeNames = new string[2] { "EnhancedPrefabLoader.Core.EplRuntimeData", "EnhancedPrefabLoader.Core.Models.SaveData.ItemSaveData" }; private static bool _eplLoaded; private static bool _eplProbed; private static int _eplProbeTick; private static bool _eplLoadedLogged; private const string VanillaEnumNote = "EPL not loaded - vanilla, modded set is EMPTY; any enum_values.json on disk is ignored"; private static readonly string[] ModdedEnumTypeNames = new string[6] { "EObjectType", "EDecoObject", "EItemType", "ECardExpansionType", "ERarity", "ECollectionPackType" }; private const long ModdedIdFloor = 200000L; private static bool _enumSourceLogged; private static readonly Regex WhitespaceRx = new Regex("\\s"); private static readonly Regex NonWordRx = new Regex("[^A-Za-z0-9_]"); private static string _installBackupPath; public static bool RegistryFileMatchesRuntime() { if (!RestartRequiredForJoin) { return !RestartRequiredForSolo; } return false; } public static string CardsHash() { if (_cards != null) { return _cards; } try { List list = CardEntries(); _cards = ((list.Count == 0) ? "none" : Short(Sha1(string.Join(";", list)))); } catch { _cards = "err"; } return _cards; } public static List CardsList() { try { return CardEntries(); } catch { return new List(); } } private static List CardEntries() { string path = Path.Combine(Paths.BepInExRootPath, "patchers", "CreateCardsPreloader", "MonsterConfigs"); List list = new List(); if (Directory.Exists(path)) { string[] files = Directory.GetFiles(path, "*.ini"); foreach (string path2 in files) { string text = null; string text2 = null; string[] array = File.ReadAllLines(path2); foreach (string text3 in array) { int num = text3.IndexOf('='); if (num > 0) { string text4 = text3.Substring(0, num).Trim(); if (text4.Equals("Monster Type", StringComparison.OrdinalIgnoreCase)) { text = text3.Substring(num + 1).Trim(); } else if (text4.Equals("Monster Type ID", StringComparison.OrdinalIgnoreCase)) { text2 = text3.Substring(num + 1).Trim(); } } } if (text != null && text2 != null) { list.Add(text + "=" + text2); } } } list.Sort(StringComparer.Ordinal); return list; } public static string PluginHash() { if (_plugins != null) { return _plugins; } try { _plugins = Short(Sha1(string.Join(";", PluginEntries()))); } catch { _plugins = "err"; } return _plugins; } public static List PluginList() { try { return PluginEntries(); } catch { return new List(); } } private static List PluginEntries() { List list = new List(); foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { list.Add(pluginInfo.Key + "=" + pluginInfo.Value.Metadata.Version); } list.Sort(StringComparer.Ordinal); return list; } public static string EnumFilePath() { return Path.Combine(Application.persistentDataPath, "PrefabLoader", "enum_values.json"); } public static bool EplLoaded() { if (_eplLoaded) { return true; } int tickCount = Environment.TickCount; if (_eplProbed && tickCount - _eplProbeTick < 5000) { return false; } _eplProbed = true; _eplProbeTick = tickCount; string[] eplSentinelTypeNames = EplSentinelTypeNames; foreach (string text in eplSentinelTypeNames) { try { if (AccessTools.TypeByName(text) == null) { continue; } _eplLoaded = true; break; } catch { } } if (_eplLoaded && !_eplLoadedLogged) { _eplLoadedLogged = true; try { CoopPlugin.Log.LogInfo((object)"EnhancedPrefabLoader detected - a custom id registry is in play"); } catch { } } return _eplLoaded; } public static string EnumHash() { if (_enum != null) { return _enum; } try { List list = RuntimeEnumEntries(); if (list.Count > 0) { _enum = Short(Sha1(string.Join("\n", list))); return _enum; } if (!EplLoaded()) { LogEnumSourceOnce("EPL not loaded - vanilla, modded set is EMPTY; any enum_values.json on disk is ignored"); _enum = "none"; return _enum; } string path = EnumFilePath(); if (!File.Exists(path)) { _enum = "none"; return _enum; } List list2 = CanonicalEnumLines(File.ReadAllText(path)); _enum = ((list2 != null && list2.Count > 0) ? Short(Sha1(string.Join("\n", list2))) : Short(Sha1Bytes(File.ReadAllBytes(path)))); } catch { _enum = "none"; } return _enum; } public static List EnumLines() { try { List list = RuntimeEnumEntries(); if (list.Count > 0) { LogEnumSourceOnce("runtime enums (" + list.Count + " modded ids)"); return list; } if (!EplLoaded()) { LogEnumSourceOnce("EPL not loaded - vanilla, modded set is EMPTY; any enum_values.json on disk is ignored"); return list; } string path = EnumFilePath(); if (!File.Exists(path)) { LogEnumSourceOnce("EPL is loaded but no modded ids were found and there is no registry file"); return list; } List list2 = CanonicalEnumLines(File.ReadAllText(path)); if (list2 == null) { LogEnumSourceOnce("registry file unparseable - ID-conflict check disabled"); return list; } List list3 = new List(); foreach (string item in list2) { int num = item.LastIndexOf('='); if (num > 0 && num != item.Length - 1 && long.TryParse(item.Substring(num + 1), out var result) && result >= 200000) { list3.Add(item); } } LogEnumSourceOnce("enum_values.json fallback (" + list3.Count + " modded ids) - the runtime walk found none"); return list3; } catch { return new List(); } } private static void LogEnumSourceOnce(string source) { if (_enumSourceLogged) { return; } _enumSourceLogged = true; try { CoopPlugin.Log.LogInfo((object)("enum identity source: " + source)); } catch { } } private static List RuntimeEnumEntries() { List list = new List(); string[] moddedEnumTypeNames = ModdedEnumTypeNames; foreach (string text in moddedEnumTypeNames) { try { Type type = AccessTools.TypeByName(text); if (type == null || !type.IsEnum) { continue; } string[] names = Enum.GetNames(type); Array values = Enum.GetValues(type); int num = Math.Min(names.Length, values.Length); for (int j = 0; j < num; j++) { long num2; try { num2 = Convert.ToInt64(values.GetValue(j)); } catch { continue; } if (num2 >= 200000) { list.Add(type.Name + ":" + names[j] + "=" + num2); } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("enum walk (" + text + "): " + ex.Message)); } } list.Sort(StringComparer.Ordinal); return list; } private static string SanitizeMemberName(string value) { if (string.IsNullOrEmpty(value) || value.Trim().Length == 0) { return "_Invalid"; } string input = WhitespaceRx.Replace(value, ""); input = NonWordRx.Replace(input, "_"); if (input.Length > 0 && !char.IsDigit(input[0])) { return input; } return "_" + input; } private static List CanonicalEnumLines(string json) { try { List list = new List(); Regex regex = new Regex("\"([^\"]+)\"\\s*:\\s*\\{([^{}]*)\\}", RegexOptions.Singleline); Regex regex2 = new Regex("\"([^\"]+)\"\\s*:\\s*(-?\\d+)"); foreach (Match item in regex.Matches(json)) { string value = item.Groups[1].Value; foreach (Match item2 in regex2.Matches(item.Groups[2].Value)) { list.Add(value + ":" + SanitizeMemberName(item2.Groups[1].Value) + "=" + item2.Groups[2].Value); } } if (list.Count == 0) { return null; } list.Sort(StringComparer.Ordinal); return list; } catch { return null; } } public static string InstallEnumFile(byte[] hostBytes) { string text = EnumFilePath(); try { string text2 = null; if (File.Exists(text)) { byte[] array = File.ReadAllBytes(text); List list = CanonicalEnumLines(Encoding.UTF8.GetString(array)); List list2 = CanonicalEnumLines(Encoding.UTF8.GetString(hostBytes)); if ((list != null && list2 != null) ? (string.Join("\n", list) == string.Join("\n", list2)) : SameBytes(array, hostBytes)) { return "card database already synced - RESTART the game, then join again"; } text2 = text + ".coopbak-" + DateTime.Now.ToString("yyyyMMdd-HHmmss"); File.Copy(text, text2, overwrite: true); if (_installBackupPath == null && !RestartRequiredForJoin) { _installBackupPath = text2; } PruneBackups(text); } else { Directory.CreateDirectory(Path.GetDirectoryName(text)); } File.WriteAllBytes(text, hostBytes); RestartRequiredForJoin = true; WriteEnumMarker(text2); 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 string EnumMarkerPath() { return EnumFilePath() + ".hostlend"; } private static void WriteEnumMarker(string newestBackup) { try { string contents = ((newestBackup != null) ? Path.GetFileName(newestBackup) : "(no prior registry - none to back up)") + Environment.NewLine + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); File.WriteAllText(EnumMarkerPath(), contents); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("enum marker write failed: " + ex.Message)); } } public static bool HostEnumInstalled() { try { if (!EplLoaded()) { return false; } return File.Exists(EnumMarkerPath()); } catch { return false; } } public static bool RestoreEnumBackup(out string message) { string text = EnumFilePath(); try { string directoryName = Path.GetDirectoryName(text); string text2 = null; if (Directory.Exists(directoryName)) { string[] files = Directory.GetFiles(directoryName, Path.GetFileName(text) + ".coopbak-*"); Array.Sort(files, (IComparer?)StringComparer.Ordinal); if (files.Length != 0) { text2 = files[^1]; } } if (text2 == null) { try { File.Delete(EnumMarkerPath()); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("enum marker clear: " + ex.Message)); } bool flag; try { flag = !File.Exists(EnumMarkerPath()); } catch { flag = false; } message = (flag ? "no backup of your card database was found, so your card database was left exactly as it is - the 'borrowed from a host' flag has been cleared (it was blocking hosting). If your solo saves still won't load, put your own enum_values.json back by hand (see mod page)." : "no backup of your card database was found, and the co-op marker could not be deleted - delete enum_values.json.hostlend by hand (it sits next to enum_values.json; see mod page)"); try { CoopPlugin.Log.LogWarning((object)("enum restore: " + message)); } catch { } return flag; } if (File.Exists(text)) { File.Copy(text, text + ".hostcopy", overwrite: true); } File.Copy(text2, text, overwrite: true); if (_installBackupPath != null && string.Equals(text2, _installBackupPath, StringComparison.OrdinalIgnoreCase)) { RestartRequiredForJoin = false; RestartRequiredForSolo = false; _installBackupPath = null; try { File.Delete(EnumMarkerPath()); } catch { } message = "your card database was restored from the backup this session made - it is exactly what the game is already running, so NO restart is needed"; try { CoopPlugin.Log.LogInfo((object)("enum restore: " + message + " (from " + Path.GetFileName(text2) + ")")); } catch { } return true; } RestartRequiredForSolo = true; try { File.Delete(EnumMarkerPath()); } catch { } message = "your card database was restored from backup - RESTART the game before loading your solo saves"; try { CoopPlugin.Log.LogInfo((object)("enum restore: " + message + " (from " + Path.GetFileName(text2) + ")")); } catch { } return true; } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("enum restore failed: " + ex2.Message)); message = "could not restore your card database automatically - put your own enum_values.json backup back by hand (see mod page)"; return false; } } 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 static class CoopTheme { public static readonly Color Panel = Rgb(247, 241, 227, 0.97f); public static readonly Color PanelBorder = Rgb(185, 174, 148); public static readonly Color HeaderBg = Rgb(46, 75, 78); public static readonly Color HeaderText = Rgb(242, 237, 226); public static readonly Color Text = Rgb(51, 48, 43); public static readonly Color TextDim = Rgb(107, 101, 92); public static readonly Color SectionBg = Rgb(255, 251, 240, 0.9f); public static readonly Color SectionBorder = Rgb(221, 211, 188); public static readonly Color Primary = Rgb(62, 158, 140); public static readonly Color PrimaryHover = Rgb(71, 178, 158); public static readonly Color PrimaryActive = Rgb(54, 139, 123); public static readonly Color Secondary = Rgb(239, 231, 212); public static readonly Color SecondaryHover = Rgb(246, 239, 222); public static readonly Color SecondaryBorder = Rgb(201, 191, 168); public static readonly Color Danger = Rgb(201, 79, 61); public static readonly Color Warn = Rgb(201, 134, 45); public static readonly Color Success = Rgb(63, 157, 83); public static readonly Color DividerCol = Rgb(217, 207, 184); public static readonly Color FieldBg = Rgb(255, 253, 246); public static readonly Color FieldBorder = Rgb(201, 191, 168); public static readonly Color HudBg = Rgb(31, 29, 26, 0.78f); public static GUIStyle Window; public static GUIStyle HeaderStrip; public static GUIStyle Header; public static GUIStyle HeaderVersion; public static GUIStyle SectionHeader; public static GUIStyle Label; public static GUIStyle LabelDim; public static GUIStyle LabelBold; public static GUIStyle LabelWrap; public static GUIStyle LabelDanger; public static GUIStyle LabelWarn; public static GUIStyle SectionBox; public static GUIStyle Toggle; public static GUIStyle ButtonPrimary; public static GUIStyle ButtonSecondary; public static GUIStyle ButtonDanger; public static GUIStyle TextField; public static GUIStyle ChipSuccess; public static GUIStyle ChipWarn; public static GUIStyle ChipDanger; public static GUIStyle ChipInfo; public static GUIStyle HudPill; public static GUIStyle HudPillBig; public static GUIStyle RowEven; public static GUIStyle RowOdd; private static Texture2D _panelTex; private static Texture2D _headerTex; private static Texture2D _shadowTex; private static Texture2D _fieldTex; private static Texture2D _fieldFocusTex; private static Texture2D _sectionTex; private static Texture2D _dividerTex; private static Texture2D _rowEvenTex; private static Texture2D _rowOddTex; private static Texture2D _hudTex; private static Texture2D _primaryTex; private static Texture2D _primaryHoverTex; private static Texture2D _primaryActiveTex; private static Texture2D _secondaryTex; private static Texture2D _secondaryHoverTex; private static Texture2D _secondaryActiveTex; private static Texture2D _dangerTex; private static Texture2D _dangerHoverTex; private static Texture2D _dangerActiveTex; private static Texture2D _chipSuccessTex; private static Texture2D _chipWarnTex; private static Texture2D _chipDangerTex; private static Texture2D _chipInfoTex; private static bool _built; private const int WinTex = 28; private const int WinRadius = 10; private const int CtlTex = 16; private const int CtlRadius = 6; private const int SecTex = 20; private const int SecRadius = 8; private const int PillTex = 20; private const int PillRadius = 8; private const int HudTexSz = 32; private const int HudRadius = 14; private static GUIStyle _shadowStyle; private static GUIContent _titleGc; private static GUIContent _versionGc; public static void EnsureBuilt() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: 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_00fd: 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_0117: 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_0136: 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_0145: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: 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_01b2: 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_01c1: 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_01e5: 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_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0209: 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_0218: 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_0237: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0274: 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_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: 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_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: 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) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Expected O, but got Unknown //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Expected O, but got Unknown //IL_034f: Expected O, but got Unknown //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Expected O, but got Unknown //IL_040b: Expected O, but got Unknown //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_0450: Expected O, but got Unknown //IL_045a: Unknown result type (might be due to invalid IL or missing references) //IL_046e: Unknown result type (might be due to invalid IL or missing references) //IL_0473: Unknown result type (might be due to invalid IL or missing references) //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_048e: Expected O, but got Unknown //IL_0498: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_04bb: Unknown result type (might be due to invalid IL or missing references) //IL_04c2: Unknown result type (might be due to invalid IL or missing references) //IL_04c9: Unknown result type (might be due to invalid IL or missing references) //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Expected O, but got Unknown //IL_04e5: Expected O, but got Unknown //IL_04ef: Unknown result type (might be due to invalid IL or missing references) //IL_0503: Unknown result type (might be due to invalid IL or missing references) //IL_0508: Unknown result type (might be due to invalid IL or missing references) //IL_050f: Unknown result type (might be due to invalid IL or missing references) //IL_051c: Expected O, but got Unknown //IL_0526: Unknown result type (might be due to invalid IL or missing references) //IL_053a: Unknown result type (might be due to invalid IL or missing references) //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_0546: Unknown result type (might be due to invalid IL or missing references) //IL_0553: Expected O, but got Unknown //IL_055d: Unknown result type (might be due to invalid IL or missing references) //IL_0571: Unknown result type (might be due to invalid IL or missing references) //IL_0576: Unknown result type (might be due to invalid IL or missing references) //IL_057d: Unknown result type (might be due to invalid IL or missing references) //IL_0585: Unknown result type (might be due to invalid IL or missing references) //IL_0591: Expected O, but got Unknown //IL_059b: Unknown result type (might be due to invalid IL or missing references) //IL_05af: Unknown result type (might be due to invalid IL or missing references) //IL_05b4: Unknown result type (might be due to invalid IL or missing references) //IL_05bb: Unknown result type (might be due to invalid IL or missing references) //IL_05c3: Unknown result type (might be due to invalid IL or missing references) //IL_05cf: Expected O, but got Unknown //IL_05d9: Unknown result type (might be due to invalid IL or missing references) //IL_05ed: Unknown result type (might be due to invalid IL or missing references) //IL_05f2: Unknown result type (might be due to invalid IL or missing references) //IL_05f9: Unknown result type (might be due to invalid IL or missing references) //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_060d: Expected O, but got Unknown //IL_0617: Unknown result type (might be due to invalid IL or missing references) //IL_062b: Unknown result type (might be due to invalid IL or missing references) //IL_0630: Unknown result type (might be due to invalid IL or missing references) //IL_0637: Unknown result type (might be due to invalid IL or missing references) //IL_063f: Unknown result type (might be due to invalid IL or missing references) //IL_064b: Expected O, but got Unknown //IL_0655: Unknown result type (might be due to invalid IL or missing references) //IL_0669: Unknown result type (might be due to invalid IL or missing references) //IL_066e: Unknown result type (might be due to invalid IL or missing references) //IL_0673: Unknown result type (might be due to invalid IL or missing references) //IL_067d: Expected O, but got Unknown //IL_067d: Unknown result type (might be due to invalid IL or missing references) //IL_0686: Unknown result type (might be due to invalid IL or missing references) //IL_0690: Expected O, but got Unknown //IL_0690: Unknown result type (might be due to invalid IL or missing references) //IL_0695: Unknown result type (might be due to invalid IL or missing references) //IL_069f: Expected O, but got Unknown //IL_06a4: Expected O, but got Unknown //IL_06c2: Unknown result type (might be due to invalid IL or missing references) //IL_06c7: Unknown result type (might be due to invalid IL or missing references) //IL_06ce: Unknown result type (might be due to invalid IL or missing references) //IL_06db: Expected O, but got Unknown //IL_06e0: Unknown result type (might be due to invalid IL or missing references) //IL_06f9: Unknown result type (might be due to invalid IL or missing references) //IL_0717: Unknown result type (might be due to invalid IL or missing references) //IL_0735: Unknown result type (might be due to invalid IL or missing references) //IL_074e: Unknown result type (might be due to invalid IL or missing references) //IL_0753: Unknown result type (might be due to invalid IL or missing references) //IL_075a: Unknown result type (might be due to invalid IL or missing references) //IL_0762: Unknown result type (might be due to invalid IL or missing references) //IL_0769: Unknown result type (might be due to invalid IL or missing references) //IL_076e: Unknown result type (might be due to invalid IL or missing references) //IL_0778: Expected O, but got Unknown //IL_0778: Unknown result type (might be due to invalid IL or missing references) //IL_077d: Unknown result type (might be due to invalid IL or missing references) //IL_0787: Expected O, but got Unknown //IL_078c: Expected O, but got Unknown //IL_07cd: Unknown result type (might be due to invalid IL or missing references) //IL_07dc: Unknown result type (might be due to invalid IL or missing references) //IL_07f0: Unknown result type (might be due to invalid IL or missing references) //IL_07fa: Unknown result type (might be due to invalid IL or missing references) //IL_080e: Unknown result type (might be due to invalid IL or missing references) //IL_0822: Unknown result type (might be due to invalid IL or missing references) //IL_0831: Unknown result type (might be due to invalid IL or missing references) //IL_0836: Unknown result type (might be due to invalid IL or missing references) //IL_083d: Unknown result type (might be due to invalid IL or missing references) //IL_0844: Unknown result type (might be due to invalid IL or missing references) //IL_084c: Unknown result type (might be due to invalid IL or missing references) //IL_0853: Unknown result type (might be due to invalid IL or missing references) //IL_085c: Unknown result type (might be due to invalid IL or missing references) //IL_0866: Expected O, but got Unknown //IL_0866: Unknown result type (might be due to invalid IL or missing references) //IL_086f: Unknown result type (might be due to invalid IL or missing references) //IL_0879: Expected O, but got Unknown //IL_087e: Expected O, but got Unknown //IL_089c: Unknown result type (might be due to invalid IL or missing references) //IL_08ab: Unknown result type (might be due to invalid IL or missing references) //IL_08b0: Unknown result type (might be due to invalid IL or missing references) //IL_08b8: Unknown result type (might be due to invalid IL or missing references) //IL_08bf: Unknown result type (might be due to invalid IL or missing references) //IL_08c8: Unknown result type (might be due to invalid IL or missing references) //IL_08d2: Expected O, but got Unknown //IL_08d7: Expected O, but got Unknown //IL_08f5: Unknown result type (might be due to invalid IL or missing references) if (!_built) { _built = true; _panelTex = MakeRounded(28, 28, 10, Panel, PanelBorder, 1); _headerTex = MakeRoundedTop(28, 28, 10, HeaderBg); _shadowTex = MakeShadow(40, 40, 12, 0.35f, 4f); _fieldTex = MakeRounded(16, 16, 6, FieldBg, FieldBorder, 1); _fieldFocusTex = MakeRounded(16, 16, 6, FieldBg, Primary, 1); _sectionTex = MakeRounded(20, 20, 8, SectionBg, SectionBorder, 1); _dividerTex = Solid(DividerCol); _rowEvenTex = Solid(Rgb(255, 255, 255, 0.28f)); _rowOddTex = Solid(Rgb(120, 110, 90, 0.1f)); _hudTex = MakeRounded(32, 32, 14, HudBg, HudBg, 0); _primaryTex = MakeRounded(16, 16, 6, Primary, Mul(Primary, 0.85f), 1); _primaryHoverTex = MakeRounded(16, 16, 6, PrimaryHover, Mul(PrimaryHover, 0.85f), 1); _primaryActiveTex = MakeRounded(16, 16, 6, PrimaryActive, Mul(PrimaryActive, 0.85f), 1); _secondaryTex = MakeRounded(16, 16, 6, Secondary, SecondaryBorder, 1); _secondaryHoverTex = MakeRounded(16, 16, 6, SecondaryHover, SecondaryBorder, 1); _secondaryActiveTex = MakeRounded(16, 16, 6, Mul(Secondary, 0.94f), SecondaryBorder, 1); _dangerTex = MakeRounded(16, 16, 6, Danger, Mul(Danger, 0.85f), 1); _dangerHoverTex = MakeRounded(16, 16, 6, Lerp(Danger, Color.white, 0.1f), Mul(Danger, 0.85f), 1); _dangerActiveTex = MakeRounded(16, 16, 6, Mul(Danger, 0.88f), Mul(Danger, 0.8f), 1); _chipSuccessTex = MakeRounded(20, 20, 8, WithA(Success, 0.18f), WithA(Success, 0.55f), 1); _chipWarnTex = MakeRounded(20, 20, 8, WithA(Warn, 0.18f), WithA(Warn, 0.55f), 1); _chipDangerTex = MakeRounded(20, 20, 8, WithA(Danger, 0.18f), WithA(Danger, 0.55f), 1); _chipInfoTex = MakeRounded(20, 20, 8, WithA(HeaderBg, 0.16f), WithA(HeaderBg, 0.45f), 1); Window = new GUIStyle(GUI.skin.window) { richText = true, padding = new RectOffset(14, 14, 40, 12), border = new RectOffset(10, 10, 10, 10) }; Window.normal.background = _panelTex; Window.onNormal.background = _panelTex; Window.focused.background = _panelTex; Window.onFocused.background = _panelTex; Window.hover.background = _panelTex; Window.active.background = _panelTex; Window.normal.textColor = Clear(); Window.onNormal.textColor = Clear(); HeaderStrip = new GUIStyle { border = new RectOffset(10, 10, 10, 3) }; HeaderStrip.normal.background = _headerTex; Header = new GUIStyle(GUI.skin.label) { richText = true, fontStyle = (FontStyle)1, fontSize = 15, alignment = (TextAnchor)3 }; Header.normal.textColor = HeaderText; HeaderVersion = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 11, alignment = (TextAnchor)5 }; HeaderVersion.normal.textColor = WithA(HeaderText, 0.8f); SectionHeader = new GUIStyle(GUI.skin.label) { richText = true, fontStyle = (FontStyle)1, fontSize = 12, margin = new RectOffset(0, 0, 0, 4) }; SectionHeader.normal.textColor = HeaderBg; Label = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 12 }; Label.normal.textColor = Text; LabelDim = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 11 }; LabelDim.normal.textColor = TextDim; LabelBold = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 12, fontStyle = (FontStyle)1 }; LabelBold.normal.textColor = Text; LabelWrap = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 12, wordWrap = true }; LabelWrap.normal.textColor = Text; LabelDanger = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 12, wordWrap = true }; LabelDanger.normal.textColor = Danger; LabelWarn = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 12, wordWrap = true }; LabelWarn.normal.textColor = Warn; SectionBox = new GUIStyle(GUI.skin.box) { border = new RectOffset(8, 8, 8, 8), padding = new RectOffset(10, 10, 10, 10), margin = new RectOffset(0, 0, 4, 4) }; SectionBox.normal.background = _sectionTex; Toggle = new GUIStyle(GUI.skin.toggle) { richText = true, fontSize = 12 }; SetTextColorAllStates(Toggle, Text); ButtonPrimary = MakeButton(_primaryTex, _primaryHoverTex, _primaryActiveTex, Color.white); ButtonSecondary = MakeButton(_secondaryTex, _secondaryHoverTex, _secondaryActiveTex, Text); ButtonDanger = MakeButton(_dangerTex, _dangerHoverTex, _dangerActiveTex, Color.white); TextField = new GUIStyle(GUI.skin.textField) { richText = false, fontSize = 12, alignment = (TextAnchor)3, padding = new RectOffset(6, 6, 4, 4), border = new RectOffset(6, 6, 6, 6) }; TextField.normal.background = _fieldTex; TextField.hover.background = _fieldTex; TextField.focused.background = _fieldFocusTex; SetTextColorAllStates(TextField, Text); ChipSuccess = MakeChip(_chipSuccessTex, Success); ChipWarn = MakeChip(_chipWarnTex, Mul(Warn, 0.92f)); ChipDanger = MakeChip(_chipDangerTex, Danger); ChipInfo = MakeChip(_chipInfoTex, HeaderBg); HudPill = new GUIStyle { richText = true, wordWrap = true, fontSize = 13, alignment = (TextAnchor)4, border = new RectOffset(14, 14, 14, 14), padding = new RectOffset(14, 14, 9, 9) }; HudPill.normal.background = _hudTex; HudPill.normal.textColor = Color.white; HudPillBig = new GUIStyle(HudPill) { fontSize = 20, fontStyle = (FontStyle)1, padding = new RectOffset(18, 18, 10, 10) }; HudPillBig.normal.background = _hudTex; HudPillBig.normal.textColor = Color.white; RowEven = MakeRow(_rowEvenTex); RowOdd = MakeRow(_rowOddTex); } } public static void DrawWindowShadow(Rect win) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_shadowTex == (Object)null)) { GUI.Box(new Rect(((Rect)(ref win)).x + 3f, ((Rect)(ref win)).y + 4f, ((Rect)(ref win)).width, ((Rect)(ref win)).height), GUIContent.none, HeaderShadow()); } } private static GUIStyle HeaderShadow() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0024: Expected O, but got Unknown if (_shadowStyle == null) { _shadowStyle = new GUIStyle { border = new RectOffset(16, 16, 16, 16) }; _shadowStyle.normal.background = _shadowTex; } return _shadowStyle; } public static void DrawWindowChrome(Rect r, string title, string version) { //IL_0016: 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_004e: Expected O, but got Unknown //IL_006a: 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) //IL_00a2: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) GUI.Box(new Rect(0f, 0f, ((Rect)(ref r)).width, 30f), GUIContent.none, HeaderStrip); if (_titleGc == null || _titleGc.text != title) { _titleGc = new GUIContent(title); } GUI.Label(new Rect(13f, 5f, ((Rect)(ref r)).width - 26f, 20f), _titleGc, Header); if (_versionGc == null || _versionGc.text != version) { _versionGc = new GUIContent(version); } GUI.Label(new Rect(13f, 6f, ((Rect)(ref r)).width - 26f, 18f), _versionGc, HeaderVersion); } public static void Divider() { //IL_0030: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0043: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(3f); Rect rect = GUILayoutUtility.GetRect(1f, 1f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(1f) }); if ((int)Event.current.type == 7) { GUI.DrawTexture(rect, (Texture)(object)_dividerTex); } GUILayout.Space(3f); } public static void Chip(string text, GUIStyle chipStyle) { GUILayout.Label(text, chipStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); } public static Vector2 PillSize(GUIStyle style, GUIContent content, float maxW) { //IL_0002: 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_0008: 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) Vector2 val = style.CalcSize(content); if (val.x > maxW) { val.x = maxW; val.y = style.CalcHeight(content, maxW); } return val; } private static GUIStyle MakeButton(Texture2D bg, Texture2D hover, Texture2D active, Color textColor) { //IL_000a: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0037: 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_0048: Expected O, but got Unknown //IL_0048: 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_0057: Expected O, but got Unknown //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_0066: Expected O, but got Unknown //IL_0066: 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_007e: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00de: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.button) { richText = true, fontSize = 12, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, fixedHeight = 26f, padding = new RectOffset(10, 10, 4, 4), margin = new RectOffset(2, 2, 3, 3), border = new RectOffset(6, 6, 6, 6) }; val.normal.background = bg; val.normal.textColor = textColor; val.hover.background = hover; val.hover.textColor = textColor; val.active.background = active; val.active.textColor = textColor; val.focused.background = bg; val.focused.textColor = textColor; val.onNormal.background = bg; val.onNormal.textColor = textColor; val.onHover.background = hover; val.onHover.textColor = textColor; val.onActive.background = active; val.onActive.textColor = textColor; val.onFocused.background = bg; val.onFocused.textColor = textColor; return val; } private static GUIStyle MakeChip(Texture2D bg, Color textColor) { //IL_000a: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_003b: 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_004a: Expected O, but got Unknown //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_0059: Expected O, but got Unknown //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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 11, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, padding = new RectOffset(8, 8, 3, 3), margin = new RectOffset(0, 0, 2, 2), border = new RectOffset(8, 8, 8, 8) }; val.normal.background = bg; val.normal.textColor = textColor; return val; } private static GUIStyle MakeRow(Texture2D bg) { //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_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //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) //IL_0032: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown GUIStyle val = new GUIStyle { padding = new RectOffset(8, 6, 4, 4), margin = new RectOffset(0, 0, 1, 1), border = new RectOffset(2, 2, 2, 2) }; val.normal.background = bg; return val; } private static void SetTextColorAllStates(GUIStyle s, Color c) { //IL_0006: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_004e: 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) s.normal.textColor = c; s.hover.textColor = c; s.active.textColor = c; s.focused.textColor = c; s.onNormal.textColor = c; s.onHover.textColor = c; s.onActive.textColor = c; s.onFocused.textColor = c; } public static Texture2D MakeRounded(int w, int h, int radius, Color fill, Color border, int borderWidth) { //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) return MakeCore(w, h, radius, radius, radius, radius, fill, border, borderWidth); } private static Texture2D MakeRoundedTop(int w, int h, int radius, Color fill) { //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) return MakeCore(w, h, radius, radius, 0f, 0f, fill, fill, 0f); } private static Texture2D MakeCore(int w, int h, float rTL, float rTR, float rBR, float rBL, Color fill, Color border, float bw) { //IL_0090: 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) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_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_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: 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_00b2: 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_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) Color[] array = (Color[])(object)new Color[w * h]; float num = (float)w * 0.5f; float num2 = (float)h * 0.5f; Color val = default(Color); for (int i = 0; i < h; i++) { float py = (float)i + 0.5f - num2; int num3 = h - 1 - i; for (int j = 0; j < w; j++) { float num4 = SdRoundBox((float)j + 0.5f - num, py, num, num2, rTL, rTR, rBR, rBL); float num5 = Coverage(num4); float num6 = ((bw > 0f) ? Coverage(num4 + bw) : num5); float num7 = Mathf.Max(0f, num5 - num6); float num8 = num6; float num9 = fill.a * num8 + border.a * num7; if (num9 <= 0.0001f) { ((Color)(ref val))..ctor(fill.r, fill.g, fill.b, 0f); } else { float num10 = fill.r * fill.a * num8 + border.r * border.a * num7; float num11 = fill.g * fill.a * num8 + border.g * border.a * num7; float num12 = fill.b * fill.a * num8 + border.b * border.a * num7; ((Color)(ref val))..ctor(num10 / num9, num11 / num9, num12 / num9, num9); } array[num3 * w + j] = val; } } return Bake(w, h, array); } public static Texture2D MakeShadow(int w, int h, int radius, float maxAlpha, float blur) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) Color[] array = (Color[])(object)new Color[w * h]; float num = (float)w * 0.5f; float num2 = (float)h * 0.5f; float bx = num - blur; float num3 = num2 - blur; for (int i = 0; i < h; i++) { float py = (float)i + 0.5f - num2; int num4 = h - 1 - i; for (int j = 0; j < w; j++) { float x = SdRoundBox((float)j + 0.5f - num, py, bx, num3, radius, radius, radius, radius); float num5 = maxAlpha * (1f - SmoothStep01(0f - blur, blur, x)); array[num4 * w + j] = new Color(0f, 0f, 0f, num5); } } return Bake(w, h, array); } private static Texture2D Solid(Color c) { //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) return Bake(1, 1, (Color[])(object)new Color[1] { c }); } private static Texture2D Bake(int w, int h, Color[] px) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown Texture2D val = new Texture2D(w, h, (TextureFormat)5, false) { hideFlags = (HideFlags)61, filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; val.SetPixels(px); val.Apply(false, true); return val; } private static float SdRoundBox(float px, float py, float bx, float by, float rTL, float rTR, float rBR, float rBL) { float num = ((!(px > 0f)) ? ((py > 0f) ? rBL : rTL) : ((py > 0f) ? rBR : rTR)); float num2 = Mathf.Abs(px) - bx + num; float num3 = Mathf.Abs(py) - by + num; float num4 = Mathf.Max(num2, 0f); float num5 = Mathf.Max(num3, 0f); float num6 = Mathf.Sqrt(num4 * num4 + num5 * num5); float num7 = Mathf.Min(Mathf.Max(num2, num3), 0f); return num6 + num7 - num; } private static float Coverage(float d) { return 1f - SmoothStep01(-0.5f, 0.5f, d); } private static float SmoothStep01(float edge0, float edge1, float x) { float num = Mathf.Clamp01((x - edge0) / (edge1 - edge0)); return num * num * (3f - 2f * num); } private static Color Rgb(int r, int g, int b, float a = 1f) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f, a); } private static Color Mul(Color c, float m) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) return new Color(c.r * m, c.g * m, c.b * m, c.a); } private static Color WithA(Color c, float a) { //IL_0000: 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_000c: 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) return new Color(c.r, c.g, c.b, a); } private static Color Lerp(Color a, Color b, float t) { //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_0003: Unknown result type (might be due to invalid IL or missing references) return Color.Lerp(a, b, t); } private static Color Clear() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) return new Color(0f, 0f, 0f, 0f); } } public class CoopUI { public bool Visible = true; public static bool TextFieldFocused; private Rect _win = new Rect(24f, 96f, 400f, 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 KeyCode _hintKeySeen; private string _hintText; private GUIContent _hintGc; private string _errorSeen; private string _errorText; private GUIContent _errorGc; private string _enumRestoreMsg; private string _hostTimeSeen; private string _hostTimeText; private GUIContent _hostTimeGc; private string _promptSeen; private string _promptText; private GUIContent _promptGc; private string _registerSeen; private string _registerText; private GUIContent _registerGc; 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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) //IL_00b7: Expected O, but got Unknown //IL_006a: 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_00c7: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_025d: 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) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Expected O, but got Unknown //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Expected O, but got Unknown //IL_0463: 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_0480: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Expected O, but got Unknown //IL_0494: Unknown result type (might be due to invalid IL or missing references) //IL_0499: 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_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: 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_01bd: 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_0176: Expected O, but got Unknown //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Unknown result type (might be due to invalid IL or missing references) //IL_0436: Unknown result type (might be due to invalid IL or missing references) //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_03f5: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Expected O, but got Unknown CoopTheme.EnsureBuilt(); if (_ipField == null) { _ipField = CoopPlugin.LastJoinIP.Value; } if (_nameField == null) { _nameField = CoopPlugin.PlayerName.Value; } if (!Visible) { TextFieldFocused = false; if (_hintText == null || _hintKeySeen != CoopPlugin.UiToggleKey.Value) { _hintKeySeen = CoopPlugin.UiToggleKey.Value; _hintText = $"CardShopCoop: {_hintKeySeen} for co-op"; _hintGc = new GUIContent(_hintText); } Vector2 val = CoopTheme.PillSize(CoopTheme.HudPill, _hintGc, 440f); GUI.Label(new Rect(8f, (float)Screen.height - val.y - 6f, val.x, val.y), _hintGc, CoopTheme.HudPill); if (core.ErrorLine.Length > 0) { if (core.ErrorLine != _errorSeen) { _errorSeen = core.ErrorLine; _errorText = "CO-OP: " + core.ErrorLine + ""; _errorGc = new GUIContent(_errorText); } Vector2 val2 = CoopTheme.PillSize(CoopTheme.HudPill, _errorGc, 660f); GUI.Label(new Rect(8f, (float)Screen.height - val.y - 6f - val2.y - 6f, val2.x, val2.y), _errorGc, CoopTheme.HudPill); } } if (CoopCore.Role == CoopRole.Client && core.HostTimeLine.Length > 0) { if (core.HostTimeLine != _hostTimeSeen) { _hostTimeSeen = core.HostTimeLine; _hostTimeText = "" + core.HostTimeLine + " - co-op"; _hostTimeGc = new GUIContent(_hostTimeText); } Vector2 val3 = CoopTheme.PillSize(CoopTheme.HudPill, _hostTimeGc, 440f); GUI.Label(new Rect(((float)Screen.width - val3.x) / 2f, 4f, val3.x, val3.y), _hostTimeGc, CoopTheme.HudPill); } if (core.RegisterLine.Length > 0 || core.PromptLine.Length > 0) { if (core.PromptLine.Length > 0) { if (core.PromptLine != _promptSeen) { _promptSeen = core.PromptLine; _promptText = "" + core.PromptLine + ""; _promptGc = new GUIContent(_promptText); } Vector2 val4 = CoopTheme.PillSize(CoopTheme.HudPillBig, _promptGc, 640f); GUI.Label(new Rect(((float)Screen.width - val4.x) / 2f, (float)Screen.height * 0.58f, val4.x, val4.y), _promptGc, CoopTheme.HudPillBig); } if (core.RegisterLine.Length > 0) { if (core.RegisterLine != _registerSeen) { _registerSeen = core.RegisterLine; _registerText = "" + core.RegisterLine + ""; _registerGc = new GUIContent(_registerText); } Vector2 val5 = CoopTheme.PillSize(CoopTheme.HudPillBig, _registerGc, 740f); GUI.Label(new Rect(((float)Screen.width - val5.x) / 2f, (float)Screen.height * 0.63f, val5.x, val5.y), _registerGc, CoopTheme.HudPillBig); } } if (Visible) { CoopTheme.DrawWindowShadow(_win); _win = GUILayout.Window(867530, _win, (WindowFunction)delegate { WindowFn(core, net); }, "", CoopTheme.Window, Array.Empty()); } } private void WindowFn(CoopCore core, ICoopTransport net) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) CoopTheme.EnsureBuilt(); CoopTheme.DrawWindowChrome(new Rect(0f, 0f, ((Rect)(ref _win)).width, ((Rect)(ref _win)).height), "CARD SHOP CO-OP", "v1.0.37"); DrawStatusRow(core, net); if (core.ErrorLine.Length > 0) { GUILayout.BeginHorizontal(Array.Empty()); CoopTheme.Chip("PROBLEM", CoopTheme.ChipDanger); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Label(core.ErrorLine, CoopTheme.LabelDanger, Array.Empty()); } if (_enumRestoreMsg != null) { GUILayout.BeginHorizontal(Array.Empty()); CoopTheme.Chip("CARD DATABASE", CoopTheme.ChipWarn); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Label(_enumRestoreMsg, CoopTheme.LabelWarn, Array.Empty()); GUILayout.Space(4f); } string text = CoopCore.EnumLendState(); if (text != null) { GUILayout.BeginHorizontal(Array.Empty()); CoopTheme.Chip("CARD DATABASE", CoopTheme.ChipWarn); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Label(text, CoopTheme.LabelWarn, Array.Empty()); if (CoopCore.Role == CoopRole.None && GUILayout.Button("Restore MY card database (for solo saves - restart after)", CoopTheme.ButtonDanger, Array.Empty())) { ModParity.RestoreEnumBackup(out _enumRestoreMsg); } GUILayout.Space(4f); } switch (CoopCore.Role) { case CoopRole.None: DrawNone(core); break; case CoopRole.Host: DrawHost(core, net); break; case CoopRole.Client: DrawClient(core); break; } TextFieldFocused = GUI.GetNameOfFocusedControl()?.StartsWith("coop_") ?? false; GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f)); } private void DrawStatusRow(CoopCore core, ICoopTransport net) { ClassifyStatus(core, net, out var chip, out var text); GUILayout.BeginHorizontal(Array.Empty()); if (chip != null) { CoopTheme.Chip(text, chip); GUILayout.Space(6f); GUILayout.Label(core.StatusLine, CoopTheme.LabelDim, Array.Empty()); } else { GUILayout.Label(core.StatusLine, CoopTheme.Label, Array.Empty()); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private static void ClassifyStatus(CoopCore core, ICoopTransport net, out GUIStyle chip, out string text) { chip = null; text = null; string hay = core.StatusLine ?? ""; switch (CoopCore.Role) { case CoopRole.Host: if ((net?.ConnectionCount ?? 0) > 0) { chip = CoopTheme.ChipSuccess; text = "HOSTING"; } else { chip = CoopTheme.ChipInfo; text = "WAITING"; } break; case CoopRole.Client: if (Has(hay, "download") || Has(hay, "loading") || Has(hay, "requesting") || Has(hay, "received") || Has(hay, "Joining") || Has(hay, "Connecting")) { chip = CoopTheme.ChipInfo; text = "CONNECTING"; } else { chip = CoopTheme.ChipSuccess; text = "CONNECTED"; } break; default: if (Has(hay, "Joining") || Has(hay, "Creating") || Has(hay, "Connecting")) { chip = CoopTheme.ChipInfo; text = "CONNECTING"; } break; } } private static bool Has(string hay, string needle) { return hay.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0; } private void DrawNone(CoopCore core) { //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: 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) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) if (_browserOpen) { DrawBrowser(core); return; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Your name:", CoopTheme.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(72f) }); GUI.SetNextControlName("coop_name"); string text = GUILayout.TextField(_nameField, 16, CoopTheme.TextField, Array.Empty()); if (text != _nameField) { _nameField = text; if (text.Trim().Length > 0) { CoopPlugin.PlayerName.Value = text.Trim(); } } GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.BeginVertical(CoopTheme.SectionBox, Array.Empty()); GUILayout.Label("HOST YOUR SHOP", CoopTheme.SectionHeader, Array.Empty()); GUILayout.Label("Load your shop first.", CoopTheme.LabelDim, Array.Empty()); _publicLobby = GUILayout.Toggle(_publicLobby, " public lobby (shows in the browser)", CoopTheme.Toggle, Array.Empty()); if (_publicLobby) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Lobby name:", CoopTheme.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUI.SetNextControlName("coop_lobbyname"); _lobbyNameField = GUILayout.TextField(_lobbyNameField, 28, CoopTheme.TextField, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Password:", CoopTheme.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUI.SetNextControlName("coop_hostpw"); _hostPwField = GUILayout.TextField(_hostPwField, 20, CoopTheme.TextField, Array.Empty()); GUILayout.Label("(blank = open)", CoopTheme.LabelDim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Host via Steam", CoopTheme.ButtonPrimary, Array.Empty())) { core.StartHostingSteam(_publicLobby, _lobbyNameField, _publicLobby ? _hostPwField : ""); } if (GUILayout.Button("Host via LAN", CoopTheme.ButtonSecondary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { core.StartHosting(); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); CoopTheme.Divider(); GUILayout.BeginVertical(CoopTheme.SectionBox, Array.Empty()); GUILayout.Label("JOIN A FRIEND", CoopTheme.SectionHeader, Array.Empty()); GUILayout.Label("Stay on the main menu.", CoopTheme.LabelDim, Array.Empty()); if (GUILayout.Button("Browse public lobbies", CoopTheme.ButtonPrimary, Array.Empty())) { _browserOpen = true; _page = 0; _pwPromptLobby = CSteamID.Nil; core.Lobby.RefreshList(); } GUILayout.Label("Steam friends: just accept the host's invite.", CoopTheme.LabelDim, Array.Empty()); if (core.ErrorLine == "wrong password" && core.LastFailedLobby != CSteamID.Nil) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Password:", CoopTheme.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUI.SetNextControlName("coop_joinpw"); _joinPwField = GUILayout.TextField(_joinPwField, 20, CoopTheme.TextField, Array.Empty()); if (GUILayout.Button("Retry", CoopTheme.ButtonPrimary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) })) { core.JoinSteam(core.LastFailedLobby, _joinPwField); } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); GUI.SetNextControlName("coop_ip"); _ipField = GUILayout.TextField(_ipField, 24, CoopTheme.TextField, Array.Empty()); if (GUILayout.Button("Join LAN", CoopTheme.ButtonPrimary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(84f) })) { core.Join(_ipField); } GUILayout.EndHorizontal(); GUILayout.Label($"LAN port {CoopPlugin.Port.Value} - all players need this mod + the same mods.", CoopTheme.LabelDim, Array.Empty()); GUILayout.EndVertical(); } private void DrawHost(CoopCore core, ICoopTransport net) { //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_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) if (core.IsSteamSession) { GUILayout.Label("Hosting through Steam - no IPs needed.", CoopTheme.Label, Array.Empty()); if (GUILayout.Button("Invite friend (Steam overlay)", CoopTheme.ButtonPrimary, Array.Empty())) { core.OpenSteamInvite(); } GUILayout.Label((net == null || net.ConnectionCount == 0) ? "Waiting for your invite to be accepted..." : PlayersLine(core), CoopTheme.Label, Array.Empty()); if (GUILayout.Button("Wave (" + ((object)CoopPlugin.EmoteKey.Value/*cast due to .constrained prefix*/).ToString() + ")", CoopTheme.ButtonSecondary, Array.Empty())) { core.SendEmote(); } if (GUILayout.Button("Stop hosting", CoopTheme.ButtonDanger, Array.Empty())) { core.Disconnect(); } return; } 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:", CoopTheme.Label, Array.Empty()); if (!_revealIp) { if (GUILayout.Button("click to show IP (hidden for streams)", CoopTheme.ButtonSecondary, Array.Empty())) { _revealIp = true; } } else { GUILayout.Label($"{_lanIps} (port {CoopPlugin.Port.Value})", CoopTheme.Label, Array.Empty()); if (_lanIpsOther.Length > 0) { GUILayout.Label("(other adapters, usually wrong: " + _lanIpsOther + ")", CoopTheme.LabelDim, Array.Empty()); } } GUILayout.Label((net == null || net.ConnectionCount == 0) ? "Waiting for a player..." : PlayersLine(core), CoopTheme.Label, Array.Empty()); if (GUILayout.Button("Wave (" + ((object)CoopPlugin.EmoteKey.Value/*cast due to .constrained prefix*/).ToString() + ")", CoopTheme.ButtonSecondary, Array.Empty())) { core.SendEmote(); } if (GUILayout.Button("Stop hosting", CoopTheme.ButtonDanger, Array.Empty())) { core.Disconnect(); } } private void DrawClient(CoopCore core) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label(PlayersLine(core), CoopTheme.Label, Array.Empty()); 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.", CoopTheme.LabelWrap, Array.Empty()); if (GUILayout.Button("Wave (" + ((object)CoopPlugin.EmoteKey.Value/*cast due to .constrained prefix*/).ToString() + ")", CoopTheme.ButtonSecondary, Array.Empty())) { core.SendEmote(); } if (GUILayout.Button("Leave session", CoopTheme.ButtonDanger, Array.Empty())) { core.Disconnect(); } } private void DrawBrowser(CoopCore core) { //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_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_031a: 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_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("PUBLIC LOBBIES", CoopTheme.SectionHeader, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button(core.Lobby.ListRefreshing ? "..." : "Refresh", CoopTheme.ButtonPrimary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(72f) })) { core.Lobby.RefreshList(); } if (GUILayout.Button("Back", CoopTheme.ButtonSecondary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) })) { _browserOpen = false; _pwPromptLobby = CSteamID.Nil; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Search:", CoopTheme.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) }); GUI.SetNextControlName("coop_search"); string text = GUILayout.TextField(_searchField, 24, CoopTheme.TextField, 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!", CoopTheme.LabelDim, 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.37"; GUILayout.BeginHorizontal(((i & 1) == 0) ? CoopTheme.RowEven : CoopTheme.RowOdd, Array.Empty()); GUILayout.Label((lobbyRow.HasPw ? "[pw] " : "") + lobbyRow.Name, CoopTheme.LabelBold, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); GUILayout.FlexibleSpace(); GUILayout.Label($"{lobbyRow.Players}/{lobbyRow.Max}" + (flag ? "" : (" v" + lobbyRow.Ver + "")), CoopTheme.LabelDim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); GUILayout.Space(6f); GUI.enabled = flag; if (GUILayout.Button("Join", CoopTheme.ButtonPrimary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(56f) })) { 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:", CoopTheme.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUI.SetNextControlName("coop_joinpw"); _joinPwField = GUILayout.TextField(_joinPwField, 20, CoopTheme.TextField, Array.Empty()); if (GUILayout.Button("Go", CoopTheme.ButtonPrimary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) })) { core.JoinSteam(lobbyRow.Id, _joinPwField); _browserOpen = false; _pwPromptLobby = CSteamID.Nil; } GUILayout.EndHorizontal(); } } GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = _page > 0; if (GUILayout.Button("< Prev", CoopTheme.ButtonSecondary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) })) { _page--; } GUI.enabled = _page < num - 1; if (GUILayout.Button("Next >", CoopTheme.ButtonSecondary, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) })) { _page++; } GUI.enabled = true; GUILayout.FlexibleSpace(); GUILayout.Label($"page {_page + 1}/{num} - {list.Count} lobbies", CoopTheme.LabelDim, 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 readonly List HoldTypes = new List(6); 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 static CustomerManager _customers; 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //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_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Expected I4, but got Unknown //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0201: 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.Clear(); if (holdTypes != null) { for (int i = 0; i < holdTypes.Count; i++) { value.HoldTypes.Add(holdTypes[i]); } } 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) ? "" : ((value.HoldTypes.Count >= 2) ? (value.HoldTypes[0] + ":" + value.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 && value.HoldTypes.Count > 0) ? string.Join(",", value.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_041a: Unknown result type (might be due to invalid IL or missing references) //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_046d: Unknown result type (might be due to invalid IL or missing references) //IL_06ec: 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_06f7: Unknown result type (might be due to invalid IL or missing references) //IL_06fc: Unknown result type (might be due to invalid IL or missing references) //IL_0626: Unknown result type (might be due to invalid IL or missing references) //IL_0646: Unknown result type (might be due to invalid IL or missing references) //IL_072a: 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_0735: Unknown result type (might be due to invalid IL or missing references) //IL_073a: 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_0345: 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.Count >= 1 && value.HoldTypes[0] == 1; int itemType = ((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) { for (int j = 0; j < value.HoldTypes.Count; j++) { try { if (value.HoldTypes[j] == -1) { continue; } 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_04f5: Unknown result type (might be due to invalid IL or missing references) //IL_0506: Unknown result type (might be due to invalid IL or missing references) //IL_0572: Unknown result type (might be due to invalid IL or missing references) //IL_05a2: Unknown result type (might be due to invalid IL or missing references) //IL_0545: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_customers == (Object)null) { _customers = Object.FindObjectOfType(); } CustomerManager customers = _customers; if ((Object)(object)customers == (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 ? customers.m_CustomerFemalePrefab : customers.m_CustomerPrefab); if ((Object)(object)val == (Object)null) { val = (((Object)(object)customers.m_CustomerPrefab != (Object)null) ? customers.m_CustomerPrefab : customers.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 ushort Id; public int Type; public int Count; public bool IsBig; public bool IsOpen; public bool Carried; public bool Settled; public bool Stored; public byte StoreShelf; public byte StoreComp; public Vector3 Pos; public float Yaw; public bool Unmapped; } public struct HostBoxWhere { public bool Tracked; public ushort Id; public bool Stored; public int Shelf; public int Comp; } 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 static readonly FieldInfo FiBeingHold = AccessTools.Field(typeof(InteractableObject), "m_IsBeingHold"); private static readonly FieldInfo FiPreventWorkerTake = AccessTools.Field(typeof(InteractablePackagingBox_Item), "m_PreventWorkerTakeBox"); private readonly List _lastApplied = new List(); private readonly Dictionary _byId = new Dictionary(); private readonly Dictionary _idOf = new Dictionary(); private readonly HashSet _carriedLastTick = new HashSet(); private readonly Dictionary _recentlyReleased = new Dictionary(); private readonly Dictionary _locallyTouched = new Dictionary(); private readonly HashSet _snapshotIds = new HashSet(); private readonly List _removeScratch = new List(); private readonly Dictionary _prevApplied = new Dictionary(); private readonly HashSet _skippedIds = new HashSet(); private readonly List _orphanScratch = new List(); private readonly Dictionary _hostWhereScratch = new Dictionary(); private double _lastCapWarn; private double _lastCapSkipLog; private readonly Dictionary _hostIds = new Dictionary(); private readonly Dictionary _hostById = new Dictionary(); private ushort _nextId = 1; private readonly HashSet _remoteCarried = new HashSet(); private readonly HashSet _hostCarriedLastTick = new HashSet(); private readonly Dictionary _hostRecentlyReleased = new Dictionary(); private readonly Dictionary _remoteReleased = 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; private static ShelfManager _sm; private static double _lastResolveWarn; private static readonly Dictionary _storeFails = new Dictionary(); private static readonly HashSet _storeGaveUp = new HashSet(); public static Func HostLocationOf = (InteractablePackagingBox_Item _) => default(HostBoxWhere); private static readonly Dictionary _remWindowStart = new Dictionary(); private static readonly Dictionary _remWindowCount = new Dictionary(); private static readonly HashSet _underMapLogged = new HashSet(); private static bool IsBeingHeld(InteractablePackagingBox_Item box) { try { object obj = FiBeingHold?.GetValue(box); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static void SetHostWorkerLock(InteractablePackagingBox_Item box, bool locked) { if ((Object)(object)box == (Object)null) { return; } try { FiPreventWorkerTake?.SetValue(box, locked); } catch { } } public void HostReleaseRemoteCarried() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_00a4: Unknown result type (might be due to invalid IL or missing references) if (_remoteCarried.Count == 0) { return; } int num = 0; double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; foreach (ushort item in _remoteCarried) { _remoteReleased[item] = realtimeSinceStartupAsDouble; if (!_hostById.TryGetValue(item, out var value) || (Object)(object)value == (Object)null) { continue; } SetHostWorkerLock(value, locked: false); try { if (((Component)value).transform.position.y < -2f) { Vector3 position = ((Component)value).transform.position; ((Component)value).transform.position = new Vector3(position.x, 0.5f, position.z); } } catch { } try { if (!((Component)value).gameObject.activeSelf) { ((Component)value).gameObject.SetActive(true); } } catch { } try { ((InteractablePackagingBox)value).SetPhysicsEnabled(true); } catch { } num++; } _remoteCarried.Clear(); if (num > 0) { CoopPlugin.Log.LogInfo((object)$"BoxSync host: released {num} client-carried box(es) after a disconnect"); ForceBroadcastNextTick(); } } public void Reset() { _lastApplied.Clear(); _byId.Clear(); _idOf.Clear(); _carriedLastTick.Clear(); _recentlyReleased.Clear(); _locallyTouched.Clear(); _prevApplied.Clear(); _skippedIds.Clear(); foreach (ushort item in _remoteCarried) { if (_hostById.TryGetValue(item, out var value) && (Object)(object)value != (Object)null) { SetHostWorkerLock(value, locked: false); } } _hostIds.Clear(); _hostById.Clear(); _nextId = 1; _remoteCarried.Clear(); _remoteReleased.Clear(); _hostCarriedLastTick.Clear(); _hostRecentlyReleased.Clear(); _remWindowStart.Clear(); _remWindowCount.Clear(); _timer = -0.6f; _lastHostHash = 0; _hostHeal = 0f; _rm = null; _storeFails.Clear(); _storeGaveUp.Clear(); _underMapLogged.Clear(); _sm = null; _lastResolveWarn = 0.0; _hostWhereScratch.Clear(); HostLocationOf = (InteractablePackagingBox_Item _) => default(HostBoxWhere); } public void ForceBroadcastNextTick() { _lastHostHash = 0; _timer = 1.5f; } 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected I4, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_013a: 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_002d: Unknown result type (might be due to invalid IL or missing references) bool flag = true; try { Rigidbody rigidbody = ((InteractablePackagingBox)box).m_Rigidbody; int num; if (!((Object)(object)rigidbody == (Object)null) && !rigidbody.isKinematic && !rigidbody.IsSleeping()) { Vector3 velocity = rigidbody.velocity; num = ((((Vector3)(ref velocity)).sqrMagnitude < 0.04f) ? 1 : 0); } else { num = 1; } flag = (byte)num != 0; } catch { } bool flag2 = false; int num2 = 0; int num3 = 0; try { if (box.m_IsStored) { ShelfCompartment boxStoredCompartment = box.GetBoxStoredCompartment(); if ((Object)(object)boxStoredCompartment != (Object)null) { flag2 = true; num2 = boxStoredCompartment.GetWarehouseIndex(); num3 = boxStoredCompartment.GetIndex(); } } } catch { } return new Entry { Type = (int)box.m_ItemCompartment.GetItemType(), Count = box.m_ItemCompartment.GetItemCount(), IsBig = box.m_IsBigBox, IsOpen = ((InteractablePackagingBox)box).IsBoxOpened(), Carried = (!flag2 && (IsLocallyCarried(box) || IsBeingHeld(box))), Settled = (flag2 || flag), Stored = flag2, StoreShelf = (byte)Mathf.Clamp(num2, 0, 255), StoreComp = (byte)Mathf.Clamp(num3, 0, 255), Pos = ((Component)box).transform.position, Yaw = ((Component)box).transform.eulerAngles.y }; } private static bool Differs(Entry a, Entry b) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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) { return true; } if (a.Stored != b.Stored) { return true; } if (a.Stored) { if (a.StoreShelf == b.StoreShelf) { return a.StoreComp != b.StoreComp; } return true; } 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; } private static bool EnsureCompartmentType(InteractablePackagingBox_Item box, int wantType) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected I4, but got Unknown //IL_0040: 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_0049: Unknown result type (might be due to invalid IL or missing references) try { if (wantType == -1) { return false; } ShelfCompartment itemCompartment = box.m_ItemCompartment; int num = (int)itemCompartment.GetItemType(); bool flag = itemCompartment.GetItemPosListCount() > 0; if (num == wantType && flag) { return true; } if (itemCompartment.GetItemCount() > 0) { return num == wantType; } EItemType val = (EItemType)wantType; box.SetItemType(val); itemCompartment.SetCompartmentItemType(val); itemCompartment.CalculatePositionList(); return true; } catch { return false; } } private static void ApplyClosedCount(InteractablePackagingBox_Item box, int count) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) try { ShelfCompartment itemCompartment = box.m_ItemCompartment; if (itemCompartment.GetItemCount() == count) { return; } bool flag = true; try { flag = box.m_IsStored || !((InteractablePackagingBox)box).IsBoxOpened(); } catch { } if (flag && count > 0 && itemCompartment.GetItemPosListCount() <= 0) { try { itemCompartment.SetCompartmentItemType(itemCompartment.GetItemType()); itemCompartment.CalculatePositionList(); } catch { } } itemCompartment.PreSpawnItemUpdate(count); FiAmountToSpawn?.SetValue(box, count); } catch { } } private static void UnhookIfStored(InteractablePackagingBox_Item box) { try { if (!((Object)(object)box == (Object)null) && box.m_IsStored) { ShelfCompartment boxStoredCompartment = box.GetBoxStoredCompartment(); if ((Object)(object)boxStoredCompartment != (Object)null) { boxStoredCompartment.RemoveBox(box); } box.m_IsStored = false; } } catch { } } private static ShelfCompartment ResolveWarehouseCompartment(int shelfIdx, int compIdx) { try { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } ShelfManager sm = _sm; if ((Object)(object)sm == (Object)null) { return null; } List warehouseShelfList = sm.m_WarehouseShelfList; for (int i = 0; i < warehouseShelfList.Count; i++) { WarehouseShelf val = warehouseShelfList[i]; if (!((Object)(object)val == (Object)null) && val.GetIndex() == shelfIdx) { return val.GetWarehouseCompartment(compIdx); } } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (realtimeSinceStartupAsDouble - _lastResolveWarn > 30.0) { _lastResolveWarn = realtimeSinceStartupAsDouble; CoopPlugin.Log.LogWarning((object)$"BoxSync store: no warehouse rack with index {shelfIdx} (have {warehouseShelfList.Count}) - retrying on next snapshot"); } } catch { } return null; } private static bool TryEvictGhostAndRetryStore(InteractablePackagingBox_Item box, ShelfCompartment rackComp, Entry want) { try { List interactablePackagingBoxList = rackComp.GetInteractablePackagingBoxList(); int num = -1; int num2 = -1; try { num = rackComp.GetWarehouseIndex(); } catch { } try { num2 = rackComp.GetIndex(); } catch { } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append($"BoxSync store DIAG: box id {want.Id} rejected by rack want={want.StoreShelf}/{want.StoreComp} ").Append($"resolved warehouseIdx={num} compIdx={num2} ").Append($"occupants={interactablePackagingBoxList?.Count ?? 0}: "); bool flag = false; if (interactablePackagingBoxList != null) { List list = new List(interactablePackagingBoxList); for (int i = 0; i < list.Count; i++) { InteractablePackagingBox_Item val = list[i]; if ((Object)(object)val == (Object)null) { stringBuilder.Append("[null] "); continue; } HostBoxWhere hostBoxWhere = HostLocationOf(val); if (!hostBoxWhere.Tracked) { stringBuilder.Append("[untracked] "); continue; } string arg = (hostBoxWhere.Stored ? $"{hostBoxWhere.Shelf}/{hostBoxWhere.Comp}" : "not-stored"); bool flag2 = !hostBoxWhere.Stored || hostBoxWhere.Shelf != want.StoreShelf || hostBoxWhere.Comp != want.StoreComp; stringBuilder.Append(string.Format("[id {0} host={1}{2}] ", hostBoxWhere.Id, arg, flag2 ? " GHOST" : "")); if (!flag2 || val == box) { continue; } UnhookIfStored(val); try { ((Component)val).transform.SetParent((Transform)null); } catch { } try { ((InteractablePackagingBox)val).SetPhysicsEnabled(true); } catch { } try { if ((Object)(object)((InteractableObject)val).m_MoveStateValidArea != (Object)null) { ((Component)((InteractableObject)val).m_MoveStateValidArea).gameObject.SetActive(true); } } catch { } try { val.m_ItemCompartment.SetPriceTagVisibility(((Component)val).gameObject.activeSelf); } catch { } flag = true; } } CoopPlugin.Log.LogWarning((object)stringBuilder.ToString()); if (!flag) { return false; } try { box.DispenseItem(false, rackComp); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync store evict-retry: " + ex.Message)); } return true; } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("BoxSync store evict: " + ex2.Message)); return false; } } private ushort HostIdFor(InteractablePackagingBox_Item box) { if (_hostIds.TryGetValue(box, out var value)) { return value; } do { value = _nextId++; if (_nextId == 0) { _nextId = 1; } } while (value == 0 || _hostById.ContainsKey(value)); _hostIds[box] = value; _hostById[value] = box; return value; } private void HostPruneDead() { _removeScratch.Clear(); foreach (KeyValuePair item in _hostById) { if ((Object)(object)item.Value == (Object)null) { _removeScratch.Add(item.Key); } } for (int i = 0; i < _removeScratch.Count; i++) { ushort num = _removeScratch[i]; if (_hostById.TryGetValue(num, out var value) && value != null) { _hostIds.Remove(value); } _hostById.Remove(num); _remoteCarried.Remove(num); _remoteReleased.Remove(num); _hostCarriedLastTick.Remove(num); _hostRecentlyReleased.Remove(num); } } public void HostTick(float dt, bool active) { //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: 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; } ushort num = HostIdFor(list[i]); if (IsLocallyCarried(list[i])) { if (_hostCarriedLastTick.Add(num)) { flag = true; } } else if (_hostCarriedLastTick.Remove(num)) { flag = true; _hostRecentlyReleased[num] = 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(); if (list2.Count > 1000 && Time.realtimeSinceStartupAsDouble - _lastCapWarn > 60.0) { _lastCapWarn = Time.realtimeSinceStartupAsDouble; CoopPlugin.Log.LogWarning((object)$"BoxSync host: {list2.Count} live boxes exceed the 1000-box sync cap - boxes past the cap will not sync to guests"); } List list3 = new List(Mathf.Min(list2.Count, 1000)); for (int j = 0; j < list2.Count; j++) { if (list3.Count >= 1000) { break; } if (!((Object)(object)list2[j] == (Object)null)) { Entry item = Snapshot(list2[j]); item.Id = HostIdFor(list2[j]); if (_remoteCarried.Contains(item.Id)) { item.Carried = true; item.Stored = false; SetHostWorkerLock(list2[j], locked: true); } list3.Add(item); } } int num2 = 17; for (int k = 0; k < list3.Count; k++) { Entry entry = list3[k]; num2 = num2 * 31 + entry.Id; num2 = num2 * 31 + entry.Type; num2 = num2 * 31 + entry.Count; num2 = num2 * 31 + (int)((entry.IsBig ? 1u : 0u) | (uint)(entry.IsOpen ? 2 : 0) | (uint)(entry.Carried ? 4 : 0) | (uint)(entry.Settled ? 8 : 0) | (uint)(entry.Stored ? 16 : 0)); num2 = num2 * 31 + entry.StoreShelf * 311 + entry.StoreComp; num2 = num2 * 31 + (int)(entry.Pos.x * 8f); num2 = num2 * 31 + (int)(entry.Pos.y * 8f); num2 = num2 * 31 + (int)(entry.Pos.z * 8f); } _hostHeal += 1.5f; if (num2 != _lastHostHash || !(_hostHeal < 10f)) { _lastHostHash = num2; if (_hostHeal >= 10f) { HostPruneDead(); } _hostHeal = 0f; OnHostSnapshot?.Invoke(list3); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync host: " + ex.Message)); } } public void HostApplyRequest(List entries) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected I4, but got Unknown for (int i = 0; i < entries.Count; i++) { Entry want = entries[i]; if (!_hostById.TryGetValue(want.Id, out var value) || (Object)(object)value == (Object)null) { continue; } if (want.Carried) { _remoteCarried.Add(want.Id); SetHostWorkerLock(value, locked: true); } else { if (_remoteCarried.Remove(want.Id)) { _remoteReleased[want.Id] = Time.realtimeSinceStartupAsDouble; } SetHostWorkerLock(value, locked: false); } int num = (int)value.m_ItemCompartment.GetItemType(); if ((num == want.Type || num == -1 || value.m_ItemCompartment.GetItemCount() <= 0) && !IsLocallyCarried(value) && !IsBeingHeld(value) && !((InteractableObject)value).GetIsMovingObject() && (!_hostRecentlyReleased.TryGetValue(want.Id, out var value2) || !(Time.realtimeSinceStartupAsDouble - value2 < 6.0))) { bool flag = false; try { flag = ((InteractablePackagingBox)value).IsBoxOpened(); } catch { } double value3; bool applyContent = _remoteCarried.Contains(want.Id) || (_remoteReleased.TryGetValue(want.Id, out value3) && Time.realtimeSinceStartupAsDouble - value3 < 6.0) || !flag; ApplyToBox(value, want, applyPosition: true, hostAuthoritative: true, applyContent); } } _timer = 1.5f; _lastHostHash = 0; } public static bool RemovalFlooded(int connId, string channel, int refusedId = -1) { double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (!_remWindowStart.TryGetValue(connId, out var value) || realtimeSinceStartupAsDouble - value > 2.0) { _remWindowStart[connId] = realtimeSinceStartupAsDouble; _remWindowCount[connId] = 0; } int num = (_remWindowCount[connId] += 1); int num3 = num; if (num3 <= 16) { return false; } if (num3 == 17 || num3 % 100 == 0) { CoopPlugin.Log.LogWarning((object)("ignoring " + channel + " removal" + ((refusedId >= 0) ? $" of box id {refusedId}" : "") + $" from client {connId} - removal budget spent for this 2s window (reload echo, not gameplay)")); } return true; } public void HostApplyRemoval(int id, int type, int connId) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 if (RemovalFlooded(connId, "item-box", id) || !_hostById.TryGetValue((ushort)id, out var value) || (Object)(object)value == (Object)null || (int)value.m_ItemCompartment.GetItemType() != type || IsLocallyCarried(value) || IsBeingHeld(value)) { return; } ApplyingRemote = true; try { UnhookIfStored(value); ((InteractableObject)value).OnDestroyed(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync removal: " + ex.Message)); } finally { ApplyingRemote = false; } _hostIds.Remove(value); _hostById.Remove((ushort)id); _remoteCarried.Remove((ushort)id); _remoteReleased.Remove((ushort)id); _hostCarriedLastTick.Remove((ushort)id); _hostRecentlyReleased.Remove((ushort)id); } public void HostNotifyLocalDestroyed() { HostPruneDead(); } 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 if (!_idOf.TryGetValue(box, out var value)) { return; } int arg = 0; try { arg = (int)box.m_ItemCompartment.GetItemType(); } catch { } _idOf.Remove(box); _byId.Remove(value); _carriedLastTick.Remove(value); _locallyTouched.Remove(value); _recentlyReleased.Remove(value); for (int i = 0; i < _lastApplied.Count; i++) { if (_lastApplied[i].Id == value) { _lastApplied.RemoveAt(i); break; } } OnLocalRemoved?.Invoke(value, arg); } public void ClientApply(List hostList) { ApplyingRemote = true; _hostWhereScratch.Clear(); for (int i = 0; i < hostList.Count; i++) { _hostWhereScratch[hostList[i].Id] = hostList[i]; } HostLocationOf = delegate(InteractablePackagingBox_Item occupant) { HostBoxWhere result = default(HostBoxWhere); try { if ((Object)(object)occupant == (Object)null || !_idOf.TryGetValue(occupant, out var value)) { return result; } result.Tracked = true; result.Id = value; if (_hostWhereScratch.TryGetValue(value, out var value2)) { result.Stored = value2.Stored; result.Shelf = value2.StoreShelf; result.Comp = value2.StoreComp; } } catch { } return result; }; try { ClientApplyInner(hostList); } finally { ApplyingRemote = false; HostLocationOf = (InteractablePackagingBox_Item _) => default(HostBoxWhere); } } private void ClientApplyInner(List hostList) { //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Invalid comparison between Unknown and I4 //IL_0645: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Invalid comparison between Unknown and I4 _prevApplied.Clear(); for (int i = 0; i < _lastApplied.Count; i++) { _prevApplied[_lastApplied[i].Id] = _lastApplied[i]; } _skippedIds.Clear(); _removeScratch.Clear(); foreach (KeyValuePair item3 in _byId) { if ((Object)(object)item3.Value == (Object)null) { _removeScratch.Add(item3.Key); } } for (int j = 0; j < _removeScratch.Count; j++) { ushort key = _removeScratch[j]; if (_byId.TryGetValue(key, out var value) && value != null) { _idOf.Remove(value); } _byId.Remove(key); } _orphanScratch.Clear(); List list = LiveBoxes(); for (int k = 0; k < list.Count; k++) { InteractablePackagingBox_Item val = list[k]; if ((Object)(object)val != (Object)null && !_idOf.ContainsKey(val)) { _orphanScratch.Add(val); } } if (_orphanScratch.Count > 0) { for (int l = 0; l < hostList.Count; l++) { Entry entry = hostList[l]; if ((_byId.TryGetValue(entry.Id, out var value2) && (Object)(object)value2 != (Object)null) || entry.Unmapped) { continue; } for (int m = 0; m < _orphanScratch.Count; m++) { InteractablePackagingBox_Item val2 = _orphanScratch[m]; if (!((Object)(object)val2 == (Object)null) && (int)val2.m_ItemCompartment.GetItemType() == entry.Type && val2.m_IsBigBox == entry.IsBig) { _byId[entry.Id] = val2; _idOf[val2] = entry.Id; _orphanScratch[m] = null; break; } } } } _snapshotIds.Clear(); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; for (int n = 0; n < hostList.Count; n++) { Entry want = hostList[n]; _snapshotIds.Add(want.Id); if (want.Unmapped) { continue; } _byId.TryGetValue(want.Id, out var value3); if ((Object)(object)value3 != (Object)null && ((int)value3.m_ItemCompartment.GetItemType() != want.Type || value3.m_IsBigBox != want.IsBig)) { if (IsLocallyCarried(value3)) { _skippedIds.Add(want.Id); continue; } _idOf.Remove(value3); UnhookIfStored(value3); try { ((InteractableObject)value3).OnDestroyed(); } catch { } value3 = null; } if ((Object)(object)value3 == (Object)null) { try { if (want.Type >= 200000) { CoopPlugin.Log.LogInfo((object)$"BoxSync client: spawning modded-item box id {want.Id} type {want.Type} (EPL virtual id)"); } value3 = RestockManager.SpawnPackageBoxItem((EItemType)want.Type, want.Count, want.IsBig); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync spawn: " + ex.Message)); continue; } if ((Object)(object)value3 == (Object)null) { continue; } _byId[want.Id] = value3; _idOf[value3] = want.Id; } double value4; double value5; if (IsLocallyCarried(value3)) { _skippedIds.Add(want.Id); } else if (want.Carried && _recentlyReleased.TryGetValue(want.Id, out value4) && realtimeSinceStartupAsDouble - value4 < 6.0) { _skippedIds.Add(want.Id); } else if (_locallyTouched.TryGetValue(want.Id, out value5) && realtimeSinceStartupAsDouble - value5 < 6.0) { if (!want.Carried && !((Component)value3).gameObject.activeSelf) { ((Component)value3).gameObject.SetActive(true); try { value3.m_ItemCompartment.SetPriceTagVisibility(true); } catch { } } else if (want.Carried && ((Component)value3).gameObject.activeSelf) { try { value3.m_ItemCompartment.SetPriceTagVisibility(false); } catch { } ((Component)value3).gameObject.SetActive(false); } _skippedIds.Add(want.Id); } else { if (want.Carried) { _skippedIds.Add(want.Id); } ApplyToBox(value3, want, !want.Carried); } } bool num = hostList.Count >= 1000; if (num && Time.realtimeSinceStartupAsDouble - _lastCapSkipLog > 60.0) { _lastCapSkipLog = Time.realtimeSinceStartupAsDouble; CoopPlugin.Log.LogInfo((object)"BoxSync client: snapshot rode the 1000-box cap - skipping the absent-box sweep (can't tell destroyed from truncated)"); } if (!num) { _removeScratch.Clear(); foreach (KeyValuePair item4 in _byId) { if (!_snapshotIds.Contains(item4.Key)) { _removeScratch.Add(item4.Key); } } for (int num2 = 0; num2 < _removeScratch.Count; num2++) { ushort num3 = _removeScratch[num2]; if (!_byId.TryGetValue(num3, out var value6)) { continue; } if ((Object)(object)value6 != (Object)null) { if (IsLocallyCarried(value6)) { CoopPlugin.Log.LogWarning((object)$"host removed the box in your hands (id {num3}, {value6.m_ItemCompartment.GetItemType()}) - it was consumed host-side"); try { CoopCore.ForceExitHoldBox((Object)(object)value6); } catch { } } _idOf.Remove(value6); UnhookIfStored(value6); try { ((InteractableObject)value6).OnDestroyed(); } catch { } } _byId.Remove(num3); _carriedLastTick.Remove(num3); _locallyTouched.Remove(num3); _recentlyReleased.Remove(num3); } } _lastApplied.Clear(); for (int num4 = 0; num4 < hostList.Count; num4++) { Entry item = hostList[num4]; if (_skippedIds.Count > 0 && _skippedIds.Contains(item.Id) && _byId.TryGetValue(item.Id, out var value7) && (Object)(object)value7 != (Object)null) { Entry item2; try { item2 = Snapshot(value7); } catch { _lastApplied.Add(item); continue; } item2.Id = item.Id; item2.Carried = item.Carried; item2.Stored = item.Stored; item2.StoreShelf = item.StoreShelf; item2.StoreComp = item.StoreComp; _lastApplied.Add(item2); } else { _lastApplied.Add(item); } } } public void ClientTick(float dt, bool active) { if (!active || (Object)(object)Rm() == (Object)null || _lastApplied.Count == 0) { return; } bool flag = false; try { foreach (KeyValuePair item2 in _idOf) { if ((Object)(object)item2.Key == (Object)null) { continue; } if (IsLocallyCarried(item2.Key)) { if (_carriedLastTick.Add(item2.Value)) { flag = true; } } else if (_carriedLastTick.Remove(item2.Value)) { flag = true; _recentlyReleased[item2.Value] = Time.realtimeSinceStartupAsDouble; } } } catch { } _timer += dt; if (!flag && _timer < 1.5f) { return; } if (_timer >= 1.5f) { _timer -= 1.5f; } try { bool flag2 = flag; _reportBuf.Clear(); List reportBuf = _reportBuf; double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; for (int i = 0; i < _lastApplied.Count; i++) { Entry entry = _lastApplied[i]; _byId.TryGetValue(entry.Id, out var value); if ((Object)(object)value == (Object)null) { continue; } double value2; bool flag3 = _locallyTouched.TryGetValue(entry.Id, out value2) && realtimeSinceStartupAsDouble - value2 < 6.0; double value3; bool flag4 = _recentlyReleased.TryGetValue(entry.Id, out value3) && realtimeSinceStartupAsDouble - value3 < 6.0; if (IsLocallyCarried(value)) { Entry item = entry; item.Carried = true; item.Stored = false; reportBuf.Add(item); continue; } if (entry.Stored) { bool flag5 = true; try { flag5 = value.m_IsStored; } catch { } if (!(!flag5 && flag4)) { if (flag3) { reportBuf.Add(entry); flag2 = true; } continue; } } if (entry.Carried && !flag4) { continue; } Entry entry2 = Snapshot(value); entry2.Id = entry.Id; bool num = Differs(entry2, entry); if (num) { flag2 = true; _locallyTouched[entry.Id] = realtimeSinceStartupAsDouble; } if (num || flag3 || flag4) { reportBuf.Add(entry2); if (flag4) { flag2 = true; } } } if (flag2) { OnClientChanges?.Invoke(reportBuf); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync client: " + ex.Message)); } } private static bool UnderMapPose(Entry want) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (want.Pos.y >= -2f) { return false; } if (_underMapLogged.Add(want.Id)) { CoopPlugin.Log.LogWarning((object)$"BoxSync: refusing under-map pose (y={want.Pos.y:F2}) for box id {want.Id} - keeping its current position"); } return true; } private static void ApplyToBox(InteractablePackagingBox_Item box, Entry want, bool applyPosition = true, bool hostAuthoritative = false, bool applyContent = true) { //IL_02ab: 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) //IL_0619: Unknown result type (might be due to invalid IL or missing references) //IL_061f: Unknown result type (might be due to invalid IL or missing references) //IL_0624: Unknown result type (might be due to invalid IL or missing references) //IL_0629: Unknown result type (might be due to invalid IL or missing references) //IL_065f: 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_063b: Unknown result type (might be due to invalid IL or missing references) //IL_06a2: Unknown result type (might be due to invalid IL or missing references) //IL_06ae: Unknown result type (might be due to invalid IL or missing references) try { if (hostAuthoritative && want.Stored && !want.Carried) { bool flag = false; try { flag = box.m_IsStored; } catch { } if (!flag) { ShelfCompartment val = ResolveWarehouseCompartment(want.StoreShelf, want.StoreComp); if ((Object)(object)val != (Object)null) { try { if (!((Component)box).gameObject.activeSelf) { ((Component)box).gameObject.SetActive(true); } if (box.m_ItemCompartment.GetItemCount() <= 0) { ApplyClosedCount(box, Mathf.Max(want.Count, 1)); } ((InteractablePackagingBox)box).SetPhysicsEnabled(false); box.DispenseItem(false, val); bool flag2 = false; try { flag2 = box.m_IsStored; } catch { } if (!flag2) { ((InteractablePackagingBox)box).SetPhysicsEnabled(true); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("BoxSync host store: " + ex.Message)); try { ((InteractablePackagingBox)box).SetPhysicsEnabled(true); } catch { } } } } } if (!hostAuthoritative) { bool flag3 = false; try { flag3 = box.m_IsStored; } catch { } if (want.Stored) { ApplyClosedCount(box, want.Count); if (flag3) { return; } if (!_storeGaveUp.Contains(want.Id)) { if (!((Component)box).gameObject.activeSelf) { ((Component)box).gameObject.SetActive(true); } ShelfCompartment val2 = ResolveWarehouseCompartment(want.StoreShelf, want.StoreComp); if (!((Object)(object)val2 != (Object)null)) { return; } try { ((InteractablePackagingBox)box).SetPhysicsEnabled(false); box.DispenseItem(false, val2); } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("BoxSync store: " + ex2.Message)); } if (box.m_IsStored) { _storeFails.Remove(want.Id); return; } _storeFails.TryGetValue(want.Id, out var value); if (value >= 1 && TryEvictGhostAndRetryStore(box, val2, want) && box.m_IsStored) { _storeFails.Remove(want.Id); return; } ((InteractablePackagingBox)box).SetPhysicsEnabled(true); value = (_storeFails[want.Id] = value + 1); if (value < 4) { return; } _storeGaveUp.Add(want.Id); CoopPlugin.Log.LogWarning((object)$"BoxSync store: rack {want.StoreShelf}/{want.StoreComp} keeps rejecting box id {want.Id} (slot full or size/type mismatch) - pinning it shelved at the host pose"); } try { if (!((Component)box).gameObject.activeSelf) { ((Component)box).gameObject.SetActive(true); try { box.m_ItemCompartment.SetPriceTagVisibility(true); } catch { } } ((InteractablePackagingBox)box).SetPhysicsEnabled(false); if (!UnderMapPose(want)) { ((Component)box).transform.SetPositionAndRotation(want.Pos, Quaternion.Euler(0f, want.Yaw, 0f)); ObjMoveSync.SyncTagGroup(((Component)box).transform); } return; } catch (Exception ex3) { CoopPlugin.Log.LogWarning((object)("BoxSync store pin: " + ex3.Message)); return; } } bool num2 = _storeGaveUp.Count > 0 && _storeGaveUp.Remove(want.Id); if (_storeFails.Count > 0) { _storeFails.Remove(want.Id); } if (num2 && !flag3) { try { ((InteractablePackagingBox)box).SetPhysicsEnabled(true); } catch { } } if (flag3) { UnhookIfStored(box); try { ((Component)box).transform.SetParent((Transform)null); } catch { } try { ((InteractablePackagingBox)box).SetPhysicsEnabled(true); } catch { } try { if ((Object)(object)((InteractableObject)box).m_MoveStateValidArea != (Object)null) { ((Component)((InteractableObject)box).m_MoveStateValidArea).gameObject.SetActive(true); } } catch { } try { box.m_ItemCompartment.SetPriceTagVisibility(((Component)box).gameObject.activeSelf); } catch { } } } else if (want.Carried) { bool flag4 = false; try { flag4 = box.m_IsStored; } catch { } if (flag4) { UnhookIfStored(box); try { ((Component)box).transform.SetParent((Transform)null); } catch { } try { ((InteractablePackagingBox)box).SetPhysicsEnabled(true); } catch { } try { if ((Object)(object)((InteractableObject)box).m_MoveStateValidArea != (Object)null) { ((Component)((InteractableObject)box).m_MoveStateValidArea).gameObject.SetActive(true); } } catch { } try { box.m_ItemCompartment.SetPriceTagVisibility(((Component)box).gameObject.activeSelf); } catch { } } } 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 (applyContent && ((InteractablePackagingBox)box).IsBoxOpened() != want.IsOpen && MiSetOpenClose != null) { try { MiSetOpenClose.Invoke(box, null); } catch { } } ShelfCompartment itemCompartment = box.m_ItemCompartment; int itemCount = itemCompartment.GetItemCount(); if (applyContent && itemCount != want.Count) { if (want.Count > 0) { EnsureCompartmentType(box, want.Type); } 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); } } bool flag5 = false; try { flag5 = box.m_IsStored; } catch { } if (!applyPosition || !want.Settled || want.Stored || flag5 || UnderMapPose(want)) { return; } Transform transform = ((Component)box).transform; Vector3 val3 = transform.position - want.Pos; if (!(((Vector3)(ref val3)).sqrMagnitude > 0.01f) && !(Mathf.Abs(Mathf.DeltaAngle(transform.eulerAngles.y, want.Yaw)) > 3f)) { return; } transform.SetPositionAndRotation(want.Pos, Quaternion.Euler(0f, want.Yaw, 0f)); ObjMoveSync.SyncTagGroup(transform); try { Rigidbody rigidbody = ((InteractablePackagingBox)box).m_Rigidbody; if ((Object)(object)rigidbody != (Object)null && !rigidbody.isKinematic) { rigidbody.velocity = Vector3.zero; rigidbody.angularVelocity = Vector3.zero; rigidbody.WakeUp(); } } catch { } } catch (Exception ex4) { CoopPlugin.Log.LogWarning((object)("BoxSync apply: " + ex4.Message)); } } public static void WriteEntries(BinaryWriter bw, List entries) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) bw.Write((ushort)Mathf.Min(entries.Count, 1000)); for (int i = 0; i < entries.Count && i < 1000; i++) { Entry entry = entries[i]; bw.Write(entry.Id); Msg.WriteItemType(bw, (EItemType)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) | (uint)(entry.Settled ? 8 : 0) | (uint)(entry.Stored ? 16 : 0))); bw.Write(entry.StoreShelf); bw.Write(entry.StoreComp); 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) int num = br.ReadUInt16(); List list = new List(num); for (int i = 0; i < num; i++) { Entry item = new Entry { Id = br.ReadUInt16() }; item.Unmapped = !EnumMap.TryFromWire(EnumKind.ItemType, br.ReadInt32(), out var localId); item.Type = localId; item.Count = br.ReadUInt16(); byte b = br.ReadByte(); item.IsBig = (b & 1) != 0; item.IsOpen = (b & 2) != 0; item.Carried = (b & 4) != 0; item.Settled = (b & 8) != 0; item.Stored = (b & 0x10) != 0; item.StoreShelf = br.ReadByte(); item.StoreComp = br.ReadByte(); 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 InteractionPlayerController _ipc; 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; _ipc = null; } public void ForceResend() { _lastHostHash = 0; _hostHeal = 999f; } public void HostReleaseRemoteCarried() { //IL_008e: 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) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (_remoteCarried.Count == 0) { return; } if ((Object)(object)Rm() == (Object)null) { _remoteCarried.Clear(); return; } List list = LiveBoxes(); int num = 0; foreach (int item in _remoteCarried) { if (item >= 0 && item < list.Count && !((Object)(object)list[item] == (Object)null)) { InteractablePackagingBox_Card val = list[item]; ApplyToBox(val, new Entry { Cards = null, Carried = false, Pos = ((Component)val).transform.position, Yaw = ((Component)val).transform.eulerAngles.y }); num++; } } _remoteCarried.Clear(); if (num > 0) { CoopPlugin.Log.LogInfo((object)$"CardBoxSync host: released {num} client-carried box(es) after a disconnect"); ForceResend(); } } private RestockManager Rm() { if ((Object)(object)_rm == (Object)null) { _rm = Object.FindObjectOfType(); } return _rm; } private InteractionPlayerController Ipc() { if ((Object)(object)_ipc == (Object)null) { _ipc = Object.FindObjectOfType(); } return _ipc; } 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 && !CoopCore.ClientReloading) { 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, int connId) { 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, connId); 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) { continue; } if (!CoopCore.CardSetInstalledHere(list[i])) { CoopCore.WarnRefusedCard(list[i], "card-box"); continue; } 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, int connId) { int index = br.ReadByte(); int cardCount = br.ReadByte(); int cardsHash = br.ReadInt32(); if (BoxSync.RemovalFlooded(connId, "card-box")) { return; } 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 { InteractionPlayerController obj = Ipc(); if (obj != null) { obj.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 Action RequestBoxResync; 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++) { Msg.WriteItemType(bw, (EItemType)(((Object)(object)list2[j] != (Object)null) ? ((int)list2[j].GetItemType()) : (-1))); } 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_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_01c4: 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) //IL_00e1: Invalid comparison between Unknown and I4 //IL_00f7: 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 idx6 = br.ReadByte(); bool canWorkerTake2 = br.ReadBoolean(); InteractableCardStorageShelf val8 = Get(9, idx6); if (!((Object)(object)val8 == (Object)null)) { ApplyingRemote = true; try { val8.SetCanWorkerTake(canWorkerTake2); val8.OnCardStorageShelfSettingDone(); break; } finally { ApplyingRemote = false; } } break; } case 2: { int idx4 = br.ReadByte(); EItemType val3 = Msg.ReadItemType(br); InteractableAutoPackOpener val4 = Get(11, idx4); if ((Object)(object)val4 == (Object)null) { break; } if ((int)val3 == -1) { CoopPlugin.Log.LogWarning((object)"ContainerSync pack insert: item type has no counterpart here (one-sided content pack) - skipped"); break; } Item val5 = SpawnItem(val3, val4.m_PosInside); if ((Object)(object)val5 == (Object)null) { break; } ApplyingRemote = true; try { val4.AddItem(val5, true, false); break; } finally { ApplyingRemote = false; } } case 3: { int idx7 = br.ReadByte(); InteractableAutoPackOpener val9 = Get(11, idx7); if ((Object)(object)val9 != (Object)null && !val9.GetIsProcessing() && val9.GetStoredItemList().Count > 0) { ((InteractableObject)val9).OnMouseButtonUp(); } break; } case 4: { int idx2 = br.ReadByte(); List revealed = ReadCards(br); HostApplyPackCollect(idx2, revealed); break; } case 5: { int num4 = br.ReadByte(); Vector3 reqPos = default(Vector3); ((Vector3)(ref reqPos))..ctor(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); HostApplyBoxTake(num4, reqPos); _lastHash.Remove(0xC00 | num4); break; } case 6: { int num2 = br.ReadByte(); InteractableEmptyBoxStorage val7 = Get(12, num2); if (!((Object)(object)val7 == (Object)null)) { int num3 = (FiEbMax?.GetValue(val7) as int?) ?? 200; if (val7.GetBoxStoredCount() < num3) { FiEbCount?.SetValue(val7, val7.GetBoxStoredCount() + 1); MiEbEval?.Invoke(val7, null); _lastHash.Remove(0xC00 | num2); } } break; } case 7: { int idx5 = br.ReadByte(); bool flag = br.ReadBoolean(); InteractableAutoCleanser val6 = Get(10, idx5); if (!((Object)(object)val6 == (Object)null)) { FiClTurnedOn?.SetValue(val6, flag); if (!flag) { FiClCooldown?.SetValue(val6, true); FiClTimer?.SetValue(val6, 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_0060: 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) { 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); } _lastHash.Remove(0xB00 | idx); } 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); RequestBoxResync?.Invoke(); } } public void ClientApplyState(BinaryReader br) { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected I4, but got Unknown 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 cards2 = ReadCards(br); InteractableCardStorageShelf val4 = Get(num2, num3); if (!((Object)(object)val4 == (Object)null) && !val4.IsEditingBulkBox() && !IsTouched(num2, num3)) { ApplyContent(num2, num3, cards2, hasFlag: true, canWorkerTake); } break; } case 13: { List cards = ReadCards(br); InteractableBulkDonationBox val3 = Get(num2, num3); if (!((Object)(object)val3 == (Object)null) && !val3.IsEditingBulkBox() && !IsTouched(num2, num3)) { ApplyContent(num2, num3, cards, 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((int)Msg.ReadItemType(br)); } bool processing = br.ReadBoolean(); float timer = br.ReadSingle(); int openedCount = br.ReadInt32(); List output = ReadCards(br); InteractableAutoPackOpener val5 = Get(num2, num3); if (!((Object)(object)val5 == (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; if (!IsTouched(num2, num3)) { ApplyPackMirrorToMachine(val5, value); } } break; } case 12: { int num5 = br.ReadInt32(); InteractableEmptyBoxStorage val2 = Get(num2, num3); if (!((Object)(object)val2 == (Object)null)) { 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); }); Touch(11, idx); 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected I4, but got Unknown if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } if (CoopCore.ClientReloading) { try { ItemSpawnManager.DisableItem(item); } catch { } return false; } 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); Msg.WriteItemType(bw, (EItemType)itemType); }); instance.Touch(11, idx); } 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; } if (CoopCore.ClientReloading) { try { ItemSpawnManager.DisableItem(item); } catch { } return false; } 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_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); Msg.WriteExpansion(bw, 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_0026: 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) 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 = Msg.ReadExpansion(br); 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 FurnBoxSync { public struct Entry { public int WireType; public int NameHash; public byte Kind; public int ObjIndex; public Vector3 Pos; public float Yaw; public bool Carried; } private const int MaxBoxes = 32; private const float Period = 1.5f; private const byte GenericKind = 15; private const byte Unresolved = byte.MaxValue; public static FurnBoxSync Instance; public static Func IsLocallyCarried = (InteractablePackagingBox_Shelf _) => false; public Action> SendOp; public Action> BroadcastState; public static bool ApplyingRemote; private const byte OpReport = 0; private const byte OpPlace = 1; private const byte OpRemoved = 2; private static readonly FieldInfo FiBoxedObject = AccessTools.Field(typeof(InteractablePackagingBox_Shelf), "m_BoxedObject"); private static readonly FieldInfo FiMovingValid = AccessTools.Field(typeof(InteractableObject), "m_IsMovingObjectValidState"); private static readonly FieldInfo FiGenericList = AccessTools.Field(typeof(ShelfManager), "m_InteractableObjectList"); private static readonly FieldInfo FiCounterScreen = AccessTools.Field(typeof(InteractableCashierCounter), "m_UICashCounterScreen"); private static readonly FieldInfo FiCreditScreen = AccessTools.Field(typeof(InteractableCashierCounter), "m_UICreditCardScreen"); private readonly Dictionary _lastApplied = new Dictionary(); 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 readonly Dictionary _recentlyUnpacked = new Dictionary(); private double _suppressBoxUp; private readonly Dictionary _kindCache = new Dictionary(); private Dictionary _nameToType; private float _timer; private int _lastHostHash; private float _hostHeal; private RestockManager _rm; private ShelfManager _sm; public FurnBoxSync() { Instance = this; } public void Reset() { _lastApplied.Clear(); _carriedLastTick.Clear(); _remoteCarried.Clear(); _recentlyReleased.Clear(); _locallyTouched.Clear(); _hostCarriedLastTick.Clear(); _hostRecentlyReleased.Clear(); _recentlyUnpacked.Clear(); _kindCache.Clear(); _suppressBoxUp = 0.0; _timer = -9.1f; _lastHostHash = 0; _hostHeal = 0f; _rm = null; _sm = null; } public void ForceResend() { _lastHostHash = 0; _hostHeal = 999f; } public void HostReleaseRemoteCarried() { //IL_0036: 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) if (_remoteCarried.Count == 0) { return; } int num = 0; foreach (InteractablePackagingBox_Shelf item in _remoteCarried) { if (!((Object)(object)item == (Object)null)) { ApplyToBox(item, ((Component)item).transform.position, ((Component)item).transform.eulerAngles.y, carried: false); num++; } } _remoteCarried.Clear(); if (num > 0) { CoopPlugin.Log.LogInfo((object)$"FurnBoxSync host: released {num} client-carried box(es) after a disconnect"); ForceResend(); } } private RestockManager Rm() { if ((Object)(object)_rm == (Object)null) { _rm = Object.FindObjectOfType(); } return _rm; } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } private static List LiveBoxes() { return RestockManager.GetShelfPackagingBoxList(); } 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(InteractableObject), "PlaceMovedObject", new HarmonyMethod(typeof(FurnBoxSync), "PlacePrefix", (Type[])null)); Try(h, typeof(InteractablePackagingBox_Shelf), "OnDestroyed", new HarmonyMethod(typeof(FurnBoxSync), "DestroyedPrefix", (Type[])null)); } public static bool PlacePrefix(InteractableObject __instance) { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } if ((Object)(object)__instance == (Object)null || !__instance.GetIsBoxedUp()) { return true; } try { if (FiMovingValid != null && !(bool)FiMovingValid.GetValue(__instance)) { return true; } InteractablePackagingBox_Shelf packagingBoxShelf = __instance.GetPackagingBoxShelf(); if ((Object)(object)packagingBoxShelf == (Object)null) { return true; } Instance?.ClientPlace(__instance, packagingBoxShelf); return false; } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync place: " + ex.Message)); return true; } } public static bool DestroyedPrefix(InteractablePackagingBox_Shelf __instance) { if (!ApplyingRemote && !CoopCore.ClientReloading) { 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_0251: Unknown result type (might be due to invalid IL or missing references) //IL_026c: 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_014c: Expected I4, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: 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) if (!inGame || (Object)(object)Rm() == (Object)null) { return; } bool flag = false; try { List list = LiveBoxes(); for (int i = 0; i < list.Count; i++) { InteractablePackagingBox_Shelf val = list[i]; if ((Object)(object)val == (Object)null) { continue; } if (IsLocallyCarried(val)) { if (_hostCarriedLastTick.Add(val)) { flag = true; } } else if (_hostCarriedLastTick.Remove(val)) { flag = true; _hostRecentlyReleased[val] = 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, 32)); for (int j = 0; j < list2.Count; j++) { if (list3.Count >= 32) { break; } InteractablePackagingBox_Shelf val2 = list2[j]; if (!((Object)(object)val2 == (Object)null)) { InteractableObject val3 = BoxedObject(val2); if (!((Object)(object)val3 == (Object)null) && TryFindObjKey(val3, out var kind, out var idx)) { list3.Add(new Entry { WireType = (int)val3.m_ObjectType, NameHash = Fnv(((object)Unsafe.As(ref val3.m_ObjectType)/*cast due to .constrained prefix*/).ToString()), Kind = kind, ObjIndex = idx, Pos = ((Component)val2).transform.position, Yaw = ((Component)val2).transform.eulerAngles.y, Carried = (IsLocallyCarried(val2) || _remoteCarried.Contains(val2)) }); } } } int num = 17; for (int k = 0; k < list3.Count; k++) { Entry entry = list3[k]; num = num * 31 + entry.WireType; num = num * 31 + ((entry.Kind << 16) | (entry.ObjIndex & 0xFFFF)); 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 = list3; BroadcastState?.Invoke(delegate(BinaryWriter bw) { WriteEntries(bw, snap); }); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync host: " + ex.Message)); } } public void HostApplyOp(BinaryReader br, int connId) { if (CoopCore.Role == CoopRole.Host) { byte b = br.ReadByte(); switch (b) { case 0: HostApplyReport(br); break; case 1: HostApplyPlace(br); break; case 2: HostApplyRemoved(br, connId); break; default: CoopPlugin.Log.LogWarning((object)$"FurnBoxSync: unknown op {b}"); break; } } } private void HostApplyReport(BinaryReader br) { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min((int)br.ReadByte(), 32); List list = LiveBoxes(); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; Vector3 pos = default(Vector3); for (int i = 0; i < num; i++) { int num2 = br.ReadByte(); int wireType = br.ReadInt32(); int nameHash = br.ReadInt32(); bool flag = br.ReadBoolean(); ((Vector3)(ref pos))..ctor(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); float yaw = br.ReadSingle(); if (num2 >= list.Count || (Object)(object)list[num2] == (Object)null) { continue; } InteractablePackagingBox_Shelf val = list[num2]; if (BoxedTypeMatches(val, wireType, nameHash) && !IsLocallyCarried(val) && (!_hostRecentlyReleased.TryGetValue(val, out var value) || !(realtimeSinceStartupAsDouble - value < 6.0))) { if (flag) { _remoteCarried.Add(val); } else { _remoteCarried.Remove(val); } ApplyToBox(val, pos, yaw, flag); } } SweepOld(_hostRecentlyReleased, realtimeSinceStartupAsDouble); _timer = 1.5f; _lastHostHash = 0; } private void HostApplyPlace(BinaryReader br) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) byte idx = br.ReadByte(); int wireType = br.ReadInt32(); int nameHash = br.ReadInt32(); Vector3 pos = default(Vector3); ((Vector3)(ref pos))..ctor(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); float yaw = br.ReadSingle(); InteractablePackagingBox_Shelf val = FindBox(idx, wireType, nameHash); if ((Object)(object)val == (Object)null) { CoopPlugin.Log.LogWarning((object)"FurnBoxSync: place for unknown/mismatched box - ignored"); } else { if (IsLocallyCarried(val)) { return; } InteractableObject val2 = BoxedObject(val); if ((Object)(object)val2 == (Object)null) { return; } ApplyingRemote = true; try { PlaceFromBox(val2, pos, yaw); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync place apply: " + ex.Message)); } finally { ApplyingRemote = false; } ForgetBox(val); _timer = 1.5f; _lastHostHash = 0; } } private void HostApplyRemoved(BinaryReader br, int connId) { int idx = br.ReadByte(); int wireType = br.ReadInt32(); int nameHash = br.ReadInt32(); if (BoxSync.RemovalFlooded(connId, "furniture-box")) { return; } InteractablePackagingBox_Shelf val = FindBox(idx, wireType, nameHash); if ((Object)(object)val == (Object)null || IsLocallyCarried(val)) { return; } ApplyingRemote = true; try { ((InteractableObject)val).OnDestroyed(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync removal: " + ex.Message)); } finally { ApplyingRemote = false; } ForgetBox(val); _timer = 1.5f; _lastHostHash = 0; } private static InteractablePackagingBox_Shelf FindBox(int idx, int wireType, int nameHash) { List list = LiveBoxes(); if (idx >= 0 && idx < list.Count && (Object)(object)list[idx] != (Object)null && BoxedTypeMatches(list[idx], wireType, nameHash)) { return list[idx]; } InteractablePackagingBox_Shelf val = null; for (int i = 0; i < list.Count; i++) { if (!((Object)(object)list[i] == (Object)null) && BoxedTypeMatches(list[i], wireType, nameHash)) { if ((Object)(object)val != (Object)null) { return null; } val = list[i]; } } return val; } private void ForgetBox(InteractablePackagingBox_Shelf box) { _remoteCarried.Remove(box); _hostCarriedLastTick.Remove(box); _hostRecentlyReleased.Remove(box); } public void ClientApplyState(BinaryReader br) { List hostList = ReadEntries(br); ApplyingRemote = true; try { ClientApplyInner(hostList); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync apply: " + ex.Message)); } finally { ApplyingRemote = false; } } private void ClientApplyInner(List hostList) { //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_04a9: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Rm() == (Object)null || (Object)(object)Sm() == (Object)null) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; bool flag = true; List list = new List(hostList.Count); for (int i = 0; i < hostList.Count; i++) { InteractableObject val = ResolveEntryObject(hostList[i]); list.Add(val); if ((Object)(object)val == (Object)null && hostList[i].Kind != 15) { flag = false; } } if (flag) { List list2 = LiveBoxes(); for (int num = list2.Count - 1; num >= 0; num--) { InteractablePackagingBox_Shelf val2 = list2[num]; if (!((Object)(object)val2 == (Object)null)) { InteractableObject val3 = BoxedObject(val2); if (!((Object)(object)val3 != (Object)null) || !val3.m_IsGenericObject) { bool flag2 = false; for (int j = 0; j < list.Count; j++) { if (list[j] == val3 && (Object)(object)val3 != (Object)null) { flag2 = true; break; } } if (!flag2) { Entry value; bool flag3 = _lastApplied.TryGetValue(val2, out value) && value.Carried; try { if ((Object)(object)val3 == (Object)null || flag3) { try { CoopCore.ForceExitHoldBox((Object)(object)val2); } catch { } ((InteractableObject)val2).OnDestroyed(); } else { FiMovingValid?.SetValue(val3, true); ((Component)val3).gameObject.SetActive(true); val3.PlaceMovedObject(); ObjMoveSync.SyncTagGroup(((Component)val3).transform); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync retire: " + ex.Message)); } _lastApplied.Remove(val2); _carriedLastTick.Remove(val2); _recentlyReleased.Remove(val2); _locallyTouched.Remove(val2); } } } } } for (int k = 0; k < hostList.Count; k++) { Entry value2 = hostList[k]; InteractableObject val4 = list[k]; if ((Object)(object)val4 == (Object)null) { if (value2.Kind != 15 || !(realtimeSinceStartupAsDouble - _suppressBoxUp >= 6.0)) { continue; } bool flag4 = false; List list3 = LiveBoxes(); for (int l = 0; l < list3.Count; l++) { InteractablePackagingBox_Shelf val5 = list3[l]; if (!((Object)(object)val5 == (Object)null) && BoxedTypeMatches(val5, value2.WireType, value2.NameHash)) { Vector3 position = ((Component)val5).transform.position; float num2 = position.x - value2.Pos.x; float num3 = position.z - value2.Pos.z; if (num2 * num2 + num3 * num3 <= 1f) { flag4 = true; break; } } } if (!flag4) { try { ShelfManager.SpawnInteractableObjectInPackageBox((EObjectType)ResolveObjType(value2.WireType, value2.NameHash), value2.Pos, Quaternion.Euler(0f, value2.Yaw, 0f)); } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync spawn: " + ex2.Message)); } } continue; } InteractablePackagingBox_Shelf val6 = (val4.GetIsBoxedUp() ? val4.GetPackagingBoxShelf() : null); if ((Object)(object)val6 == (Object)null) { if (val4.GetIsMovingObject() || (_recentlyUnpacked.TryGetValue(val4, out var value3) && realtimeSinceStartupAsDouble - value3 < 6.0) || realtimeSinceStartupAsDouble - _suppressBoxUp < 6.0) { continue; } try { val4.BoxUpObject(false); val6 = val4.GetPackagingBoxShelf(); } catch (Exception ex3) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync boxup: " + ex3.Message)); continue; } if ((Object)(object)val6 == (Object)null) { continue; } } double value4; double value5; if (IsLocallyCarried(val6)) { _lastApplied[val6] = value2; } else if (value2.Carried && _recentlyReleased.TryGetValue(val6, out value4) && realtimeSinceStartupAsDouble - value4 < 6.0) { _lastApplied[val6] = value2; } else if (_locallyTouched.TryGetValue(val6, out value5) && realtimeSinceStartupAsDouble - value5 < 6.0) { if (!value2.Carried && !((Component)val6).gameObject.activeSelf) { ShowBox(val6); } else if (value2.Carried && ((Component)val6).gameObject.activeSelf) { HideBox(val6); } _lastApplied[val6] = value2; } else { ApplyToBox(val6, value2.Pos, value2.Yaw, value2.Carried); _lastApplied[val6] = value2; } } SweepOld(_recentlyUnpacked, realtimeSinceStartupAsDouble); } public void ClientTick(float dt, bool inGame) { //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: 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_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) if (!inGame || (Object)(object)Rm() == (Object)null || _lastApplied.Count == 0) { return; } bool flag = false; try { List list = LiveBoxes(); for (int i = 0; i < list.Count; i++) { InteractablePackagingBox_Shelf val = list[i]; if ((Object)(object)val == (Object)null) { continue; } if (IsLocallyCarried(val)) { if (_carriedLastTick.Add(val)) { flag = true; } } else if (_carriedLastTick.Remove(val)) { flag = true; _recentlyReleased[val] = 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; double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; List idxList = new List(list2.Count); List entryList = new List(list2.Count); for (int j = 0; j < list2.Count; j++) { if (entryList.Count >= 32) { break; } InteractablePackagingBox_Shelf val2 = list2[j]; if ((Object)(object)val2 == (Object)null || !_lastApplied.TryGetValue(val2, out var value)) { continue; } Entry item = value; double value2; if (IsLocallyCarried(val2)) { item.Carried = true; } else if (!value.Carried || (_recentlyReleased.TryGetValue(val2, out value2) && realtimeSinceStartupAsDouble - value2 < 6.0)) { item.Carried = false; item.Pos = ((Component)val2).transform.position; item.Yaw = ((Component)val2).transform.eulerAngles.y; Vector3 val3 = item.Pos - value.Pos; if (((Vector3)(ref val3)).sqrMagnitude > 0.01f || Mathf.Abs(Mathf.DeltaAngle(item.Yaw, value.Yaw)) > 3f || item.Carried != value.Carried) { flag2 = true; _locallyTouched[val2] = realtimeSinceStartupAsDouble; } } idxList.Add(j); entryList.Add(item); } if (!flag2 || SendOp == null) { return; } SendOp(delegate(BinaryWriter bw) { //IL_0071: 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_0093: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)0); bw.Write((byte)entryList.Count); for (int k = 0; k < entryList.Count; k++) { Entry entry = entryList[k]; bw.Write((byte)Mathf.Clamp(idxList[k], 0, 255)); bw.Write(entry.WireType); bw.Write(entry.NameHash); bw.Write(entry.Carried); bw.Write(entry.Pos.x); bw.Write(entry.Pos.y); bw.Write(entry.Pos.z); bw.Write(entry.Yaw); } }); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync client: " + ex.Message)); } } private void ClientPlace(InteractableObject obj, InteractablePackagingBox_Shelf box) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_0092: Unknown result type (might be due to invalid IL or missing references) if (SendOp == null) { CoopPlugin.Log.LogWarning((object)"FurnBoxSync: no host link, placing locally only"); } else { int idx = LiveBoxes().IndexOf(box); Vector3 pos = ((Component)obj).transform.position; pos.y = 0f; float yaw = ((Component)obj).transform.eulerAngles.y; Entry value; int wireType = (_lastApplied.TryGetValue(box, out value) ? value.WireType : ((int)obj.m_ObjectType)); SendOp(delegate(BinaryWriter bw) { bw.Write((byte)1); bw.Write((byte)Mathf.Clamp(idx, 0, 255)); bw.Write(wireType); bw.Write(Fnv(((object)Unsafe.As(ref obj.m_ObjectType)/*cast due to .constrained prefix*/).ToString())); bw.Write(pos.x); bw.Write(pos.y); bw.Write(pos.z); bw.Write(yaw); }); } _recentlyUnpacked[obj] = Time.realtimeSinceStartupAsDouble; _lastApplied.Remove(box); _carriedLastTick.Remove(box); _recentlyReleased.Remove(box); _locallyTouched.Remove(box); ApplyingRemote = true; try { obj.PlaceMovedObject(); } finally { ApplyingRemote = false; } } private void OnLocalDestroyed(InteractablePackagingBox_Shelf box) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) if (!InGameLevel()) { return; } if (CoopCore.Role == CoopRole.Host) { ForgetBox(box); } else { if (CoopCore.Role != CoopRole.Client) { return; } InteractableObject val = BoxedObject(box); int idx = LiveBoxes().IndexOf(box); _lastApplied.TryGetValue(box, out var value); _lastApplied.Remove(box); _carriedLastTick.Remove(box); _recentlyReleased.Remove(box); _locallyTouched.Remove(box); if (!((Object)(object)val == (Object)null) && idx >= 0) { _suppressBoxUp = Time.realtimeSinceStartupAsDouble; int wireType = ((value.WireType != 0) ? value.WireType : ((int)val.m_ObjectType)); int nameHash = Fnv(((object)Unsafe.As(ref val.m_ObjectType)/*cast due to .constrained prefix*/).ToString()); SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)2); bw.Write((byte)Mathf.Clamp(idx, 0, 255)); bw.Write(wireType); bw.Write(nameHash); }); } } } private static void ApplyToBox(InteractablePackagingBox_Shelf box, Vector3 pos, float yaw, bool carried) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_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_0037: 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_0094: Unknown result type (might be due to invalid IL or missing references) try { if (carried) { HideBox(box); return; } ShowBox(box); Transform transform = ((Component)box).transform; Vector3 val = transform.position - pos; if (!(((Vector3)(ref val)).sqrMagnitude > 0.01f) && !(Mathf.Abs(Mathf.DeltaAngle(transform.eulerAngles.y, yaw)) > 3f)) { return; } transform.SetPositionAndRotation(pos, Quaternion.Euler(0f, yaw, 0f)); ObjMoveSync.SyncTagGroup(transform); try { Rigidbody rigidbody = ((InteractablePackagingBox)box).m_Rigidbody; if ((Object)(object)rigidbody != (Object)null && !rigidbody.isKinematic) { rigidbody.velocity = Vector3.zero; rigidbody.angularVelocity = Vector3.zero; rigidbody.WakeUp(); } } catch { } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("FurnBoxSync apply: " + ex.Message)); } } private static void HideBox(InteractablePackagingBox_Shelf box) { if (((Component)box).gameObject.activeSelf) { try { box.m_ItemCompartment.SetPriceTagVisibility(false); } catch { } ((Component)box).gameObject.SetActive(false); } } private static void ShowBox(InteractablePackagingBox_Shelf box) { if (((Component)box).gameObject.activeSelf) { return; } ((Component)box).gameObject.SetActive(true); try { box.m_ItemCompartment.SetPriceTagVisibility(true); } catch { } } private static void PlaceFromBox(InteractableObject obj, Vector3 pos, float yaw) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) ((Component)obj).gameObject.SetActive(true); ((Component)obj).transform.SetPositionAndRotation(pos, Quaternion.Euler(0f, yaw, 0f)); FiMovingValid?.SetValue(obj, true); obj.PlaceMovedObject(); if (obj is InteractableCashierCounter) { try { object? obj2 = FiCounterScreen?.GetValue(obj); object? obj3 = ((obj2 is Component) ? obj2 : null); if (obj3 != null) { ((Component)obj3).gameObject.SetActive(true); } object? obj4 = FiCreditScreen?.GetValue(obj); object? obj5 = ((obj4 is Component) ? obj4 : null); if (obj5 != null) { ((Component)obj5).gameObject.SetActive(true); } } catch { } } ObjMoveSync.SyncTagGroup(((Component)obj).transform); } private static InteractableObject BoxedObject(InteractablePackagingBox_Shelf box) { try { object? obj = FiBoxedObject?.GetValue(box); return (InteractableObject)((obj is InteractableObject) ? obj : null); } catch { return null; } } private static bool BoxedTypeMatches(InteractablePackagingBox_Shelf box, int wireType, int nameHash) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 InteractableObject val = BoxedObject(box); if ((Object)(object)val == (Object)null) { return false; } if ((int)val.m_ObjectType == wireType) { return true; } return Fnv(((object)Unsafe.As(ref val.m_ObjectType)/*cast due to .constrained prefix*/).ToString()) == nameHash; } private bool TryFindObjKey(InteractableObject obj, out byte kind, out int idx) { kind = byte.MaxValue; idx = -1; ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return false; } if (_kindCache.TryGetValue(obj, out var value)) { int num = KindList(val, value)?.IndexOf(obj) ?? (-1); if (num >= 0) { kind = value; idx = num; return true; } _kindCache.Remove(obj); } for (byte b = 0; b <= 15; b++) { int num2 = KindList(val, b)?.IndexOf(obj) ?? (-1); if (num2 >= 0) { _kindCache[obj] = b; kind = b; idx = num2; return true; } } return false; } private InteractableObject ResolveEntryObject(Entry e) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 if (e.Kind == byte.MaxValue) { return null; } ShelfManager val = Sm(); if ((Object)(object)val == (Object)null) { return null; } IList list = KindList(val, e.Kind); if (list == null || e.ObjIndex < 0 || e.ObjIndex >= list.Count) { return null; } object? obj = list[e.ObjIndex]; InteractableObject val2 = (InteractableObject)((obj is InteractableObject) ? obj : null); if ((Object)(object)val2 == (Object)null) { return null; } int num = ResolveObjType(e.WireType, e.NameHash); if ((int)val2.m_ObjectType != num) { return null; } return val2; } private static IList KindList(ShelfManager sm, byte kind) { if (kind < 15) { return PopulationSync.GetList(sm, kind); } if (kind == 15) { return FiGenericList?.GetValue(sm) as IList; } return null; } private int ResolveObjType(int wireType, int nameHash) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (Fnv(((object)(EObjectType)wireType/*cast due to .constrained prefix*/).ToString()) == nameHash) { return wireType; } if (_nameToType == null) { _nameToType = new Dictionary(); foreach (object value2 in Enum.GetValues(typeof(EObjectType))) { _nameToType[Fnv(value2.ToString())] = (int)value2; } } if (!_nameToType.TryGetValue(nameHash, out var value)) { return wireType; } return value; } 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; } private static void SweepOld(Dictionary dict, double now) { if (dict.Count == 0) { return; } List list = null; foreach (KeyValuePair item in dict) { if (now - item.Value > 12.0) { (list ?? (list = new List())).Add(item.Key); } } if (list == null) { return; } foreach (TKey item2 in list) { dict.Remove(item2); } } private static void WriteEntries(BinaryWriter bw, List entries) { //IL_0061: 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_0083: Unknown result type (might be due to invalid IL or missing references) bw.Write((byte)Mathf.Min(entries.Count, 32)); for (int i = 0; i < entries.Count && i < 32; i++) { Entry entry = entries[i]; bw.Write(entry.WireType); bw.Write(entry.NameHash); bw.Write(entry.Kind); bw.Write((ushort)Mathf.Clamp(entry.ObjIndex, 0, 65535)); bw.Write(entry.Pos.x); bw.Write(entry.Pos.y); bw.Write(entry.Pos.z); bw.Write(entry.Yaw); bw.Write(entry.Carried ? ((byte)1) : ((byte)0)); } } private static List ReadEntries(BinaryReader br) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min((int)br.ReadByte(), 32); List list = new List(num); for (int i = 0; i < num; i++) { Entry item = new Entry { WireType = br.ReadInt32(), NameHash = br.ReadInt32(), Kind = br.ReadByte(), ObjIndex = br.ReadUInt16(), Pos = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()), Yaw = br.ReadSingle() }; item.Carried = (br.ReadByte() & 1) != 0; list.Add(item); } return list; } } 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 static readonly FieldInfo FiServiceTotalCost = AccessTools.Field(typeof(GradedCardSubmitSelectScreen), "m_ServiceTotalCost"); private float _timer; private int _lastHash; private float _heal; private GradeCardWebsiteUIScreen _website; private static InventoryBase _inv; private static InventoryBase Inv() { if ((Object)(object)_inv == (Object)null) { _inv = Object.FindObjectOfType(); } return _inv; } public GradingSync() { Instance = this; } public void Reset() { _timer = -6.8f; _lastHash = 0; _heal = 0f; _website = null; _inv = 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() { if (CoopCore.Role != CoopRole.Client) { return !CoopCore.GuestBorrowedWorld; } return false; } 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_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Expected O, but got Unknown //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) 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; InventoryBase val2 = Inv(); if ((Object)(object)val2 == (Object)null) { return; } GradeCardServiceData gradeCardServiceData = val2.m_MonsterData_SO.GetGradeCardServiceData(serviceLevel); float total = 10f + gradeCardServiceData.m_CostPerCard * (float)picked.Count; if (GradingInterop.Present && FiServiceTotalCost != null && (Object)(object)screen != (Object)null) { try { if (FiServiceTotalCost.GetValue(screen) is float num3 && !float.IsNaN(num3) && !float.IsInfinity(num3) && num3 > 0f) { total = num3; } } catch { } } 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, 255)); 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 val3 = new GradeCardSubmitSet { m_ServiceLevel = serviceLevel, m_CardDataList = new List(8) }; for (int num4 = 0; num4 < 8; num4++) { val3.m_CardDataList.Add(new CardData()); } CPlayerData.m_CurrentGradeCardSubmitSet = val3; ((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)); } } private static void ReturnRejectedCards(List cards, int senderConn) { for (int i = 0; i < cards.Count; i++) { if (cards[i] == null) { continue; } if (!CoopCore.CardSetInstalledHere(cards[i])) { CoopCore.WarnRefusedCard(cards[i], "grade-return"); continue; } if (cards[i].cardGrade > 10 && GradingInterop.Present) { GradingInterop.Remember(cards[i]); } CPlayerData.AddCard(cards[i], 1); } } public void HostApplyOp(BinaryReader br, int senderConn) { //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Expected O, but got Unknown //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Expected O, but got Unknown int num = Mathf.Clamp((int)br.ReadByte(), 0, 255); 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 { InventoryBase val = Inv(); if ((Object)(object)val == (Object)null) { CoopPlugin.Log.LogWarning((object)"GradingSync: no InventoryBase (world loading?) - submission dropped"); return; } float num4; if (GradingInterop.Present) { if (float.IsNaN(num3) || float.IsInfinity(num3) || num3 < 0f) { CoopPlugin.Log.LogWarning((object)$"GradingSync: rejecting non-finite/negative clientFee {num3} - submission dropped"); ReturnRejectedCards(list, senderConn); return; } num4 = num3; try { int num5 = Mathf.Clamp(num, 0, val.m_MonsterData_SO.m_GradeCardServiceDataList.Count - 1); GradeCardServiceData gradeCardServiceData = val.m_MonsterData_SO.GetGradeCardServiceData(num5); float num6 = 10f + gradeCardServiceData.m_CostPerCard * (float)list.Count; if (Mathf.Abs(num6 - num3) > 0.01f) { CoopPlugin.Log.LogInfo((object)$"GradingSync: GO fee forwarded - charging guest's on-screen bill {num3} (vanilla-flat recompute would have been {num6})"); } } catch { } } else { GradeCardServiceData gradeCardServiceData2 = val.m_MonsterData_SO.GetGradeCardServiceData(num); num4 = 10f + gradeCardServiceData2.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"); ReturnRejectedCards(list, senderConn); return; } 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 val2 = new GradeCardSubmitSet { m_ServiceLevel = num, m_DayPassed = 0, m_MinutePassed = 0f, m_CardDataList = new List(8) }; val2.m_CardDataList.AddRange(list); while (val2.m_CardDataList.Count < 8) { val2.m_CardDataList.Add(new CardData()); } CPlayerData.m_GradeCardInProgressList.Add(val2); ForceResend(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("GradingSync op: " + ex.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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: 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) }; try { InventoryBase obj = Inv(); object obj2; if (obj == null) { obj2 = null; } else { MonsterData_ScriptableObject monsterData_SO = obj.m_MonsterData_SO; obj2 = ((monsterData_SO != null) ? monsterData_SO.GetGradeCardServiceData(val.m_ServiceLevel) : null); } GradeCardServiceData val2 = (GradeCardServiceData)obj2; if (val2 != null) { val.m_DayPassed = Mathf.Clamp(val.m_DayPassed, 0, Mathf.Max(0, val2.m_ServiceDays - 1)); } } catch { } 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; private const int VanillaItemTypes = 129; private static bool s_eplProbed; private static object s_eplSaveMgr; private static MethodInfo s_eplTryGet; private static PropertyInfo s_eplAssetsProp; private static PropertyInfo s_eplItemLibProp; private static PropertyInfo s_eplItemDataProp; private static PropertyInfo s_eplGenMarket; private static PropertyInfo s_eplGenCost; private static PropertyInfo s_eplPctChange; private static PropertyInfo s_eplAvgCost; 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); num = HashEplMarket(num); _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) { List modded = EplModdedItemTypes(); bw.Write(s_rollGen); WritePercents(bw, CPlayerData.m_ItemPricePercentChangeList, modded); 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, modded, s_eplGenMarket); WriteSparseFloats(bw, CPlayerData.m_GeneratedCostPriceList, modded, s_eplGenCost); WriteSparseFloats(bw, CPlayerData.m_AverageItemCostList, modded, s_eplAvgCost); } 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, ModdedGenMarket); ReadSparseFloatsInto(br, CPlayerData.m_GeneratedCostPriceList, ModdedGenCost); ReadSparseFloatsInto(br, CPlayerData.m_AverageItemCostList, ModdedAvgCost); 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, List modded) { int num = VanillaWalkCount(list); int num2 = 0; for (int i = 0; i < num; i++) { if (list[i] != 0f) { num2++; } } List> list2 = CollectModded(modded, s_eplPctChange); bw.Write(num2 + list2.Count); for (int j = 0; j < num; j++) { if (list[j] != 0f) { Msg.WriteItemType(bw, (EItemType)j); bw.Write((short)Mathf.Clamp(Mathf.RoundToInt(list[j] * 100f), -32768, 32767)); } } for (int k = 0; k < list2.Count; k++) { Msg.WriteItemType(bw, (EItemType)list2[k].Key); bw.Write((short)Mathf.Clamp(Mathf.RoundToInt(list2[k].Value * 100f), -32768, 32767)); } } private static void WriteSparseFloats(BinaryWriter bw, List list, List modded, PropertyInfo eplField) { int num = VanillaWalkCount(list); int num2 = 0; for (int i = 0; i < num; i++) { if (list[i] != 0f) { num2++; } } List> list2 = CollectModded(modded, eplField); bw.Write(num2 + list2.Count); for (int j = 0; j < num; j++) { if (list[j] != 0f) { Msg.WriteItemType(bw, (EItemType)j); bw.Write(list[j]); } } for (int k = 0; k < list2.Count; k++) { Msg.WriteItemType(bw, (EItemType)list2[k].Key); bw.Write(list2[k].Value); } } private static void ReadSparseFloatsInto(BinaryReader br, List list, Action moddedWrite) { int num = br.ReadInt32(); for (int i = 0; i < num; i++) { int wireId = br.ReadInt32(); float num2 = br.ReadSingle(); if (!EnumMap.TryFromWire(EnumKind.ItemType, wireId, out var localId) || list == null || localId < 0 || localId > 500000) { continue; } if (localId >= 129 && EplMarketBridge()) { try { moddedWrite(localId, num2); } catch { } } else { while (list.Count <= localId) { list.Add(0f); } list[localId] = num2; } } } private static void ReadPercentsInto(BinaryReader br, List list) { if (list != null) { for (int i = 0; i < list.Count; i++) { list[i] = 0f; } } if (EplMarketBridge()) { List list2 = EplModdedItemTypes(); for (int j = 0; j < list2.Count; j++) { if (EnumMap.ToWire(EnumKind.ItemType, list2[j]) != -1) { EplSetFloat(list2[j], s_eplPctChange, 0f); } } } int num = br.ReadInt32(); for (int k = 0; k < num; k++) { int wireId = br.ReadInt32(); float value = (float)br.ReadInt16() / 100f; if (!EnumMap.TryFromWire(EnumKind.ItemType, wireId, out var localId) || list == null || localId < 0 || localId > 500000) { continue; } if (localId >= 129 && EplMarketBridge()) { EplSetFloat(localId, s_eplPctChange, value); continue; } while (list.Count <= localId) { list.Add(0f); } list[localId] = 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 bool EplMarketBridge() { if (!s_eplProbed) { s_eplProbed = true; try { Type type = AccessTools.TypeByName("EnhancedPrefabLoader.Core.EplRuntimeData"); Type type2 = AccessTools.TypeByName("EnhancedPrefabLoader.Core.Models.SaveData.ItemSaveData"); s_eplAssetsProp = type?.GetProperty("Assets", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = s_eplAssetsProp?.GetValue(null); s_eplItemLibProp = obj?.GetType().GetProperty("ItemLibrary", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_eplItemDataProp = ((obj == null) ? null : s_eplItemLibProp?.GetValue(obj))?.GetType().GetProperty("ItemData", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj2 = type?.GetProperty("Services", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); s_eplSaveMgr = obj2?.GetType().GetProperty("SaveDataManager", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2); if (s_eplSaveMgr != null && type2 != null) { MethodInfo[] methods = s_eplSaveMgr.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "TryGetSaveData" && methodInfo.GetGenericArguments().Length == 2) { s_eplTryGet = methodInfo.MakeGenericMethod(typeof(EItemType), type2); break; } } } s_eplGenMarket = type2?.GetProperty("GeneratedMarketPrice", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_eplGenCost = type2?.GetProperty("GeneratedCostPrice", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_eplPctChange = type2?.GetProperty("ItemPriceChangePercent", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_eplAvgCost = type2?.GetProperty("AverageItemCost", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { } if (s_eplTryGet == null || s_eplItemDataProp == null || s_eplGenMarket == null || s_eplGenCost == null || s_eplPctChange == null || s_eplAvgCost == null) { s_eplTryGet = null; CoopPlugin.Log.LogInfo((object)"EPL market bridge inactive (EPL absent or its internals changed) - vanilla market only"); } else { CoopPlugin.Log.LogInfo((object)"EPL market bridge active (modded item market data syncs)"); } } return s_eplTryGet != null; } private static int VanillaWalkCount(List list) { int num = list?.Count ?? 0; if (!EplMarketBridge()) { return num; } return Mathf.Min(num, 129); } private static List EplModdedItemTypes() { List list = new List(); if (!EplMarketBridge()) { return list; } try { object value = s_eplAssetsProp.GetValue(null); object obj = ((value == null) ? null : s_eplItemLibProp.GetValue(value)); IDictionary dictionary = ((obj == null) ? null : (s_eplItemDataProp.GetValue(obj) as IDictionary)); if (dictionary != null) { foreach (object key in dictionary.Keys) { int num = Convert.ToInt32(key); if (num >= 200000 && num <= 500000) { list.Add(num); } } } } catch { } return list; } private static object EplSaveData(int itemType) { try { object[] array = new object[2] { (object)(EItemType)itemType, null }; return ((bool)s_eplTryGet.Invoke(s_eplSaveMgr, array)) ? array[1] : null; } catch { return null; } } private static float EplGetFloat(int itemType, PropertyInfo field) { object obj = EplSaveData(itemType); if (obj != null) { return (float)field.GetValue(obj, null); } return 0f; } private static void EplSetFloat(int itemType, PropertyInfo field, float value) { object obj = EplSaveData(itemType); if (obj != null) { field.SetValue(obj, value, null); } } private static List> CollectModded(List modded, PropertyInfo field) { List> list = new List>(modded.Count); if (field != null) { for (int i = 0; i < modded.Count; i++) { float num = EplGetFloat(modded[i], field); if (num != 0f) { list.Add(new KeyValuePair(modded[i], num)); } } } return list; } private static void ModdedGenMarket(int itemType, float v) { EplSetFloat(itemType, s_eplGenMarket, v); } private static void ModdedGenCost(int itemType, float v) { EplSetFloat(itemType, s_eplGenCost, v); } private static void ModdedAvgCost(int itemType, float v) { CPlayerData.SetAverageItemCost((EItemType)itemType, v); } private static int HashEplMarket(int h) { List list = EplModdedItemTypes(); for (int i = 0; i < list.Count; i++) { object obj = EplSaveData(list[i]); if (obj != null) { h = h * 31 + (int)((float)s_eplPctChange.GetValue(obj, null) * 100f); h = h * 31 + (int)((float)s_eplGenMarket.GetValue(obj, null) * 100f); h = h * 31 + (int)((float)s_eplGenCost.GetValue(obj, null) * 100f); h = h * 31 + (int)((float)s_eplAvgCost.GetValue(obj, null) * 100f); } } return h; } 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, Exclaim = 0x40, Female = 0x80 } 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 GameObject Exclaim; public bool Female; 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 { } try { if ((Object)(object)val.m_ExclaimationMesh != (Object)null && val.m_ExclaimationMesh.activeSelf) { npcFlags |= NpcFlags.Exclaim; } } 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) { continue; } CharacterCustomization characterCustom2 = val2.m_CharacterCustom; if (!((Object)(object)characterCustom2 == (Object)null) && !string.IsNullOrEmpty(characterCustom2.CharacterName)) { NpcFlags npcFlags2 = CollectFlags(val2.m_Anim); if (val2.m_IsFemale) { npcFlags2 |= NpcFlags.Female; } WriteEntry(list, unscaledTime, 1, (ushort)j, characterCustom2.CharacterName, ((Component)val2).transform, 0f, npcFlags2, 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_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0204: 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) 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 npcFlags = (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; } bool femaleHint = (npcFlags & NpcFlags.Female) != 0; if (flag && value.CharName != text) { ReDress(value, text, pos, femaleHint); } else if ((Object)(object)value.Go == (Object)null && value.CharName.Length > 0) { Spawn(value, value.CharName, pos, femaleHint); } 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 = npcFlags, Time = num4 }; if (value.BufCount < 4) { value.BufCount++; } } value.Flags = npcFlags; value.LastSeen = _now; } } private void ReDress(Puppet p, string charName, Vector3 pos, bool femaleHint) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) bool flag = femaleHint || (charName?.StartsWith("Female") ?? false); bool flag2 = (Object)(object)p.Go != (Object)null && p.Female != flag; if ((Object)(object)p.Go == (Object)null || (Object)(object)p.Custom == (Object)null || flag2) { if ((Object)(object)p.Go != (Object)null) { Object.Destroy((Object)(object)p.Go); } p.Go = null; Spawn(p, charName, pos, femaleHint); 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) if (!inGame || dt <= 0f) { return; } _now += dt; 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 > 6f) { 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); Toggle(value.Exclaim, (value.Flags & NpcFlags.Exclaim) != 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, bool femaleHint) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: 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 = ((p.Female = femaleHint || 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; p.Exclaim = component.m_ExclaimationMesh; 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 int Type; public bool Unresolved; 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 const int NoType = int.MinValue; private readonly Dictionary _sent = new Dictionary(); private readonly Dictionary _candidate = new Dictionary(); private ShelfManager _sm; private float _timer; private float _lastRejectLog = -999f; public Action> OnLocalChanges; private static readonly MethodInfo _miOpenerSetUI = AccessTools.Method(typeof(InteractableAutoPackOpener), "SetUITransform", (Type[])null, (Type[])null); private static readonly Dictionary _tagGrpFields = new Dictionary(); private static bool IsClientRole => CoopCore.Role == CoopRole.Client; private static int TypeIdOf(Component obj, int kind) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected I4, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected I4, but got Unknown InteractableObject val = (InteractableObject)(object)((obj is InteractableObject) ? obj : null); if ((Object)(object)val == (Object)null) { return int.MinValue; } if (kind == 5) { return (int)val.m_DecoObjectType; } return (int)val.m_ObjectType; } 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_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_0095: 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_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: 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_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0136: 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_013f: 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) //IL_00de: 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: 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_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: 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); InteractableObject val2 = (InteractableObject)(object)((val is InteractableObject) ? val : null); if (val2 != null && val2.GetIsMovingObject()) { _candidate.Remove(key); continue; } Vector3 position = val.transform.position; Quaternion rotation = val.transform.rotation; Pose value; bool flag = _sent.TryGetValue(key, out value); Pose value2; if (flag && value.Same(position, rotation)) { _candidate.Remove(key); } else if (!flag && IsClientRole && !_candidate.ContainsKey(key)) { _sent[key] = new Pose { P = position, R = rotation, Valid = true }; } 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, Type = TypeIdOf(val, kind), Pos = position, Rot = rotation }); } else { _candidate[key] = new Pose { P = position, R = rotation, Valid = true }; } } } public void ApplyRemote(List entries, bool dropIfHostMoving = false) { //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: 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_0171: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: 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_013d: 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 { if (entry.Unresolved) { continue; } Component val2 = Resolve(val, entry.Key); if ((Object)(object)val2 == (Object)null) { continue; } int num = TypeIdOf(val2, entry.Key >> 24); if (num != int.MinValue && entry.Type != int.MinValue && num != entry.Type) { if (Time.realtimeSinceStartup - _lastRejectLog > 5f) { _lastRejectLog = Time.realtimeSinceStartup; CoopPlugin.Log.LogInfo((object)($"ObjMoveSync: skipped stale move {entry.Key:X} " + $"(wire type {entry.Type} != resolved {num})")); } continue; } Transform transform = val2.transform; InteractableObject val3 = (InteractableObject)(((object)((val2 is InteractableObject) ? val2 : null)) ?? ((object)((Component)transform).GetComponent())); if (dropIfHostMoving && (Object)(object)val3 != (Object)null && val3.GetIsMovingObject()) { _sent[entry.Key] = new Pose { P = transform.position, R = transform.rotation, Valid = true }; _candidate.Remove(entry.Key); continue; } transform.SetPositionAndRotation(entry.Pos, entry.Rot); SyncTagGroup(transform); if (val3 is InteractableAutoPackOpener) { try { _miOpenerSetUI?.Invoke(val3, null); } catch { } } _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 Component 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]; return (Component)((obj is Component) ? obj : null); } private static EnumKind KindOf(int kind) { if (kind != 5) { return EnumKind.ObjectType; } return EnumKind.DecoObject; } public static void WriteEntries(BinaryWriter bw, List entries) { //IL_004e: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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(EnumMap.ToWire(KindOf(entry.Key >> 24), entry.Type)); 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_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) int num = br.ReadByte(); List list = new List(num); for (int i = 0; i < num; i++) { int num2 = br.ReadInt32(); int localId; bool unresolved = !EnumMap.TryFromWire(KindOf(num2 >> 24), br.ReadInt32(), out localId); list.Add(new Entry { Key = num2, Type = localId, Unresolved = unresolved, 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) { Msg.WriteItemType(bw, (EItemType)seatState.PlayMat); Msg.WriteItemType(bw, (EItemType)seatState.DeckBox); Msg.WriteItemType(bw, (EItemType)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) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected I4, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected I4, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected I4, but got Unknown 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 = (int)Msg.ReadItemType(br); want.DeckBox = (int)Msg.ReadItemType(br); want.Comic = (int)Msg.ReadItemType(br); } 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 bool Unresolved; 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected I4, but got Unknown //IL_016b: 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_0175: Expected I4, but got Unknown //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0191: 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) 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++) { object? obj = list[j]; InteractableObject val2 = (InteractableObject)((obj is InteractableObject) ? obj : null); if (val2 != null) { num = num * 31 + ((i == 5) ? val2.m_DecoObjectType : 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++) { object? obj2 = list3[l]; InteractableObject val3 = (InteractableObject)((obj2 is InteractableObject) ? obj2 : null); if (!((Object)(object)val3 == (Object)null)) { list4.Add(new Entry { ObjType = (int)((k == 5) ? val3.m_DecoObjectType : 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected I4, but got Unknown //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: 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) && !want[i].Unresolved) { int num2 = (int)((kind == 5) ? val2.m_DecoObjectType : val2.m_ObjectType); if (num2 != want[i].ObjType) { CoopPlugin.Log.LogInfo((object)$"population: repairing index {i} (kind {kind}): {num2} -> {want[i].ObjType}"); val2.OnDestroyed(); OnClientStructureChanged?.Invoke(kind); return; } } } num = 8; while (list.Count < want.Count && num-- > 0) { Entry entry = want[list.Count]; if (!entry.Unresolved) { InteractableObject val3 = ((kind == 5) ? ShelfManager.SpawnDecoObject((EDecoObject)entry.ObjType) : 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 {((kind == 5) ? ((object)(EDecoObject)entry.ObjType/*cast due to .constrained prefix*/).ToString() : ((object)(EObjectType)entry.ObjType/*cast due to .constrained prefix*/).ToString())} (kind {kind})"); OnClientStructureChanged?.Invoke(kind); list = GetList(sm, kind); continue; } break; } break; } } private static EnumKind KindOf(int kind) { if (kind != 5) { return EnumKind.ObjectType; } return EnumKind.DecoObject; } public static void Write(BinaryWriter bw, List> all) { //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) //IL_00a6: 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) bw.Write((byte)all.Count); for (int i = 0; i < all.Count; i++) { List list = all[i]; bw.Write((ushort)list.Count); for (int j = 0; j < list.Count; j++) { Entry entry = list[j]; bw.Write(EnumMap.ToWire(KindOf(i), 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_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) int num = br.ReadByte(); List> list = new List>(num); for (int i = 0; i < num; i++) { int num2 = br.ReadUInt16(); List list2 = new List(num2); for (int j = 0; j < num2; j++) { list2.Add(new Entry { Unresolved = !EnumMap.TryFromWire(KindOf(i), br.ReadInt32(), out var localId), ObjType = localId, 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 readonly FieldInfo FiCashScreen = AccessTools.Field(typeof(InteractableCashierCounter), "m_UICashCounterScreen"); 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_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected I4, but got Unknown //IL_016f: 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_01c6: 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"; } 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 num6 = ((FiTotalScanned?.GetValue(val2) is double num7) ? num7 : 0.0); bool flag4 = false; EItemType v = (EItemType)0; CardData val3 = null; List itemInBagList = currentCustomer.GetItemInBagList(); for (int i = 0; i < itemInBagList.Count; i++) { if (flag4) { break; } InteractableScanItem val4 = (((Object)(object)itemInBagList[i] != (Object)null) ? itemInBagList[i].m_InteractableScanItem : null); if ((Object)(object)val4 != (Object)null && val4.IsNotScanned()) { v = itemInBagList[i].GetItemType(); ((InteractableObject)val4).OnMouseButtonUp(); flag4 = true; } } if (!flag4) { List cardInBagList = currentCustomer.GetCardInBagList(); for (int j = 0; j < cardInBagList.Count; j++) { if (flag4) { break; } if ((Object)(object)cardInBagList[j] != (Object)null && cardInBagList[j].IsNotScanned()) { try { val3 = cardInBagList[j].m_Card3dUI.m_CardUI.GetCardData(); } catch { } ((InteractableObject)cardInBagList[j]).OnMouseButtonUp(); flag4 = true; } } } if (!flag4) { return "nothing left to scan - wait a moment"; } double num8 = ((FiTotalScanned?.GetValue(val2) is double num9) ? num9 : num6); using (MemoryStream memoryStream = new MemoryStream()) { using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write((byte)counterIndex); binaryWriter.Write(val3 != null); binaryWriter.Write(num8 - num6); binaryWriter.Write(num8); if (val3 != null) { Msg.WriteCard(binaryWriter, val3); } else { Msg.WriteItemType(binaryWriter, v); } binaryWriter.Flush(); scanEcho = memoryStream.ToArray(); } int num10 = currentCustomer.GetItemInBagList().Count + currentCustomer.GetCardInBagList().Count; int num11 = ((FiScannedCount?.GetValue(currentCustomer) is int num12) ? num12 : 0); if (num11 < num10) { return $"scanned {num11}/{num10}"; } return $"all {num10} 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 flag2 = default(bool); int num2; if (obj is bool) { flag2 = (bool)obj; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { double num3 = ((FiTotalScanned?.GetValue(val2) is double num4) ? num4 : 0.0); MiCreditCard?.Invoke(val2, new object[1] { num3 }); CoopPlugin.Log.LogDebug((object)$"{serverName} completed a card sale at register {counterIndex}"); return "sale complete!"; } obj = FiIsChangeReady?.GetValue(val2); bool flag3 = default(bool); int num5; if (obj is bool) { flag3 = (bool)obj; num5 = 1; } else { num5 = 0; } if (((uint)num5 & (flag3 ? 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 void ClientResetScreens() { ShelfManager val = Shelf(); if ((Object)(object)val == (Object)null) { return; } for (int i = 0; i < val.m_CashierCounterList.Count; i++) { InteractableCashierCounter val2 = val.m_CashierCounterList[i]; if (!((Object)(object)val2 == (Object)null)) { object? obj = FiCashScreen?.GetValue(val2); UI_CashCounterScreen val3 = (UI_CashCounterScreen)((obj is UI_CashCounterScreen) ? obj : null); if ((Object)(object)val3 != (Object)null) { val3.ResetCounter(); } } } } 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++) { Msg.WriteItemType(binaryWriter, (EItemType)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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected I4, but got Unknown //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((int)Msg.ReadItemType(br)); 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_004f: 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) try { if (type == -1) { return null; } 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 bool s_haveOpenReport; private static readonly FieldInfo FiIsLerping = AccessTools.Field(typeof(EndOfDayReportScreen), "m_IsLerpingNumber"); private static readonly FieldInfo FiHoldingMouseDown = AccessTools.Field(typeof(EndOfDayReportScreen), "m_IsHoldingMouseDown"); private static readonly FieldInfo FiMouseDownTime = AccessTools.Field(typeof(EndOfDayReportScreen), "m_MouseDownTime"); 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; private static EndOfDayReportScreen _screen; private static InteractionPlayerController _ipc; public void Reset() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) _timer = -4.1f; _lastHash = 0; _heal = 0f; _reviewSeq = -1; s_openPending = false; s_reviewsDirty = false; s_clientOpenReport = default(GameReportDataCollect); s_haveOpenReport = false; _screen = null; _ipc = null; } 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) { if (CoopCore.Role != CoopRole.Client) { return true; } bool flag = false; try { flag = FiIsLerping != null && (bool)FiIsLerping.GetValue(__instance); } catch { } if (flag) { return true; } CloseClientReport(); return false; } public static bool NextDayBlockPrefix() { if (CoopCore.Role != CoopRole.Client) { return true; } CloseClientReport(); return false; } 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) 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)); Msg.WriteItemType(bw, 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_01e9: 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 = Msg.ReadItemType(br); 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)_screen == (Object)null) { _screen = Object.FindObjectOfType(); } if ((Object)(object)_screen == (Object)null || EndOfDayReportScreen.IsActive()) { return; } if ((Object)(object)_ipc == (Object)null) { _ipc = Object.FindObjectOfType(); } InteractionPlayerController ipc = _ipc; if ((Object)(object)ipc != (Object)null) { try { if (FiPhoneMode != null && (bool)FiPhoneMode.GetValue(ipc)) { return; } } catch { } try { if (FiCashMode != null && (bool)FiCashMode.GetValue(ipc)) { ipc.OnExitCashCounterMode(); } } catch { } } EndOfDayReportScreen.OpenScreen(); s_haveOpenReport = true; } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ReportSync open screen: " + ex.Message)); } } public static void CloseClientReport() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role != CoopRole.Client) { return; } try { if ((Object)(object)_screen == (Object)null) { _screen = Object.FindObjectOfType(); } if ((Object)(object)_screen == (Object)null || !EndOfDayReportScreen.IsActive()) { return; } if (s_haveOpenReport) { CPlayerData.m_GameReportDataCollect = s_clientOpenReport; } s_haveOpenReport = false; EndOfDayReportScreen.CloseScreen(); try { FiHoldingMouseDown?.SetValue(_screen, false); } catch { } try { FiMouseDownTime?.SetValue(_screen, 0f); } catch { } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ReportSync close screen: " + ex.Message)); } } } public static class SaveTransfer { public const int HostSnapshotSlot = 6; 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; string text = SlotPath(6); try { if (File.Exists(text)) { File.Delete(text); } } catch { } try { string path = Application.persistentDataPath + "/savedGames_Release" + 6 + ".gd"; if (File.Exists(path)) { File.Delete(path); } } catch { } int currentSaveLoadSlotSelectedIndex = instance.m_CurrentSaveLoadSlotSelectedIndex; try { instance.SaveGameData(6); } finally { instance.m_CurrentSaveLoadSlotSelectedIndex = currentSaveLoadSlotSelectedIndex; } if (!File.Exists(text)) { throw new FileNotFoundException("Host snapshot 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 ShelfManager _sm; private static InventoryBase _inv; private static readonly FieldInfo FiBuyCategory = AccessTools.Field(typeof(ShopBuyDecoUIScreen), "m_CategoryIndex"); private static ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } private static InventoryBase Inv() { if ((Object)(object)_inv == (Object)null) { _inv = Object.FindObjectOfType(); } return _inv; } public SettingsSync() { Instance = this; _stateBw = new BinaryWriter(_stateMs); } public void Reset() { _timer = -2.6f; _lastHash = 0; _heal = 0f; ApplyingRemote = false; _sm = null; _inv = null; } 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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) //IL_00ab: 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(); ECardExpansionType pendingGameEventExpansionType = Msg.ReadExpansion(br); CPlayerData.m_PendingGameEventFormat = (EGameEventFormat)num4; CPlayerData.m_PendingGameEventExpansionType = pendingGameEventExpansionType; break; } case 4: { int num5 = br.ReadInt32(); float num6 = br.ReadSingle(); if (num5 >= 0 && num5 < CPlayerData.m_SetGameEventPriceList.Count) { ApplyingRemote = true; try { PriceChangeManager.SetGameEventPrice((EGameEventFormat)num5, Mathf.Max(0f, num6)); break; } finally { ApplyingRemote = false; } } break; } case 5: { int num3 = br.ReadByte(); byte b2 = br.ReadByte(); List list2 = Sm()?.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 = Sm()?.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 = Inv()?.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_0097: 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_00a2: 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 = Msg.ReadExpansion(br); CPlayerData.m_PendingGameEventExpansionType = Msg.ReadExpansion(br); 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 = Sm()?.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 = Sm()?.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 = Inv()?.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_0085: Unknown result type (might be due to invalid IL or missing references) 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); Msg.WriteExpansion(bw, CPlayerData.m_GameEventExpansionType); Msg.WriteExpansion(bw, 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 = Sm()?.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 = Sm()?.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) bw.Write((byte)3); bw.Write((int)CPlayerData.m_PendingGameEventFormat); Msg.WriteExpansion(bw, 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 = Sm()?.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 = Sm()?.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 const byte OpToggleLight = 4; 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); private static readonly MethodInfo MiRoomInit = AccessTools.Method(typeof(UnlockRoomManager), "Init", (Type[])null, (Type[])null); private static readonly FieldInfo FiSgCurrent = AccessTools.Field(typeof(TutorialSubGroup), "m_CurrentValue"); private static readonly FieldInfo FiSgFinish = AccessTools.Field(typeof(TutorialSubGroup), "m_IsTaskFinish"); public Action> SendOp; public Action> BroadcastState; private float _timer; private int _lastHash; private float _heal; private double _lastRoomRepaint; private RentBillScreen _billScreen; private InteractableOpenCloseSign _openSign; private InteractableWarehouseAllowEnterSign _warehouseSign; private UnlockRoomManager _urm; private ShelfManager _shelfMgr; private UnlockRoomManager Urm() { if ((Object)(object)_urm == (Object)null) { _urm = Object.FindObjectOfType(); } return _urm; } public ShopStateSync() { _instance = this; } public void Reset() { _timer = -1.9f; _lastHash = 0; _heal = 0f; _billScreen = null; _openSign = null; _warehouseSign = null; _urm = null; _shelfMgr = 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 //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: 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)); Try(h, typeof(InteractableLightSwitch), "OnMouseButtonUp", new HarmonyMethod(typeof(ShopStateSync), "LightSwitchPrefix", (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 LightSwitchPrefix() { if (CoopCore.Role != CoopRole.Client || ApplyingRemote) { return true; } _instance?.SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)4); 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) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected I4, but got Unknown 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)); num = num * 31 + CPlayerData.m_TutorialIndex; List tutorialDataList = CPlayerData.m_TutorialDataList; if (tutorialDataList != null) { foreach (TutorialData item in tutorialDataList) { num = num * 31 + item.tutorialTaskCondition * 397 + (int)(item.value * 100f); } } _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) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected I4, but got Unknown 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); List tutorialDataList = CPlayerData.m_TutorialDataList; bw.Write(CPlayerData.m_TutorialIndex); bw.Write(tutorialDataList?.Count ?? 0); if (tutorialDataList == null) { return; } foreach (TutorialData item in tutorialDataList) { bw.Write((int)item.tutorialTaskCondition); bw.Write(item.value); } } 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; case 4: HostToggleLight(); break; } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ShopStateSync op " + b + ": " + ex.Message)); } ForceResend(); } private void HostToggleLight() { LightManager instance = CSingleton.Instance; if ((Object)(object)instance != (Object)null) { instance.ToggleShopLight(); } } 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_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Expected O, but got Unknown //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown UnlockRoomManager val = Urm(); if ((Object)(object)val == (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 = val.m_ShopB_UnlockPrice; if (CPlayerData.m_ShopLevel + 1 < val.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)); val.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 >= val.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)); val.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 >= val.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)); val.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 { if ((Object)(object)_shelfMgr == (Object)null) { _shelfMgr = Object.FindObjectOfType(); } if ((Object)(object)_shelfMgr != (Object)null) { _shelfMgr.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) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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) //IL_00b5: Expected O, but got Unknown 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(); int tutIndex = br.ReadInt32(); int num5 = br.ReadInt32(); List list = new List(); for (int i = 0; i < num5 && i < 4096; i++) { TutorialData item = new TutorialData { tutorialTaskCondition = (ETutorialTaskCondition)br.ReadInt32(), value = br.ReadSingle() }; list.Add(item); } if (flag && (Object)(object)BillScreen() != (Object)null) { try { MiBillEvaluateUI?.Invoke(_billScreen, null); } catch { } try { MiBillNotification?.Invoke(_billScreen, null); } catch { } } UnlockRoomManager val2 = Urm(); if ((Object)(object)val2 != (Object)null) { bool flag5 = (flag2 && !CPlayerData.m_IsWarehouseRoomUnlocked) || CPlayerData.m_UnlockRoomCount < num3 || CPlayerData.m_UnlockWarehouseRoomCount < num4; if (flag2 && !CPlayerData.m_IsWarehouseRoomUnlocked) { val2.SetUnlockWarehouseRoom(true); } int num6 = 0; while (CPlayerData.m_UnlockRoomCount < num3 && num6 < 64) { val2.StartUnlockNextRoom(); num6++; } int num7 = 0; while (CPlayerData.m_UnlockWarehouseRoomCount < num4 && num7 < 64) { val2.StartUnlockNextWarehouseRoom(); num7++; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (flag5 || realtimeSinceStartupAsDouble - _lastRoomRepaint > 60.0) { _lastRoomRepaint = realtimeSinceStartupAsDouble; try { MiRoomInit?.Invoke(val2, null); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("room repaint: " + ex.Message)); } } } if (CPlayerData.m_IsShopOpen != flag3) { CPlayerData.m_IsShopOpen = flag3; InteractableOpenCloseSign val3 = OpenSign(); if ((Object)(object)val3 != (Object)null) { try { MiOpenSignMesh?.Invoke(val3, null); } catch { } } } if (CPlayerData.m_IsWarehouseDoorClosed != flag4) { CPlayerData.m_IsWarehouseDoorClosed = flag4; InteractableWarehouseAllowEnterSign val4 = WarehouseSign(); if ((Object)(object)val4 != (Object)null) { try { MiWarehouseSignMesh?.Invoke(val4, null); } catch { } } else if ((Object)(object)val2 != (Object)null) { val2.EvaluateWarehouseRoomOpenClose(); } } ApplyTutorial(tutIndex, list); } private void ApplyTutorial(int tutIndex, List incoming) { //IL_0031: 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_0155: Unknown result type (might be due to invalid IL or missing references) List tutorialDataList = CPlayerData.m_TutorialDataList; bool flag = tutIndex == CPlayerData.m_TutorialIndex && tutorialDataList != null && tutorialDataList.Count == incoming.Count; if (flag) { for (int i = 0; i < incoming.Count; i++) { if (tutorialDataList[i].tutorialTaskCondition != incoming[i].tutorialTaskCondition || Mathf.Abs(tutorialDataList[i].value - incoming[i].value) > 0.001f) { flag = false; break; } } } if (flag) { return; } if (CPlayerData.m_TutorialDataList == null) { CPlayerData.m_TutorialDataList = new List(); } CPlayerData.m_TutorialDataList.Clear(); CPlayerData.m_TutorialDataList.AddRange(incoming); CPlayerData.m_TutorialIndex = tutIndex; TutorialManager val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null || val.m_TutorialSubGroupList == null) { return; } foreach (TutorialSubGroup tutorialSubGroup in val.m_TutorialSubGroupList) { if ((Object)(object)tutorialSubGroup == (Object)null) { continue; } try { FiSgCurrent?.SetValue(tutorialSubGroup, 0f); FiSgFinish?.SetValue(tutorialSubGroup, false); if (tutorialSubGroup.m_TutorialData != null) { tutorialSubGroup.m_TutorialData.value = 0f; } for (int j = 0; j < incoming.Count; j++) { tutorialSubGroup.AddTaskValue(incoming[j].value, incoming[j].tutorialTaskCondition); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("tutorial subgroup apply: " + ex.Message)); } } try { val.EvaluateTaskVisibility(); } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("tutorial visibility: " + ex2.Message)); } } } 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 = Object.FindObjectOfType(); } 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; private static CustomerManager _cm; private static CustomerManager Cm() { if ((Object)(object)_cm == (Object)null) { _cm = Object.FindObjectOfType(); } return _cm; } public void Reset() { _timer = -6.1f; _lastHash = 0; _heal = 0f; _cm = 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 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_014d: 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) 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 = Msg.ReadItemType(br); 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 val = Cm(); if ((Object)(object)val == (Object)null || (Object)(object)val.m_TournamentPairingScreen == (Object)null) { return; } TournamentPairingScreen tournamentPairingScreen = val.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 val2 = 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(val2); } } } 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); } Msg.WriteItemType(bw, (EItemType)((val != null) ? ((int)val.m_ItemType) : 0)); bw.Write(val?.m_Count ?? 0); } } CustomerManager val2 = Cm(); List list2 = (((Object)(object)val2 != (Object)null) ? val2.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 val3 = list2[k]; CustomerTournamentData val4 = (((Object)(object)val3 != (Object)null) ? val3.GetCustomerTournamentData() : null); if (val4 == 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(val4.m_TournamentCustomerSortedIndex, 0, 255)); bw.Write(val3.GetCustomerModelIndex()); bw.Write((byte)((val3.m_IsFemale ? 1u : 0u) | (uint)(val4.m_IsTournamentWin ? 2 : 0) | (uint)(val4.m_HasRegisteredTournamentResult ? 4 : 0))); bw.Write(val4.m_TournamentWinCount); bw.Write(val4.m_TournamentWinPoints); bw.Write(val4.m_TournamentOMW); bw.Write(val4.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 val2 = Cm(); List list2 = (((Object)(object)val2 != (Object)null) ? val2.m_TournamentSortedCustomerList : null); if (list2 != null) { for (int k = 0; k < list2.Count; k++) { CustomerTournamentData val3 = (((Object)(object)list2[k] != (Object)null) ? list2[k].GetCustomerTournamentData() : null); if (val3 != null) { num = num * 31 + val3.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) | (val3.m_IsTournamentWin ? 2 : 0) | (val3.m_HasRegisteredTournamentResult ? 4 : 0)); num = num * 31 + val3.m_TournamentWinCount; num = num * 31 + val3.m_TournamentWinPoints; num = num * 31 + val3.m_TournamentOMW; num = num * 31 + val3.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 const byte OpScreen = 3; 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 static readonly FieldInfo FiScrIsOpen = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_IsScreenOpen"); private static readonly FieldInfo FiScrGroup = AccessTools.Field(typeof(CustomerTradeCardScreen), "m_ScreenGroup"); private float _timer; private int _lastHash; private float _heal; private byte _resultSeq; private string _result = ""; private ShelfManager _sm; private CustomerManager _cm; private InteractionPlayerController _ipc; private readonly List _hostBuf = new List(); private readonly Dictionary _preRollFailUntil = new Dictionary(); 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 bool _hostBusy; private float _claimTimer; private readonly Dictionary _guestClaims = new Dictionary(); 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; _cm = null; _ipc = null; _hostBuf.Clear(); _preRollFailUntil.Clear(); _unknownLogged.Clear(); _offers.Clear(); _staleTimer = 0f; _opThrottle = 0f; _seenSeq = -1; _lastOfferCount = -1; _pendingCounter = -1; _nativeBroken = false; _playerTf = null; _hostBusy = false; _claimTimer = 0f; _guestClaims.Clear(); } 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(Customer __instance) { //IL_013d: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role == CoopRole.Host) { TradeServe live = _live; if (live != null && live._guestClaims.Count > 0) { try { ShelfManager val = live.Sm(); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; foreach (KeyValuePair guestClaim in live._guestClaims) { if (realtimeSinceStartupAsDouble - guestClaim.Value >= 5.0) { continue; } InteractableCashierCounter val2 = (((Object)(object)val != (Object)null && guestClaim.Key >= 0 && guestClaim.Key < val.m_CashierCounterList.Count) ? val.m_CashierCounterList[guestClaim.Key] : null); if ((Object)(object)val2 != (Object)null && FiTradeCounter?.GetValue(__instance) == val2) { if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "your partner is answering that customer"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } } } catch { } } return true; } 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 num = live._pendingCounter; object obj = FiScrTrading?.GetValue(__instance); bool flag = default(bool); int num2; if (obj is bool) { flag = (bool)obj; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag ? 1u : 0u)) == 0) { try { TMP_InputField setPriceInput = __instance.m_SetPriceInput; string text = (((Object)(object)setPriceInput != (Object)null) ? setPriceInput.text : null); if (!string.IsNullOrWhiteSpace(text)) { __instance.OnInputTextUpdated(text); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: price commit: " + ex.Message)); } } float num3 = ((FiScrPriceSet?.GetValue(__instance) is float num4) ? num4 : 0f); if (num3 <= 0f) { num3 = -1f; } CoopPlugin.Log.LogInfo((object)$"TradeServe client: accept pressed on native screen (counter {num}, price {num3:F2})"); if (num < 0) { num = live.RebindOrphanedScreen("accept"); if (num >= 0) { num3 = -1f; } } if (num >= 0) { live.SendOpFor(1, num, num3, "answering the customer..."); } try { ((UIScreenBase)__instance).CloseScreen(); } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)("TradeServe client: screen close: " + ex2.Message)); } return false; } public static bool ClientDeclinePrefix() { if (CoopCore.Role != CoopRole.Client) { return true; } TradeServe live = _live; if (live != null) { int num = live._pendingCounter; if (num < 0) { num = live.RebindOrphanedScreen("decline"); } if (num >= 0) { CoopPlugin.Log.LogInfo((object)$"TradeServe client: decline pressed on native screen (counter {num})"); live.SendOpFor(2, num, 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; } try { FiTradeData?.SetValue(__instance, null); FiPausing?.SetValue(__instance, false); RestorePlayerFromUiMode(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: UI restore: " + ex.Message)); } finally { if (_live != null) { _live._pendingCounter = -1; } } return false; } private static void RestorePlayerFromUiMode() { InteractionPlayerController val = ((_live != null) ? _live.Ipc() : null); if ((Object)(object)val != (Object)null) { val.ExitWorkerInteractMode(); val.StopAimLookAt(); if ((Object)(object)val.m_WalkerCtrl != (Object)null) { val.m_WalkerCtrl.SetStopMovement(false); } val.ExitUIMode(); } GameUIScreen.ResetToolTipVisibility(); GameUIScreen.ResetEnterGoNextDayIndicatorVisible(); TutorialManager.SetGameUIVisible(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)); } } private ShelfManager Sm() { if ((Object)(object)_sm == (Object)null) { _sm = Object.FindObjectOfType(); } return _sm; } private CustomerManager Cm() { if ((Object)(object)_cm == (Object)null) { _cm = Object.FindObjectOfType(); } return _cm; } private InteractionPlayerController Ipc() { if ((Object)(object)_ipc == (Object)null) { _ipc = Object.FindObjectOfType(); } return _ipc; } private static string CardName(CardData c) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0015: 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_005d: Expected I4, but got Unknown if (c == null) { return "a card"; } string text = null; try { if ((int)c.expansionType < 7) { MonsterData monsterData = InventoryBase.GetMonsterData(c.monsterType); if (monsterData != null) { text = monsterData.GetName(); } } } catch { } if (string.IsNullOrEmpty(text)) { text = (((int)c.expansionType < 7) ? ((object)Unsafe.As(ref c.monsterType)/*cast due to .constrained prefix*/).ToString() : (((object)Unsafe.As(ref c.expansionType)/*cast due to .constrained prefix*/).ToString() + "#" + (int)c.monsterType)); } if (c.isFoil) { text += " (foil)"; } if (c.cardGrade > 0) { int num = (GradingInterop.Present ? GradingInterop.Actual(c.cardGrade) : c.cardGrade); if (num > 0) { text += $" [grade {num}]"; } } 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Invalid comparison between Unknown and I4 if (!inGame) { return; } _timer += dt; if (_timer < 0.5f) { return; } _timer -= 0.5f; try { CustomerManager val = Cm(); ShelfManager val2 = Sm(); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } _hostBuf.Clear(); List customerList = val.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { if (_hostBuf.Count >= 32) { break; } Customer val3 = customerList[i]; if ((Object)(object)val3 == (Object)null || !val3.m_IsActive || (int)val3.m_CurrentState != 18) { continue; } object? obj = FiTradeCounter?.GetValue(val3); InteractableCashierCounter val4 = (InteractableCashierCounter)((obj is InteractableCashierCounter) ? obj : null); if ((Object)(object)val4 == (Object)null) { continue; } int num = val2.m_CashierCounterList.IndexOf(val4); if (num >= 0 && num <= 250) { if (_guestClaims.TryGetValue(num, out var value) && Time.realtimeSinceStartupAsDouble - value < 5.0) { FiCustTimer?.SetValue(val3, 0f); } object? obj2 = FiTradeData?.GetValue(val3); CustomerTradeData val5 = (CustomerTradeData)((obj2 is CustomerTradeData) ? obj2 : null); if (val5 == null) { val5 = PreRoll(val, val3); } float num2 = ((FiCustTimer?.GetValue(val3) is float num3) ? num3 : 0f); bool flag = val5 != null && val5.m_CardData_L != null && (!val5.m_IsTrading || val5.m_CardData_R != null); if (!flag && _unknownLogged.Add(((Object)val3).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 = val5.m_IsTrading; item.CardL = val5.m_CardData_L; item.CardR = val5.m_CardData_R; item.Price = val5.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 static CardData CopyCard(CardData src) { //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_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) //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_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown if (src == null) { return null; } CardData val = new CardData(); try { val.CopyData(src); } catch { val.expansionType = src.expansionType; val.monsterType = src.monsterType; val.borderType = src.borderType; val.isFoil = src.isFoil; val.isDestiny = src.isDestiny; val.isChampionCard = src.isChampionCard; val.isNew = src.isNew; val.cardGrade = src.cardGrade; val.gradedCardIndex = src.gradedCardIndex; } return val; } private CustomerTradeData PreRoll(CustomerManager cm, Customer cust) { //IL_005b: 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_0093: 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_0101: 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_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: 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_021a: 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 (_preRollFailUntil.TryGetValue(instanceID, out var value) && Time.time < value) { 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 = CopyCard((CardData)((obj2 is CardData) ? obj2 : null)); ref CardData cardData_R = ref val.m_CardData_R; object? obj3 = FiScrCardR?.GetValue(customerTradeCardScreen); cardData_R = CopyCard((CardData)((obj3 is CardData) ? obj3 : null)); CustomerTradeData val2 = val; if (val2.m_CardData_L == null) { CoopPlugin.Log.LogWarning((object)"TradeServe: pre-roll produced no card; will retry next tick"); 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) { _preRollFailUntil[instanceID] = Time.time + 5f; CoopPlugin.Log.LogWarning((object)("TradeServe pre-roll: " + ex.Message)); return null; } finally { try { cm.m_IsPlayerTrading = false; } catch { } } } private void WriteState(BinaryWriter bw) { bool value = false; try { CustomerManager val = Cm(); value = (Object)(object)val != (Object)null && (val.m_IsPlayerTrading || ((Object)(object)val.m_CustomerTradeCardScreen != (Object)null && ((UIScreenBase)val.m_CustomerTradeCardScreen).IsScreenOpened())); } catch { } bw.Write(value); 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(); if (b == 3) { _guestClaims[num] = Time.realtimeSinceStartupAsDouble; return; } 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 _guestClaims.Remove(idx); CustomerManager val = Cm(); ShelfManager val2 = Sm(); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || idx < 0 || idx >= val2.m_CashierCounterList.Count) { Result("no counter there"); return; } InteractableCashierCounter val3 = val2.m_CashierCounterList[idx]; Customer val4 = null; List customerList = val.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { Customer val5 = customerList[i]; if ((Object)(object)val5 != (Object)null && val5.m_IsActive && (int)val5.m_CurrentState == 18 && FiTradeCounter?.GetValue(val5) == val3) { val4 = val5; break; } } if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null) { Result("the customer already left"); return; } if (val.m_IsPlayerTrading) { Result("the host is talking to that customer right now"); return; } switch (op) { case 2: _preRollFailUntil.Remove(((Object)val4).GetInstanceID()); FinishCustomer(val4, val3); Result("trade declined - the customer moves on"); break; case 1: { object? obj = FiTradeData?.GetValue(val4); CustomerTradeData val6 = (CustomerTradeData)((obj is CustomerTradeData) ? obj : null); if (val6 == null) { val6 = PreRoll(val, val4); } if (val6 == null || val6.m_CardData_L == null) { Result("couldn't read the offer - the host must serve this one"); break; } float num = ((float.IsNaN(price) || price < 0f) ? val6.m_SellCardAskPrice : price); if (val6.m_IsTrading) { CardData cardData_R = val6.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 = val.m_CustomerTradeCardScreen; if ((Object)(object)customerTradeCardScreen == (Object)null || ((UIScreenBase)customerTradeCardScreen).IsScreenOpened()) { Result("the host has the trade screen open"); break; } float sellCardAskPrice = val6.m_SellCardAskPrice; int declineCount = val6.m_DeclineCount; bool flag2; float num3; int num5; try { customerTradeCardScreen.SetCustomer(val4, val6); if (!val6.m_IsTrading) { FiScrPriceSet?.SetValue(customerTradeCardScreen, num); } CoopPlugin.Log.LogInfo((object)("TradeServe host: applying vanilla accept (" + (val6.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) { val6.m_PriceSet = ((FiScrPriceSet?.GetValue(customerTradeCardScreen) is float num7) ? num7 : num); val6.m_LastPriceSet = ((FiScrLastPrice?.GetValue(customerTradeCardScreen) is float num8) ? num8 : num); val6.m_SellCardAskPrice = num3; val6.m_MaxDeclineCount = ((FiScrMaxDecline?.GetValue(customerTradeCardScreen) is int num9) ? num9 : val6.m_MaxDeclineCount); val6.m_DeclineCount = num5; FiTradeData?.SetValue(val4, val6); } } finally { try { val.m_IsPlayerTrading = false; } catch { } } if (flag2) { _preRollFailUntil.Remove(((Object)val4).GetInstanceID()); FinishCustomer(val4, val3); Result(val6.m_IsTrading ? ("traded " + CardName(val6.m_CardData_R) + " for " + CardName(val6.m_CardData_L)) : ("bought " + CardName(val6.m_CardData_L) + " for " + Price(num))); } else if (val6.m_IsTrading) { Result("the trade fell through - the host must serve this one"); } else if (num5 == declineCount) { _preRollFailUntil.Remove(((Object)val4).GetInstanceID()); FinishCustomer(val4, val3); 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) { _hostBusy = br.ReadBoolean(); 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 bool AnyKnownOffer() { foreach (KeyValuePair offer in _offers) { if (offer.Value.Known) { return true; } } 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 val = Ipc(); if ((Object)(object)val == (Object)null) { return null; } _playerTf = (((Object)(object)val.m_WalkerCtrl != (Object)null) ? ((Component)val.m_WalkerCtrl).transform : ((Component)val).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 int RebindOrphanedScreen(string action) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { Transform val = PlayerBody(); if ((Object)(object)val != (Object)null) { int num = RegisterServe.FindNearestCounter(val.position, Reach, quiet: true); if (num >= 0 && _offers.TryGetValue(num, out var value) && value.Known) { _pendingCounter = num; CoopPlugin.Log.LogInfo((object)$"TradeServe client: rebound orphaned trade screen to counter {num}"); return num; } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: rebind: " + ex.Message)); } CoopPlugin.Log.LogWarning((object)("TradeServe client: " + action + " press had no bound counter and no resolvable offer nearby - closing the dead screen")); return -1; } private void OpenNativeScreen(int idx, Offer offer) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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) //IL_00b9: 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_00df: 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) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown CustomerManager val = Cm(); CustomerTradeCardScreen val2 = (((Object)(object)val != (Object)null) ? val.m_CustomerTradeCardScreen : null); if ((Object)(object)val2 == (Object)null) { throw new InvalidOperationException("no CustomerTradeCardScreen"); } if (((UIScreenBase)val2).IsScreenOpened()) { return; } Customer val3 = null; List customerList = val.GetCustomerList(); for (int i = 0; i < customerList.Count; i++) { if ((Object)(object)customerList[i] != (Object)null) { val3 = customerList[i]; break; } } if ((Object)(object)val3 == (Object)null) { throw new InvalidOperationException("no carrier customer in the pool"); } CustomerTradeData val4 = 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 = (offer.Trading ? 0f : offer.Price), m_LastPriceSet = (offer.Trading ? 0f : offer.Price), m_MaxDeclineCount = 0, m_DeclineCount = 0 }; val2.SetCustomer(val3, val4); if (!offer.Trading && (Object)(object)val2.m_SetPriceInput != (Object)null) { try { val2.m_SetPriceInput.SetTextWithoutNotify(GameInstance.GetPriceString(offer.Price, false, false, false, "F2")); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: seed price field: " + ex.Message)); } } InteractionPlayerController obj = Ipc(); if ((Object)(object)obj == (Object)null) { throw new InvalidOperationException("no InteractionPlayerController"); } obj.EnterWorkerInteractMode(); obj.EnterUIMode(); obj.EnterLockMoveMode(); GameUIScreen.HideToolTip(); GameUIScreen.HideEnterGoNextDayIndicatorVisible(); TutorialManager.SetGameUIVisible(false); ((UIScreenBase)val2).OpenScreen(); _pendingCounter = idx; _claimTimer = 999f; CoopPlugin.Log.LogInfo((object)$"TradeServe client: opened native trade screen for counter {idx}"); } public void ClientTick(float dt, bool inGame) { //IL_04f6: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_0352: 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++) { if (_keyBuf[i] != _pendingCounter) { Offer value = _offers[_keyBuf[i]]; value.Remaining -= dt; if (value.Remaining <= 0f) { _offers.Remove(_keyBuf[i]); } else { _offers[_keyBuf[i]] = value; } } } } } if (_pendingCounter >= 0) { _claimTimer += dt; if (_claimTimer >= 2f) { _claimTimer = 0f; int pc = _pendingCounter; SendOp?.Invoke(delegate(BinaryWriter bw) { bw.Write((byte)3); bw.Write((byte)pc); bw.Write(0f); }); } CustomerManager val = Cm(); CustomerTradeCardScreen val2 = (((Object)(object)val != (Object)null) ? val.m_CustomerTradeCardScreen : null); if ((Object)(object)val2 == (Object)null || !((UIScreenBase)val2).IsScreenOpened()) { _pendingCounter = -1; } else { if (_offers.ContainsKey(_pendingCounter)) { return; } CoopPlugin.Log.LogInfo((object)"TradeServe client: offer vanished while the screen was open - closing it"); try { ((UIScreenBase)val2).CloseScreen(); } catch { } if (((UIScreenBase)val2).IsScreenOpened()) { CoopPlugin.Log.LogWarning((object)"TradeServe client: vanilla close chain left the trade screen open - forcing hard teardown"); try { FiScrIsOpen?.SetValue(val2, false); object? obj2 = FiScrGroup?.GetValue(val2); GameObject val3 = (GameObject)((obj2 is GameObject) ? obj2 : null); if ((Object)(object)val3 != (Object)null) { val3.SetActive(false); } RestorePlayerFromUiMode(); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("TradeServe client: hard teardown: " + ex.Message)); } } _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) { return; } bool keyDown = Input.GetKeyDown(CoopPlugin.ServeKey.Value); bool keyDown2 = Input.GetKeyDown((KeyCode)98); if (!keyDown && !keyDown2) { return; } if (CoopUI.TextFieldFocused) { CoopPlugin.Log.LogInfo((object)"TradeServe client: serve/decline key ignored (co-op window text field focused)"); return; } if (CoopCore.NativeTextInputFocused()) { CoopPlugin.Log.LogInfo((object)"TradeServe client: serve/decline key ignored (game text field being edited)"); return; } Transform val4 = PlayerBody(); if ((Object)(object)val4 == (Object)null) { CoopPlugin.Log.LogInfo((object)"TradeServe client: key pressed but no player body resolved"); return; } int num = RegisterServe.FindNearestCounter(val4.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 (_hostBusy) { _opThrottle = 0.5f; if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "the host is serving a customer right now"; CoopCore.Instance.RegisterLineTimer = 3f; } return; } if (!_nativeBroken) { _opThrottle = 0.3f; try { OpenNativeScreen(num, value2); return; } catch (Exception ex2) { _nativeBroken = true; _pendingCounter = -1; try { CustomerManager val5 = Cm(); if ((Object)(object)val5 != (Object)null) { val5.m_IsPlayerTrading = false; } } catch { } CoopPlugin.Log.LogWarning((object)("TradeServe client: native trade screen failed, falling back to prompt keys: " + ex2)); 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 struct ClampState { public int Type; public int Requested; public int Actual; } private readonly Dictionary _last = new Dictionary(); private readonly Dictionary _locallyChanged = new Dictionary(); private readonly Dictionary _resolvable = new Dictionary(); private readonly Dictionary _clamped = new Dictionary(); private readonly HashSet _clampWarned = new HashSet(); 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(); _locallyChanged.Clear(); _resolvable.Clear(); _clamped.Clear(); _clampWarned.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_CardItemCombiShelfList.Count; k++) { CardItemCombiShelf val3 = val.m_CardItemCombiShelfList[k]; if (!((Object)(object)val3 == (Object)null)) { List itemCompartmentList2 = val3.GetItemCompartmentList(); for (int l = 0; l < itemCompartmentList2.Count; l++) { Visit(Key(3, k, l), itemCompartmentList2[l], ref changes); } } } for (int m = 0; m < val.m_TournamentPrizeShelfList.Count; m++) { TournamentPrizeShelf val4 = val.m_TournamentPrizeShelfList[m]; if (!((Object)(object)val4 == (Object)null)) { List itemCompartmentList3 = ((CardItemCombiShelf)val4).GetItemCompartmentList(); for (int n = 0; n < itemCompartmentList3.Count; n++) { Visit(Key(14, m, n), itemCompartmentList3[n], 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)) { if (value.Type == num && value.Count == itemCount) { return; } } else if (CoopCore.Role == CoopRole.Client) { _last[key] = new CompState { Type = num, Count = itemCount }; return; } if (changes == null) { changes = new List(); } if (changes.Count < 512) { _last[key] = new CompState { Type = num, Count = itemCount }; if (CoopCore.Role == CoopRole.Client) { _locallyChanged[key] = Time.realtimeSinceStartupAsDouble; } changes.Add(new Entry { Key = key, Type = num, Count = itemCount }); } } public void ApplyRemote(List entries) { //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected I4, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Invalid comparison between Unknown and I4 ShelfManager val = ResolveShelfManager(); if ((Object)(object)val == (Object)null) { return; } foreach (Entry entry in entries) { ShelfCompartment val2 = null; try { if (CoopCore.Role == CoopRole.Client && _locallyChanged.TryGetValue(entry.Key, out var value) && Time.realtimeSinceStartupAsDouble - value < 6.0) { continue; } val2 = Resolve(val, entry.Key); if ((Object)(object)val2 == (Object)null || (_clamped.TryGetValue(entry.Key, out var value2) && value2.Type == entry.Type && value2.Requested == entry.Count && (int)val2.GetItemType() == entry.Type && val2.GetItemCount() == value2.Actual)) { continue; } if (entry.Count == 0 || (entry.Type != -1 && CanResolve(entry.Type))) { ApplyCompartment(val2, entry.Type, entry.Count); } goto IL_0129; } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"WorldSync apply {entry.Key:X}: {ex.Message}"); goto IL_0129; } IL_0129: if ((Object)(object)val2 == (Object)null) { continue; } try { int num = (int)val2.GetItemType(); int itemCount = val2.GetItemCount(); _last[entry.Key] = new CompState { Type = num, Count = itemCount }; if (num == entry.Type && itemCount < entry.Count) { _clamped[entry.Key] = new ClampState { Type = entry.Type, Requested = entry.Count, Actual = itemCount }; if (_clampWarned.Add(entry.Key)) { CoopPlugin.Log.LogWarning((object)$"WorldSync: compartment {entry.Key:X} only holds {itemCount} of the {entry.Count} item(s) the host has there (type {entry.Type}) - your copy of that content pack gives the shelf fewer slots; it will stay short instead of rebuilding every heal"); } } else { _clamped.Remove(entry.Key); } } catch (Exception ex2) { CoopPlugin.Log.LogWarning((object)$"WorldSync read-back {entry.Key:X}: {ex2.Message}"); } } } private bool CanResolve(int type) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0045: Unknown result type (might be due to invalid IL or missing references) if (type == -1) { return true; } if (_resolvable.TryGetValue(type, out var value)) { return value; } bool flag = false; try { ItemData itemData = InventoryBase.GetItemData((EItemType)type); if (itemData != null) { Vector3 itemDimension = itemData.itemDimension; flag = itemDimension.x > 0f && itemDimension.y > 0f && itemDimension.z > 0f; } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)$"WorldSync item type {type} lookup: {ex.Message}"); } _resolvable[type] = flag; if (!flag) { CoopPlugin.Log.LogWarning((object)$"WorldSync: shelf item type {type} is from a content pack you don't have - that compartment will look empty for you"); } return flag; } 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 list3 = ((obj3 != null) ? obj3.GetItemCompartmentList() : null); if (list3 == null || num3 >= list3.Count) { return null; } return list3[num3]; } case 3: { if (num2 >= sm.m_CardItemCombiShelfList.Count) { return null; } CardItemCombiShelf obj2 = sm.m_CardItemCombiShelfList[num2]; List list2 = ((obj2 != null) ? obj2.GetItemCompartmentList() : null); if (list2 == null || num3 >= list2.Count) { return null; } return list2[num3]; } case 14: { if (num2 >= sm.m_TournamentPrizeShelfList.Count) { return null; } TournamentPrizeShelf obj = sm.m_TournamentPrizeShelfList[num2]; List list = ((obj != null) ? ((CardItemCombiShelf)obj).GetItemCompartmentList() : null); if (list == null || num3 >= list.Count) { return null; } return list[num3]; } default: return null; } } 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); Msg.WriteItemType(bw, (EItemType)entry.Type); bw.Write((ushort)Math.Max(0, Math.Min(entry.Count, 65535))); } } public static List ReadEntries(BinaryReader br) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected I4, but got Unknown int num = br.ReadUInt16(); List list = new List(num); for (int i = 0; i < num; i++) { list.Add(new Entry { Key = br.ReadInt32(), Type = (int)Msg.ReadItemType(br), Count = br.ReadUInt16() }); } return list; } public void BuildFullState(BinaryWriter bw) { List list = new List(); try { ShelfManager val = ResolveShelfManager(); if ((Object)(object)val != (Object)null) { for (int i = 0; i < val.m_ShelfList.Count; i++) { Shelf obj = val.m_ShelfList[i]; CollectComps(list, (obj != null) ? obj.GetItemCompartmentList() : null, 0, i); } for (int j = 0; j < val.m_CardItemCombiShelfList.Count; j++) { CardItemCombiShelf obj2 = val.m_CardItemCombiShelfList[j]; CollectComps(list, (obj2 != null) ? obj2.GetItemCompartmentList() : null, 3, j); } for (int k = 0; k < val.m_TournamentPrizeShelfList.Count; k++) { TournamentPrizeShelf obj3 = val.m_TournamentPrizeShelfList[k]; CollectComps(list, (obj3 != null) ? ((CardItemCombiShelf)obj3).GetItemCompartmentList() : null, 14, k); } } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("WorldSync full-state build: " + ex.Message)); } WriteEntries(bw, list); } private static void CollectComps(List into, List comps, int kind, int shelfIdx) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected I4, but got Unknown if (comps == null) { return; } for (int i = 0; i < comps.Count; i++) { ShelfCompartment val = comps[i]; if (!((Object)(object)val == (Object)null)) { into.Add(new Entry { Key = Key(kind, shelfIdx, i), Type = (int)val.GetItemType(), Count = val.GetItemCount() }); } } } public void ApplyFullState(BinaryReader br) { ApplyRemote(ReadEntries(br)); } } } namespace CardShopCoop.Patches { public static class GamePatches { public static bool ApplyingRemoteLicense; private static readonly FieldInfo FiOobTimer = AccessTools.Field(typeof(RestockManager), "m_OutofBoundCheckTimer"); private static InteractionPlayerController _sprayIpc; private static int _sprayForwardFrame = -1; private static readonly FieldInfo FiHoldSprayItem = AccessTools.Field(typeof(InteractionPlayerController), "m_CurrentHoldSprayItem"); private static readonly FieldInfo FiHoldingMouseDown = AccessTools.Field(typeof(InteractionPlayerController), "m_IsHoldingMouseDown"); 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_00f7: 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_0179: 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_0224: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Expected O, but got Unknown //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Expected O, but got Unknown //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Expected O, but got Unknown //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Expected O, but got Unknown //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Expected O, but got Unknown //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Expected O, but got Unknown //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Expected O, but got Unknown //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Expected O, but got Unknown //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Expected O, but got Unknown //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Expected O, but got Unknown //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03dd: Expected O, but got Unknown //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_0408: 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(InteractionPlayerController), "ShowGoNextDayScreen", new HarmonyMethod(typeof(GamePatches), "GoNextDayScreenBlockPrefix", (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(RestockManager), "Update", new HarmonyMethod(typeof(GamePatches), "RestockUpdatePrefix", (Type[])null)); Try(h, typeof(RestockManager), "GenerateCardMarketPrice", new HarmonyMethod(typeof(GamePatches), "GenerateCardMarketPriceBlockPrefix", (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(InteractionPlayerController), "ConfirmSellFurniture", new HarmonyMethod(typeof(GamePatches), "SellFurnitureBlockPrefix", (Type[])null)); Try(h, typeof(PlaceDecoUIScreen), "StartPlaceDecoItem", new HarmonyMethod(typeof(GamePatches), "PlaceDecoBlockPrefix", (Type[])null)); Try(h, typeof(InteractableCashierCounter), "OnMouseButtonUp", new HarmonyMethod(typeof(GamePatches), "CashierCounterClickBlockPrefix", (Type[])null)); Try(h, typeof(Customer), "DeodorantSprayCheck", new HarmonyMethod(typeof(GamePatches), "DeodorantSprayPrefix", (Type[])null)); Try(h, typeof(ShelfManager), "DisableMoveObjectPreviewMode", new HarmonyMethod(typeof(GamePatches), "DisablePreviewGuardPrefix", (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)); Try(h, typeof(CPlayerData), "RemoveGradedCard", null, new HarmonyMethod(typeof(GamePatches), "RemoveGradedCardPostfix", (Type[])null)); Try(h, typeof(PauseScreen), "OpenScreen", null, new HarmonyMethod(typeof(GamePatches), "PauseNoFreezePostfix", (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); TryModule("furnboxes", FurnBoxSync.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) { return; } try { CoopCore.Instance?.ForwardLicense(index); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("LicenseUnlockPostfix forward failed: " + ex.Message)); } } 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 void RestockUpdatePrefix(RestockManager __instance) { if (CoopCore.Role != CoopRole.Client) { return; } try { FiOobTimer?.SetValue(__instance, 0f); } catch { } } public static bool GenerateCardMarketPriceBlockPrefix() { return CoopCore.Role != CoopRole.Client; } public static bool DisablePreviewGuardPrefix() { try { ShelfManager instance = CSingleton.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_MoveObjectPreviewModel == (Object)null) { return false; } } catch { return false; } return true; } 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 bool SellFurnitureBlockPrefix() { if (CoopCore.Role != CoopRole.Client) { return true; } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "selling furniture is host-only for now - ask the host"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } public static bool PlaceDecoBlockPrefix() { if (CoopCore.Role != CoopRole.Client) { return true; } if ((Object)(object)CoopCore.Instance != (Object)null) { CoopCore.Instance.RegisterLine = "deco placement is host-only for now"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } public static bool CashierCounterClickBlockPrefix() { //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 = $"press {CoopPlugin.ServeKey.Value} at the counter to serve customers"; CoopCore.Instance.RegisterLineTimer = 3f; } return false; } public static bool DeodorantSprayPrefix(Vector3 sprayPos, float range, int potency) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (CoopCore.Role != CoopRole.Client) { return true; } try { if ((Object)(object)_sprayIpc == (Object)null) { _sprayIpc = Object.FindObjectOfType(); } InteractionPlayerController sprayIpc = _sprayIpc; bool flag = default(bool); int num; if ((Object)(object)sprayIpc != (Object)null && FiHoldSprayItem?.GetValue(sprayIpc) != null) { object obj = FiHoldingMouseDown?.GetValue(sprayIpc); if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0 && Time.frameCount != _sprayForwardFrame) { _sprayForwardFrame = Time.frameCount; CoopCore.Instance?.ForwardSprayHit(sprayPos, range, potency); } } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("spray forward: " + ex.Message)); } return false; } public static void SetCardPricePostfix(CardData cardData, float priceSet) { if (ApplyingRemotePrice || CoopCore.Role == CoopRole.None) { return; } try { int num = (GradingInterop.Present ? GradingInterop.Encoded(cardData) : cardData.cardGrade); if (num > 10 && cardData.cardGrade <= 10 && cardData.cardGrade != 0) { int cardGrade = cardData.cardGrade; cardData.cardGrade = num; try { CoopCore.Instance?.ForwardCardPrice(cardData, priceSet); return; } finally { cardData.cardGrade = cardGrade; } } CoopCore.Instance?.ForwardCardPrice(cardData, priceSet); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("SetCardPricePostfix forward failed: " + ex.Message)); } } public static void AddCardPostfix(CardData cardData, int addAmount) { if (ApplyingRemoteCards || CoopCore.Role == CoopRole.None) { return; } try { int num = (GradingInterop.Present ? GradingInterop.Encoded(cardData) : cardData.cardGrade); if (num > 10 && cardData.cardGrade <= 10 && cardData.cardGrade != 0) { int cardGrade = cardData.cardGrade; cardData.cardGrade = num; try { CoopCore.Instance?.ForwardCardDelta(cardData, addAmount, isAdd: true); return; } finally { cardData.cardGrade = cardGrade; } } CoopCore.Instance?.ForwardCardDelta(cardData, addAmount, isAdd: true); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("AddCardPostfix forward failed: " + ex.Message)); } } public static void ReduceCardPostfix(CardData cardData, int reduceAmount) { if (ApplyingRemoteCards || CoopCore.Role == CoopRole.None) { return; } try { CoopCore.Instance?.ForwardCardDelta(cardData, reduceAmount, isAdd: false); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("ReduceCardPostfix forward failed: " + ex.Message)); } } public static void RemoveGradedCardPostfix(CardData cardData) { if (ApplyingRemoteCards || CoopCore.Role == CoopRole.None || cardData == null || cardData.cardGrade <= 0) { return; } try { CoopCore.Instance?.ForwardGradedRemoval(cardData); } catch (Exception ex) { CoopPlugin.Log.LogWarning((object)("RemoveGradedCardPostfix forward failed: " + ex.Message)); } } 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 void PauseNoFreezePostfix() { if (CoopCore.Role != CoopRole.None) { Time.timeScale = 1f; } } public static bool SaveGuardPrefix() { if (CoopCore.Role != CoopRole.Client) { return !CoopCore.GuestBorrowedWorld; } return false; } public static bool ClientBlockPrefix() { return CoopCore.Role != CoopRole.Client; } public static bool GoNextDayScreenBlockPrefix() { 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, FurnBoxOp, FurnBoxState, GradedRemove, SprayHit, CardDeltaBatch } 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 WriteItemType(BinaryWriter bw, EItemType v) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected I4, but got Unknown bw.Write(EnumMap.ToWire(EnumKind.ItemType, (int)v)); } public static EItemType ReadItemType(BinaryReader br) { return (EItemType)EnumMap.FromWire(EnumKind.ItemType, br.ReadInt32()); } public static void WriteObjType(BinaryWriter bw, EObjectType v) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected I4, but got Unknown bw.Write(EnumMap.ToWire(EnumKind.ObjectType, (int)v)); } public static EObjectType ReadObjType(BinaryReader br) { return (EObjectType)EnumMap.FromWire(EnumKind.ObjectType, br.ReadInt32()); } public static void WriteDecoType(BinaryWriter bw, EDecoObject v) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected I4, but got Unknown bw.Write(EnumMap.ToWire(EnumKind.DecoObject, (int)v)); } public static EDecoObject ReadDecoType(BinaryReader br) { return (EDecoObject)EnumMap.FromWire(EnumKind.DecoObject, br.ReadInt32()); } public static void WriteExpansion(BinaryWriter bw, ECardExpansionType v) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected I4, but got Unknown bw.Write(EnumMap.ToWire(EnumKind.CardExpansion, (int)v)); } public static ECardExpansionType ReadExpansion(BinaryReader br) { return (ECardExpansionType)EnumMap.FromWire(EnumKind.CardExpansion, br.ReadInt32()); } public static void WriteMonsterType(BinaryWriter bw, EMonsterType v) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected I4, but got Unknown bw.Write(EnumMap.ToWire(EnumKind.MonsterType, (int)v)); } public static EMonsterType ReadMonsterType(BinaryReader br) { return (EMonsterType)EnumMap.FromWire(EnumKind.MonsterType, br.ReadInt32()); } public static void WriteCard(BinaryWriter bw, CardData card) { //IL_0002: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown WriteExpansion(bw, card.expansionType); WriteMonsterType(bw, 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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_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 = ReadExpansion(br), monsterType = ReadMonsterType(br), 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 int _stallRetries; 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0372: 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)) { byte b = result.Frame[4]; if (b == 21 || b == 30) { _transientScratch.Add(result); continue; } int key = (result.ConnId << 8) | b; 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)) { _stallRetries = 0; } else if (!SteamNetworking.SendP2PPacket(value3, result2.Frame, (uint)result2.Frame.Length, (EP2PSend)2, 71)) { if (++_stallRetries <= 30) { _stalled = result2; break; } CoopPlugin.Log.LogWarning((object)$"steam: dropping stuck reliable frame (size {result2.Frame.Length}); lane reconverges on next heal"); _stalled = null; _stallRetries = 0; } else { _stallRetries = 0; 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.37"); 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(); } } }