using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using DG.Tweening; using FlowCanvas; using GK2.FlowCanvasNodes; using HarmonyLib; using LazyBearTechnology; using Microsoft.CodeAnalysis; using Sirenix.Serialization; using Steamworks; using TMPro; using Unity.Collections; using Unity.Netcode; using Unity.Netcode.Transports.UTP; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Rendering; 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("GK2Coop")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("GK2Coop")] [assembly: AssemblyTitle("GK2Coop")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace GK2Coop { internal static class ChestLocks { private const byte OpRequest = 1; private const byte OpGrant = 2; private const byte OpDeny = 3; private const byte OpRelease = 4; private const byte OpTable = 5; private static readonly Dictionary locks = new Dictionary(); private static string myOpenChest; private static float openedAt; private static ChestInteractionHandler pendingHandler; private static PlayerController pendingInteractor; private static string pendingChest; private static string grantedChest; private static ulong Me { get { if (CoopSession.Role != CoopRole.Host) { return CoopNetwork.LocalClientId; } return 0uL; } } public static void Reset() { locks.Clear(); myOpenChest = null; pendingHandler = null; pendingInteractor = null; pendingChest = null; grantedChest = null; } private static string Name(ulong id) { object obj; if (id == 0L) { obj = PlayerSync.Host?.Label ?? "the host"; } else { obj = PartnerProfiles.NameOf(id); if (obj == null) { obj = PlayerSync.Get(id)?.Label; if (obj == null) { return $"Player {id}"; } } } return (string)obj; } public static bool BeforeOpen(ChestInteractionHandler handler, PlayerController interactor, Wgo chest) { if (CoopSession.Role != CoopRole.None && CoopSession.KeepWorldRunning) { object obj; if (chest == null) { obj = null; } else { WgoData data = chest.Data; obj = ((data != null) ? data.UniqueId : null); } if (!((SGuid)obj == (SGuid)null)) { string id = chest.Data.UniqueId.Id; if (locks.TryGetValue(id, out var value) && value != Me) { CoopRunner.Think(Name(value) + " is using this chest."); return false; } if (CoopSession.Role == CoopRole.Host) { SetLock(id, 0uL); MarkOpen(id); return true; } if (grantedChest == id) { grantedChest = null; MarkOpen(id); return true; } pendingHandler = handler; pendingInteractor = interactor; pendingChest = id; Send(null, 1, id); return false; } } return true; } private static void MarkOpen(string uid) { myOpenChest = uid; openedAt = Time.unscaledTime; } public static void Tick() { if (myOpenChest != null && !(Time.unscaledTime - openedAt < 0.5f) && !AnyChestWindowOpen()) { string uid = myOpenChest; myOpenChest = null; if (CoopSession.Role == CoopRole.Host) { ReleaseLock(uid); } else if (CoopSession.Role == CoopRole.Client) { ContainerSync.SendChangedEverywhere(); Send(null, 4, uid); } } } private static bool AnyChestWindowOpen() { try { UIChestWindow window = LazyUI.GetWindow(); int result; if (window == null || !((LazyWindow)(object)window).IsShown) { UIConveyorChestWindow window2 = LazyUI.GetWindow(); if (window2 == null || !((LazyWindow)(object)window2).IsShown) { UIConveyorVegetablesChestWindow window3 = LazyUI.GetWindow(); if (window3 == null || !((LazyWindow)(object)window3).IsShown) { result = ((((LazyWindow)(object)LazyUI.GetWindow())?.IsShown ?? false) ? 1 : 0); goto IL_004d; } } } result = 1; goto IL_004d; IL_004d: return (byte)result != 0; } catch { return false; } } public static void OnPlayerLeft(ulong clientId) { foreach (string item in (from p in locks where p.Value == clientId select p.Key).ToList()) { ReleaseLock(item); } } private static void SetLock(string uid, ulong owner) { locks[uid] = owner; BroadcastTable(); } private static void ReleaseLock(string uid) { if (locks.Remove(uid)) { BroadcastTable(); } } private unsafe static void BroadcastTable() { //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_0010: 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_001f: 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_003d: 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_007d: 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 (CoopSession.Role != CoopRole.Host) { return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 5; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = locks.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (KeyValuePair @lock in locks) { CoopMessages.WriteString(ref writer, @lock.Key); ulong value = @lock.Value; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref value, default(ForPrimitives)); } CoopMessages.SendToReadyClients("GK2Coop.Locks", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void Send(ulong? to, byte op, string uid) { //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_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_000e: 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_002f: 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) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref op, default(ForPrimitives)); CoopMessages.WriteString(ref writer, uid); if (CoopSession.Role == CoopRole.Client) { CoopMessages.SendToHost("GK2Coop.Locks", writer, (NetworkDelivery)3); } else if (to.HasValue) { CoopMessages.SendTo("GK2Coop.Locks", to.Value, writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public static void OnLocks(ulong sender, FastBufferReader reader) { //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_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_0046: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (b == 5) { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); locks.Clear(); ulong value = default(ulong); for (int i = 0; i < num; i++) { string key = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref value, default(ForPrimitives)); locks[key] = value; } return; } string text = CoopMessages.ReadString(ref reader); switch (b) { case 1: if (CoopSession.Role == CoopRole.Host) { if (locks.TryGetValue(text, out var value4) && value4 != sender) { Send(sender, 3, text); CoopLog.Info(Name(sender) + " can't open chest " + text + ": " + Name(value4) + " has it open."); } else { SetLock(text, sender); Send(sender, 2, text); } } break; case 4: { if (CoopSession.Role == CoopRole.Host && locks.TryGetValue(text, out var value3) && value3 == sender) { ReleaseLock(text); } break; } case 2: if (CoopSession.Role != CoopRole.Client) { break; } if (pendingChest == text && pendingHandler != null && (Object)(object)pendingInteractor != (Object)null) { grantedChest = text; ChestInteractionHandler val = pendingHandler; PlayerController val2 = pendingInteractor; pendingHandler = null; pendingInteractor = null; pendingChest = null; try { ((WGOInteractionHandlerBase)val).Interact(val2); break; } catch (Exception e) { CoopLog.Error("Opening the chest after the host's OK failed", e); Send(null, 4, text); break; } } Send(null, 4, text); break; case 3: if (CoopSession.Role == CoopRole.Client) { pendingHandler = null; pendingInteractor = null; pendingChest = null; CoopRunner.Think(locks.TryGetValue(text, out var value2) ? (Name(value2) + " is using this chest.") : "Someone else is using this chest."); } break; } } } [HarmonyPatch(typeof(ChestInteractionHandler), "Interact")] internal static class LockChestWhileOpen { private static bool Prefix(ChestInteractionHandler __instance, PlayerController interactor, Wgo ___assignedWgo, ref bool __result) { try { if (!ChestLocks.BeforeOpen(__instance, interactor, ___assignedWgo)) { __result = true; return false; } } catch (Exception e) { CoopLog.Error("Chest lock check failed; opening anyway", e); } return true; } } internal static class CombatSync { private struct HpEntry { public string Uid; public int Hp; public int Max; } private const byte OpFightState = 1; private const byte OpDamage = 2; private const byte OpPlayerHit = 3; private const byte OpKnockedOut = 4; private const byte OpHp = 5; private const byte OpWaves = 6; private const float HpInterval = 0.25f; private const float LocalHitGrace = 1.2f; private static readonly FieldInfo HpChangedField = AccessTools.Field(typeof(HPComponent), "OnHpChanged"); private static readonly FieldInfo FirstDamageField = AccessTools.Field(typeof(HPComponent), "OnFirstDamageDealt"); private static readonly FieldInfo FullHpField = AccessTools.Field(typeof(HPComponent), "OnFullHpRestored"); private static readonly MethodInfo EnsureLevelMethod = AccessTools.Method(typeof(FightingGameController), "EnsureFightingLevelLoaded", (Type[])null, (Type[])null); private static readonly FieldRef WasLevelLoadedField = AccessTools.FieldRefAccess("wasLevelLoaded"); private static readonly FieldRef BodyPlayerDataField = AccessTools.FieldRefAccess("playerData"); private static readonly FieldRef PlayerGuidField = AccessTools.FieldRefAccess("guid"); public static int Sent; public static int Applied; private static FightState hostState = (FightState)0; private static string hostLevel = ""; private static string hostScene = ""; private static readonly Dictionary registered = new Dictionary(); private static readonly Dictionary avatarHp = new Dictionary(); private static readonly HashSet knockedOutPartners = new HashSet(); private static readonly Dictionary sentHp = new Dictionary(); private static readonly Dictionary hpSceneSentTo = new Dictionary(); private static float nextHp; private static float nextTargets; private static float nextWaves; private static readonly FieldRef HostWavesField = AccessTools.FieldRefAccess("presetProcessor"); private static readonly MethodInfo WavesProgressSetter = AccessTools.PropertySetter(typeof(FightingLevelPresetProcessor), "CurrentProgress"); private static readonly MethodInfo WavesTotalSetter = AccessTools.PropertySetter(typeof(FightingLevelPresetProcessor), "TotalDuration"); private static readonly FieldInfo WavesProgressEvent = AccessTools.Field(typeof(FightingLevelPresetProcessor), "OnProgressChanged"); private static FightingLevelPresetProcessor shownWaves; private static bool timelineShown; private static float hostWavesProgress; private static float hostWavesTotal; private static FightState fightHere = (FightState)0; private static string fightLevel = ""; private static string fightScene = ""; private static bool levelShown; private static bool levelLoadedHere; private static string shownLevel = ""; private static bool armed; private static bool gearChecked; private static bool knockedOut; private static float nextStanceCheck; private static readonly HashSet markedFighters = new HashSet(); private static readonly Dictionary recentLocalHits = new Dictionary(); public static bool ApplyingRemote; public static bool FightAroundHost { get { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if (CoopSession.Role == CoopRole.Client) { return (int)fightHere > 0; } return false; } } public static bool InHostFight { get { if (CoopSession.Role == CoopRole.Client) { return armed; } return false; } } public static void Reset() { //IL_0043: 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) if (armed || levelShown) { try { LeaveFight("session ended"); } catch (Exception e) { CoopLog.Error("Putting the weapon away failed", e); } } HideTimeline(); hostWavesProgress = 0f; hostWavesTotal = 0f; hostState = (FightState)0; hostLevel = ""; hostScene = ""; registered.Clear(); avatarHp.Clear(); knockedOutPartners.Clear(); sentHp.Clear(); hpSceneSentTo.Clear(); fightHere = (FightState)0; fightLevel = ""; fightScene = ""; levelShown = false; levelLoadedHere = false; shownLevel = ""; armed = false; gearChecked = false; knockedOut = false; markedFighters.Clear(); recentLocalHits.Clear(); ApplyingRemote = false; Sent = 0; Applied = 0; } public static void Tick() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Invalid comparison between Unknown and I4 if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && CoopSession.IsInGame) { if (Time.unscaledTime >= nextHp) { nextHp = Time.unscaledTime + 0.25f; SendHp(); } if (Time.unscaledTime >= nextTargets) { nextTargets = Time.unscaledTime + 0.5f; UpdatePartnerTargets(); } if ((int)hostState == 2 && Time.unscaledTime >= nextWaves) { nextWaves = Time.unscaledTime + 0.25f; SendWaves(); } } else if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { TickStamina(); if (Time.unscaledTime >= nextStanceCheck) { nextStanceCheck = Time.unscaledTime + 0.5f; UpdateStance(); } } } private static void TickStamina() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!armed || MainGame.IsGamePaused) { return; } try { FightingGameController instance = LazySingleton.Instance; if ((Object)(object)instance != (Object)null && (int)instance.CurrentFightState != 0) { return; } PlayerData playerData = MainGame.PlayerData; if (playerData != null) { StaminaSystem staminaSystem = playerData.staminaSystem; if (staminaSystem != null) { staminaSystem.UpdateStaminaLogic(Time.deltaTime); } } } catch (Exception ex) { CoopLog.Debug("Refilling stamina failed: " + ex.Message); } } private static long Pack(HPComponent hp) { return ((long)hp.MaxHpValue << 32) | (uint)hp.Hp; } private static void SendHp() { HashSet hashSet = NpcSync.ScenesWithPartners(); List list = new List(); foreach (string item in hashSet) { foreach (WgoData item2 in SceneObjects(item)) { HPComponent hpComponent = item2.HpComponent; if (hpComponent != null && hpComponent.MaxHpValue > 0 && !(item2.UniqueId == (SGuid)null)) { string id = item2.UniqueId.Id; long num = Pack(hpComponent); long value; bool num2 = sentHp.TryGetValue(id, out value); sentHp[id] = num; if (num2 && value != num && hpComponent.Hp > 0) { list.Add(new HpEntry { Uid = id, Hp = hpComponent.Hp, Max = hpComponent.MaxHpValue }); } } } } SendHpEntries(list, null); foreach (ulong item3 in CoopSession.ReadyClients.ToList()) { RemotePlayer remotePlayer = PlayerSync.Get(item3); if (remotePlayer == null || !remotePlayer.HasState || string.IsNullOrEmpty(remotePlayer.Scene) || (hpSceneSentTo.TryGetValue(item3, out var value2) && value2 == remotePlayer.Scene)) { continue; } hpSceneSentTo[item3] = remotePlayer.Scene; List list2 = new List(); foreach (WgoData item4 in SceneObjects(remotePlayer.Scene)) { HPComponent hpComponent2 = item4.HpComponent; if (hpComponent2 != null && hpComponent2.MaxHpValue > 0 && hpComponent2.Hp > 0 && hpComponent2.Hp < hpComponent2.MaxHpValue && item4.UniqueId != (SGuid)null) { list2.Add(new HpEntry { Uid = item4.UniqueId.Id, Hp = hpComponent2.Hp, Max = hpComponent2.MaxHpValue }); } } SendHpEntries(list2, item3); if (list2.Count > 0) { CoopLog.Debug($"Sent the HP of {list2.Count} damaged objects in '{remotePlayer.Scene}' to {remotePlayer.Label}."); } } } private static IEnumerable SceneObjects(string scene) { WorldData worldData = MainGame.WorldData; GameSceneData val = ((worldData != null) ? worldData.GetGameSceneDataById(scene) : null); if (val == null) { return Enumerable.Empty(); } return val.wgoDataList.Where((WgoData d) => d != null).ToList(); } private unsafe static void SendHpEntries(List entries, ulong? onlyTo) { //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_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_002c: 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_0047: 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_0080: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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) for (int i = 0; i < entries.Count; i += 80) { List list = entries.Skip(i).Take(80).ToList(); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 5; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (HpEntry item in list) { HpEntry current = item; CoopMessages.WriteString(ref writer, current.Uid); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref current.Hp, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref current.Max, default(ForPrimitives)); } if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Combat", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Combat", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } } public static void NoteLocalHit(WgoData data) { if (((data != null) ? data.UniqueId : null) != (SGuid)null && CoopSession.Role == CoopRole.Client) { recentLocalHits[data.UniqueId.Id] = Time.unscaledTime + 1.2f; } } public static void ApplyHitHp(WgoData data, int hp, int max) { HPComponent val = ((data != null) ? data.HpComponent : null); if (val == null || hp <= 0 || max <= 0) { return; } if (CoopSession.Role == CoopRole.Host) { if (hp < val.Hp) { SetHp(data, hp, max); } } else { ApplyHostHp(data, hp, max); } } private static void ApplyHostHp(WgoData data, int hp, int max) { HPComponent hpComponent = data.HpComponent; if (hpComponent != null && (hp <= hpComponent.Hp || !recentLocalHits.TryGetValue(data.UniqueId.Id, out var value) || !(Time.unscaledTime < value))) { SetHp(data, hp, max); } } private static void SetHp(WgoData data, int hp, int max) { HPComponent hpComponent = data.HpComponent; if (hpComponent == null || hpComponent.isDeathDelayed || (hpComponent.Hp == hp && hpComponent.MaxHpValue == max)) { return; } bool flag = !hpComponent.WasDamagedAtLeastOnce; if (hpComponent.MaxHpValue != max) { hpComponent.SetCustomHpValue(max, true); } hpComponent.SetCustomHpValue(Mathf.Clamp(hp, 1, max), false); bool flag2 = hp >= max; hpComponent.wasDamagedAtLeastOnce = !flag2; try { if (flag && !flag2) { (FirstDamageField?.GetValue(hpComponent) as Action)?.Invoke(); } (HpChangedField?.GetValue(hpComponent) as Action)?.Invoke(hpComponent); if (flag2) { (FullHpField?.GetValue(hpComponent) as Action)?.Invoke(); } } catch (Exception ex) { CoopLog.Warn("HP change listeners on " + ((ObjectLinkedToDefinition)(object)data).id + " failed: " + ex.Message); } Applied++; } public unsafe static void OnHostFightState(FightingGameController controller, FightState state) { //IL_0009: 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_0062: 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_007e: Invalid comparison between Unknown and I4 if (CoopSession.Role == CoopRole.Host) { hostState = state; string value = (((Object)(object)controller.CurrentLevel != (Object)null) ? controller.CurrentLevel.id : ""); if (!string.IsNullOrEmpty(value)) { hostLevel = value; } hostScene = MainGame.PlayerData?.currentGameSceneId ?? ""; CoopLog.Info($"Host fight state: {state} (level '{hostLevel}', area '{hostScene}')."); if ((int)state != 2) { UnregisterPartners("fight is " + ((object)(*(FightState*)(&state))/*cast due to .constrained prefix*/).ToString()); knockedOutPartners.Clear(); } if (CoopSession.HasReadyClients) { SendFightState(null); } } } private unsafe static void SendFightState(ulong? onlyTo) { //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_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_0010: 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_0029: 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_0082: 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) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = (byte)hostState; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, hostLevel ?? ""); CoopMessages.WriteString(ref writer, hostScene ?? ""); if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Combat", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Combat", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } public static void OnClientReady(ulong clientId) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) hpSceneSentTo.Remove(clientId); if ((int)hostState != 0) { SendFightState(clientId); } } private static void UpdatePartnerTargets() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 FightingGameController instance = LazySingleton.Instance; if ((Object)(object)instance == (Object)null || (int)instance.CurrentFightState != 2) { if (registered.Count > 0) { UnregisterPartners("no fight"); } return; } string text = MainGame.PlayerData?.currentGameSceneId; foreach (ulong item in CoopSession.ReadyClients.ToList()) { RemotePlayer remotePlayer = PlayerSync.Get(item); bool flag = remotePlayer != null && (Object)(object)remotePlayer.Body != (Object)null && remotePlayer.Visible && remotePlayer.HasState && remotePlayer.Scene == text && !knockedOutPartners.Contains(item); registered.TryGetValue(item, out var value); if (flag && (Object)(object)value != (Object)(object)remotePlayer.Body) { if ((Object)(object)value != (Object)null) { Unregister(item, value); } Register(item, remotePlayer); } else if (!flag && (Object)(object)value != (Object)null) { Unregister(item, value); } } foreach (ulong item2 in registered.Keys.Where((ulong c) => !CoopSession.ReadyClients.Contains(c)).ToList()) { Unregister(item2, registered[item2]); } } private static void Register(ulong clientId, RemotePlayer remote) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown try { PlayerData val = (((Object)(object)remote.Controller != (Object)null) ? remote.Controller.PlayerData : null); if (val != null) { if (PlayerGuidField.Invoke(val) == (SGuid)null || PlayerGuidField.Invoke(val).Equals(MainGame.PlayerData.Guid)) { PlayerGuidField.Invoke(val) = new SGuid(); } BodyPlayerDataField.Invoke(remote.Body) = val; val.hpComponent.RestoreFullHp(); remote.Body.IsActiveCombatant = true; LazySingleton.Instance.RegisterTargetNonPersistent((ICombatEntity)(object)remote.Body, -1, -1, true); registered[clientId] = remote.Body; avatarHp[val.hpComponent] = clientId; CoopLog.Info(remote.Label + " is now a fight target."); } } catch (Exception e) { CoopLog.Error("Making " + remote.Label + " a fight target failed", e); } } private static void Unregister(ulong clientId, PlayerPhysicalBody body) { registered.Remove(clientId); foreach (HPComponent item in (from p in avatarHp where p.Value == clientId select p.Key).ToList()) { avatarHp.Remove(item); } try { if ((Object)(object)body != (Object)null) { LazySingleton.Instance.UnregisterTarget((ICombatEntity)(object)body); body.IsActiveCombatant = true; } } catch (Exception ex) { CoopLog.Warn("Removing a partner from the fight targets failed: " + ex.Message); } } public static void UnregisterPartners(string why) { if (registered.Count == 0) { return; } CoopLog.Info($"Removing {registered.Count} partners from the fight targets ({why})."); foreach (KeyValuePair item in registered.ToList()) { Unregister(item.Key, item.Value); } } public unsafe static void OnHpDamaged(HPComponent component, int before) { //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_0035: 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_0040: 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_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_0066: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || component == null || !avatarHp.TryGetValue(component, out var value)) { return; } int num = before - component.Hp; component.RestoreFullHp(); if (num > 0) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); CoopMessages.SendTo("GK2Coop.Combat", value, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Debug($"{PlayerSync.Get(value)?.Label ?? value.ToString()} was hit for {num}."); } } private static void HostApplyPartnerDamage(ulong sender, string uid, int damage) { //IL_00e0: 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) //IL_00e5: 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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(uid)) : null); HPComponent val2 = ((val != null) ? val.HpComponent : null); if (val2 == null || damage <= 0) { return; } Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(val.UniqueId); val2.ApplyDamage(damage); Applied++; CoopLog.Debug($"{PlayerSync.Get(sender)?.Label ?? sender.ToString()} hit {((ObjectLinkedToDefinition)(object)val).id} for {damage} (HP {val2.Hp}/{val2.MaxHpValue})."); if ((Object)(object)wgoViewGlobal == (Object)null) { return; } try { PlayerPhysicalBody physicalBody = MainGame.PlayerController.PhysicalBody; Vector3 val3 = PlayerSync.Get(sender)?.Position ?? ((Component)physicalBody).transform.position; Vector3 val4 = val.Position - val3; Vector3 normalized = ((Vector3)(ref val4)).normalized; AttackContext val5 = default(AttackContext); ((AttackContext)(ref val5))..ctor((ICombatEntity)(object)physicalBody, (TeamType)0, (FighterDef)null, (ItemDef)null, val3, normalized, val.Position, damage, false, (AttackComponent)null, (DamageEffectSettings)null); wgoViewGlobal.OnOtherCombatTargetHitMe(val5); } catch (Exception ex) { CoopLog.Warn("On-hit logic for " + ((ObjectLinkedToDefinition)(object)val).id + " failed: " + ex.Message); } } private static void UpdateStance() { //IL_0000: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Invalid comparison between Unknown and I4 //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if ((int)fightHere == 0) { gearChecked = false; } else if (!gearChecked) { PlayerController playerController = MainGame.PlayerController; if (((playerController != null) ? playerController.PlayerData : null) != null) { gearChecked = true; FightGear.EnsureOwn(MainGame.PlayerController, withSword: true); } } PlayerController playerController2 = MainGame.PlayerController; string text = ((playerController2 == null) ? null : playerController2.PlayerData?.currentGameSceneId); bool flag = (int)fightHere != 0 && !string.IsNullOrEmpty(text) && text == fightScene; if (flag && (!levelShown || shownLevel != fightLevel)) { ShowLevel(); } bool flag2 = flag && (int)fightHere == 2 && !knockedOut; if (flag2 && !armed) { Arm(); } else if (!flag2 && armed) { Disarm(); } if (!flag && levelShown) { LeaveFight(((int)fightHere == 0) ? "the fight ended" : "we left the fight's area"); } else if (flag) { MarkFighters(text); } } private static void ShowLevel() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Invalid comparison between Unknown and I4 levelShown = true; shownLevel = fightLevel; if (string.IsNullOrEmpty(fightLevel)) { return; } FightingGameController instance = LazySingleton.Instance; try { bool flag = WasLevelLoadedField.Invoke(instance); WasLevelLoadedField.Invoke(instance) = false; EnsureLevelMethod?.Invoke(instance, new object[1] { fightLevel }); levelLoadedHere = WasLevelLoadedField.Invoke(instance); WasLevelLoadedField.Invoke(instance) = flag; } catch (Exception e) { CoopLog.Error("Loading fight level '" + fightLevel + "' here failed", e); } try { FightingLevel fightingLevel = MainGame.GetFightingLevel(fightLevel); if ((Object)(object)fightingLevel != (Object)null) { fightingLevel.ApplyStageId(((int)fightHere == 2) ? 4 : 3); fightingLevel.SetActive(true); fightingLevel.SetBorderObjectsActive(true); } CoopLog.Info($"Showing the host's fight level '{fightLevel}' (loaded here: {levelLoadedHere}, found: {(Object)(object)fightingLevel != (Object)null})."); } catch (Exception e2) { CoopLog.Error("Showing fight level '" + fightLevel + "' failed", e2); } } private static void Arm() { armed = true; try { PlayerController playerController = MainGame.PlayerController; MainGame.PlayerData.RemoveInteractingItem(); FightGear.EnsureOwn(playerController, withSword: true); playerController.SetArmorView(true, (int?)null, true); FightGear.DrawWeapon(playerController); Item sword = playerController.Sword; Item bow = playerController.Bow; ItemDef val = ((sword != null && ((ObjectLinkedToDefinition)(object)sword).id != "empty") ? ((ObjectLinkedToDefinition)(object)sword).Definition : ((bow != null && ((ObjectLinkedToDefinition)(object)bow).id != "empty") ? ((ObjectLinkedToDefinition)(object)bow).Definition : null)); MainGame.PlayerData.staminaSystem.SetMax(); playerController.View.UpdateStaminaBarState(); try { FightingLevel fightingLevel = MainGame.GetFightingLevel(fightLevel); if (fightingLevel != null) { fightingLevel.ApplyStageId(4); } } catch { } ShowTimeline(); string text = PlayerSync.Host?.Label ?? "the host"; CoopRunner.Toast("Fight! Help " + text + ": attack the enemies.", 6f); CoopLog.Info("Joined " + text + "'s fight with " + ((val != null) ? ((BalanceBaseObject)val).id : "the default sword") + "."); } catch (Exception e) { CoopLog.Error("Getting the weapon out failed", e); } } private unsafe static void SendWaves() { //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_0029: 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_0033: 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_004e: 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_0069: 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_007b: Unknown result type (might be due to invalid IL or missing references) FightingLevelPresetProcessor val = HostWavesField.Invoke(LazySingleton.Instance); if ((Object)(object)val == (Object)null || !val.IsPlaying) { return; } FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 6; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); float currentProgress = val.CurrentProgress; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref currentProgress, default(ForPrimitives)); currentProgress = val.TotalDuration; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref currentProgress, default(ForPrimitives)); CoopMessages.SendToReadyClients("GK2Coop.Combat", writer, (NetworkDelivery)1); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private static void ShowTimeline() { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //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_00a1: Expected O, but got Unknown if (timelineShown) { return; } try { FightingLevel fightingLevel = MainGame.GetFightingLevel(fightLevel); HUD val = LazyUI.Get(); if ((Object)(object)((fightingLevel != null) ? fightingLevel.FightingLevelPreset : null) == (Object)null || (Object)(object)val == (Object)null) { CoopLog.Debug("No wave timeline for '" + fightLevel + "' here (level " + (((Object)(object)fightingLevel != (Object)null) ? "has no preset" : "not found") + ")."); return; } if ((Object)(object)shownWaves == (Object)null) { GameObject val2 = new GameObject("GK2Coop fight waves"); Object.DontDestroyOnLoad((Object)val2); shownWaves = val2.AddComponent(); } SetShownWaves(hostWavesProgress, hostWavesTotal); val.DrawFightingTimeline(new UIFightingTimelineRendererData(fightingLevel.FightingLevelPreset, shownWaves, fightingLevel)); val.SetMode((HUDMode)1); timelineShown = true; CoopLog.Info("Showing the host's wave timeline for '" + fightLevel + "'."); } catch (Exception ex) { CoopLog.Warn("Showing the wave timeline failed: " + ex.Message); } } private static void HideTimeline() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 if (!timelineShown) { return; } timelineShown = false; try { HUD val = LazyUI.Get(); if ((Object)(object)val != (Object)null && (int)val.Mode == 1) { val.HideFightingTimeline(); val.SetMode((HUDMode)0); } } catch (Exception ex) { CoopLog.Warn("Putting the HUD back after the fight failed: " + ex.Message); } } private static void SetShownWaves(float progress, float total) { if (!((Object)(object)shownWaves == (Object)null)) { WavesTotalSetter?.Invoke(shownWaves, new object[1] { total }); WavesProgressSetter?.Invoke(shownWaves, new object[1] { Mathf.Clamp(progress, 0f, Mathf.Max(total, progress)) }); (WavesProgressEvent?.GetValue(shownWaves) as Action)?.Invoke(shownWaves.ProgressNormalized); } } private static void Disarm() { HideTimeline(); armed = false; try { PlayerController playerController = MainGame.PlayerController; playerController.AttackComponent.UnequipWeapon(); playerController.SetArmorView(false, (int?)null, true); playerController.View.UpdateStaminaBarState(); } catch (Exception e) { CoopLog.Error("Putting the weapon away failed", e); } } private static void LeaveFight(string why) { if (armed) { Disarm(); } UnmarkFighters(); if (levelShown && !string.IsNullOrEmpty(shownLevel)) { try { FightingLevel fightingLevel = MainGame.GetFightingLevel(shownLevel); if ((Object)(object)fightingLevel != (Object)null) { fightingLevel.SetActive(false); fightingLevel.SetBorderObjectsActive(false); } GameSceneData val = default(GameSceneData); GameSceneConfig val2 = default(GameSceneConfig); if (levelLoadedHere && MainGame.WorldData.TryGetGameSceneDataForContent(shownLevel, ref val, ref val2)) { val.RemoveFightingLevelData(shownLevel); } } catch (Exception e) { CoopLog.Error("Hiding fight level '" + shownLevel + "' failed", e); } } try { PlayerData playerData = MainGame.PlayerData; if (playerData != null) { HPComponent hpComponent = playerData.hpComponent; if (hpComponent != null) { hpComponent.RestoreFullHp(); } } } catch { } CoopLog.Info("Left the fight (" + why + ")."); levelShown = false; levelLoadedHere = false; shownLevel = ""; knockedOut = false; } private static void MarkFighters(string scene) { HashSet hashSet = GameBalance.Me?.fighterWgoIdsCache; if (hashSet == null) { return; } foreach (WgoData item in SceneObjects(scene)) { if (!(item.UniqueId == (SGuid)null) && hashSet.Contains(((ObjectLinkedToDefinition)(object)item).id)) { Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(item.UniqueId); if ((Object)(object)wgoViewGlobal != (Object)null && !wgoViewGlobal.IsActiveCombatant) { wgoViewGlobal.IsActiveCombatant = true; markedFighters.Add(item.UniqueId.Id); } } } } private static void UnmarkFighters() { foreach (string markedFighter in markedFighters) { try { Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(markedFighter)); if ((Object)(object)wgoViewGlobal != (Object)null) { wgoViewGlobal.IsActiveCombatant = false; } } catch { } } markedFighters.Clear(); } public unsafe static bool OnLocalAttackHit(AttackComponent attacker, ICombatEntity target, AttackContext context) { //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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00db: 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) if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || !Plugin.HostRunsWorld.Value || ApplyingRemote) { return true; } PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController == (Object)null || (Object)(object)attacker != (Object)(object)playerController.AttackComponent) { return false; } Wgo val = (Wgo)(object)((target is Wgo) ? target : null); if (val != null) { WgoData data = val.Data; if (!(((data != null) ? data.UniqueId : null) == (SGuid)null) && !knockedOut) { int num = 0; try { num = target.ArmorValue; } catch { } int num2 = Mathf.Clamp(((AttackContext)(ref context)).Damage - num, 0, int.MaxValue); if (num2 > 0) { FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, val.Data.UniqueId.Id); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num2, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Combat", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } return false; } } return false; } private unsafe static void OnPlayerHit(int damage) { //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_0083: 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_008d: 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_009f: Unknown result type (might be due to invalid IL or missing references) HPComponent val = MainGame.PlayerData?.hpComponent; if (val == null || knockedOut || damage <= 0) { return; } if (val.Hp - damage > 0) { val.ApplyDamage(damage); return; } val.SetCustomHpValue(1, false); val.wasDamagedAtLeastOnce = true; try { (HpChangedField?.GetValue(val) as Action)?.Invoke(val); } catch { } knockedOut = true; if (armed) { Disarm(); } FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Combat", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } CoopRunner.Toast("You're knocked out until the fight ends.", 8f); CoopLog.Info("Knocked out in the host's fight."); } public static void OnCombat(ulong sender, FastBufferReader reader) { //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_005a: 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_007e: 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_0086: 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_00ae: 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_016d: 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_026a: 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_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Invalid comparison between Unknown and I4 //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_028a: 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_029c: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Client && !CoopSession.ClientInWorld && b != 1) { return; } switch (b) { case 1: if (CoopSession.Role == CoopRole.Client) { byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); fightLevel = CoopMessages.ReadString(ref reader); fightScene = CoopMessages.ReadString(ref reader); FightState val2 = fightHere; fightHere = (FightState)b2; CoopLog.Info($"Host fight state {fightHere} (level '{fightLevel}', area '{fightScene}')."); if ((int)fightHere == 0 && (int)val2 != 0 && levelShown) { CoopRunner.Toast("The fight is over."); } else if ((int)fightHere == 1 && (int)val2 == 0) { CoopRunner.Toast((PlayerSync.Host?.Label ?? "The host") + " is getting ready for a fight."); } if (CoopSession.ClientInWorld) { UpdateStance(); } } break; case 2: if (CoopSession.Role == CoopRole.Host) { string uid = CoopMessages.ReadString(ref reader); int damage2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref damage2, default(ForPrimitives)); if (!knockedOutPartners.Contains(sender)) { HostApplyPartnerDamage(sender, uid, damage2); } } break; case 3: if (CoopSession.Role == CoopRole.Client) { int damage = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref damage, default(ForPrimitives)); OnPlayerHit(damage); } break; case 4: if (CoopSession.Role == CoopRole.Host) { knockedOutPartners.Add(sender); if (registered.TryGetValue(sender, out var value)) { Unregister(sender, value); } CoopRunner.Toast((PlayerSync.Get(sender)?.Label ?? "A partner") + " is knocked out."); CoopLog.Info((PlayerSync.Get(sender)?.Label ?? sender.ToString()) + " is knocked out."); } break; case 6: if (CoopSession.Role == CoopRole.Client) { float progress = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref progress, default(ForPrimitives)); float total = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref total, default(ForPrimitives)); hostWavesProgress = progress; hostWavesTotal = total; if (timelineShown) { SetShownWaves(progress, total); } } break; case 5: { if (CoopSession.Role != CoopRole.Client) { break; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); int hp = default(int); int max = default(int); for (int i = 0; i < num; i++) { string text = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref hp, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref max, default(ForPrimitives)); WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text)) : null); if (val != null) { ApplyHostHp(val, hp, max); } } break; } } } public static string Describe() { //IL_0081: 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) if (CoopSession.Role != CoopRole.Host) { return $"fight={fightHere} level='{fightLevel}' shown={levelShown} armed={armed} knockedOut={knockedOut} marked={markedFighters.Count} applied={Applied}"; } return $"fight={hostState} level='{hostLevel}' targets={registered.Count} hpTracked={sentHp.Count} sent={Sent} applied={Applied}"; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class PartnerFightBars { private static void Postfix(PlayerView __instance, ref List __result) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown try { if (!CombatSync.InHostFight || __result == null) { return; } PlayerController playerController = MainGame.PlayerController; if (!((Object)(object)__instance != (Object)(object)((playerController != null) ? playerController.View : null))) { HPComponent val = MainGame.PlayerData?.hpComponent; if (val != null && !__result.Any((LazyWidgetDataBase w) => w is HpBarPlayerWidgetData)) { __result.Add((LazyWidgetDataBase)new HpBarPlayerWidgetData(val, -1f, -1f)); } if (!__result.Any((LazyWidgetDataBase w) => w is StaminaBarPlayerWidgetData)) { __result.Add((LazyWidgetDataBase)new StaminaBarPlayerWidgetData()); } } } catch (Exception ex) { CoopLog.Debug("Showing our fight bars failed: " + ex.Message); } } } [HarmonyPatch(typeof(FightingGameController), "SetFightState")] internal static class MirrorFightState { private static void Postfix(FightingGameController __instance, FightState newState) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) try { CombatSync.OnHostFightState(__instance, newState); } catch (Exception e) { CoopLog.Error("Mirroring the fight state failed", e); } } } [HarmonyPatch(typeof(FightingGameController), "Stop")] internal static class PartnersLeaveFightTargets { private static void Prefix() { CombatSync.UnregisterPartners("fight stopping"); } } [HarmonyPatch(typeof(HPComponent), "ApplyDamage")] internal static class RelayPartnerDamage { private static void Prefix(HPComponent __instance, out int __state) { __state = __instance.Hp; } private static void Postfix(HPComponent __instance, int __state) { CombatSync.OnHpDamaged(__instance, __state); } } [HarmonyPatch(typeof(AttackComponent), "HandleHit")] internal static class PartnerHitsGoToHost { private static bool Prefix(AttackComponent __instance, ICombatEntity target, AttackContext attackContext) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) try { return CombatSync.OnLocalAttackHit(__instance, target, attackContext); } catch (Exception e) { CoopLog.Error("Forwarding our hit failed", e); return true; } } } internal static class ContainerSync { private enum Slot : byte { Inventory, CraftInventory } private const byte OpContents = 1; private const float PollInterval = 1f; private static readonly Dictionary signatures = new Dictionary(); private static readonly Dictionary sentSceneTo = new Dictionary(); private static float nextPoll; private static readonly Dictionary> owners = new Dictionary>(); private static readonly HashSet announced = new HashSet(); public static int Sent; public static int Applied; private const float ClientReach = 4f; public static void Reset() { signatures.Clear(); sentSceneTo.Clear(); owners.Clear(); announced.Clear(); nextPoll = 0f; Sent = 0; Applied = 0; } public static void OnInventoryEvent(Inventory inventory) { if (inventory != null && owners.ContainsKey(inventory)) { announced.Add(inventory); } } private static void SendAnnounced() { //IL_0032: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_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) if (announced.Count == 0) { return; } bool flag = CoopSession.Role == CoopRole.Client; Vector3 val; if (flag) { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)((playerController != null) ? playerController.PhysicalBody : null) != (Object)null) { val = ((Component)MainGame.PlayerController.PhysicalBody).transform.position; goto IL_004d; } } val = Vector3.zero; goto IL_004d; IL_004d: Vector3 val2 = val; foreach (Inventory item in announced.ToList()) { if (!owners.TryGetValue(item, out var value)) { continue; } WgoData key = value.Key; if (((key != null) ? key.UniqueId : null) == (SGuid)null) { continue; } WgoData key2 = value.Key; if (flag) { if (ConveyorSync.IsBelt(key2)) { continue; } Vector3 val3 = key2.Position - val2; if (((Vector3)(ref val3)).sqrMagnitude > 16f) { continue; } } List list = Items(key2, value.Value); if (list != null) { string key3 = Key(key2, value.Value); int num = Signature(list); int value2; bool num2 = signatures.TryGetValue(key3, out value2); signatures[key3] = num; if (num2 && value2 != num) { Send(key2, value.Value, list, null); } } } announced.Clear(); } private static void Watch(WgoData data) { if (data.Inventory != null && !owners.ContainsKey(data.Inventory)) { owners[data.Inventory] = new KeyValuePair(data, Slot.Inventory); } if (data.CraftInventory != null && !owners.ContainsKey(data.CraftInventory)) { owners[data.CraftInventory] = new KeyValuePair(data, Slot.CraftInventory); } } public static void Tick() { bool flag = CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && CoopSession.IsInGame; bool flag2 = CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld; if (!flag && !flag2) { announced.Clear(); return; } SendAnnounced(); if (Time.unscaledTime < nextPoll) { return; } if (owners.Count > 20000) { owners.Clear(); } nextPoll = Time.unscaledTime + 1f; if (flag) { foreach (ulong item in CoopSession.ReadyClients.ToList()) { RemotePlayer remotePlayer = PlayerSync.Get(item); if (remotePlayer != null && remotePlayer.HasState && !string.IsNullOrEmpty(remotePlayer.Scene) && (!sentSceneTo.TryGetValue(item, out var value) || value != remotePlayer.Scene)) { sentSceneTo[item] = remotePlayer.Scene; int num = SendScene(remotePlayer.Scene, item); CoopLog.Debug($"Sent {num} containers in '{remotePlayer.Scene}' to {remotePlayer.Label}."); } } { foreach (string item2 in new HashSet(from c in CoopSession.ReadyClients select PlayerSync.Get(c)?.Scene into s where !string.IsNullOrEmpty(s) select s)) { SendChanged(item2, null); } return; } } PlayerController playerController = MainGame.PlayerController; string text = ((playerController == null) ? null : playerController.PlayerData?.currentGameSceneId); if (!string.IsNullOrEmpty(text)) { SendChanged(text, null); } } private static IEnumerable Containers(string sceneId) { WorldData worldData = MainGame.WorldData; GameSceneData val = ((worldData != null) ? worldData.GetGameSceneDataById(sceneId) : null); if (val == null) { yield break; } foreach (WgoData item in val.wgoDataList.ToList()) { if (((item != null) ? item.UniqueId : null) != (SGuid)null && ((ObjectLinkedToDefinition)(object)item).Definition != null && !((ObjectLinkedToDefinition)(object)item).Definition.hasRefToOtherWgoInventory) { yield return item; } } } private static List Items(WgoData data, Slot slot) { Inventory obj = ((slot == Slot.Inventory) ? data.Inventory : data.CraftInventory); if (obj == null) { return null; } Item data2 = obj.Data; if (data2 == null) { return null; } return data2.Inventory; } private static int Signature(List items, int depth = 0) { if (items == null) { return 0; } int num = 17 + items.Count; foreach (Item item in items) { if (item == null) { num *= 31; continue; } num = num * 31 + (((ObjectLinkedToDefinition)(object)item).id?.GetHashCode() ?? 0); num = num * 31 + item.Count; if (depth < 2 && item.Inventory != null && item.Inventory.Count > 0) { num = num * 31 + Signature(item.Inventory, depth + 1); } } return num; } private static string Key(WgoData data, Slot slot) { return data.UniqueId.Id + ((slot == Slot.Inventory) ? "" : "#craft"); } private static void SendChanged(string sceneId, ulong? onlyTo, bool ignoreReach = false) { //IL_0025: 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_003b: 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_0079: 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_007f: Unknown result type (might be due to invalid IL or missing references) bool flag = CoopSession.Role == CoopRole.Client; Vector3 val; if (flag) { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)((playerController != null) ? playerController.PhysicalBody : null) != (Object)null) { val = ((Component)MainGame.PlayerController.PhysicalBody).transform.position; goto IL_0040; } } val = Vector3.zero; goto IL_0040; IL_0040: Vector3 val2 = val; foreach (WgoData item in Containers(sceneId)) { Watch(item); if (flag && ConveyorSync.IsBelt(item)) { continue; } int num; if (!ignoreReach && flag) { Vector3 val3 = item.Position - val2; num = ((((Vector3)(ref val3)).sqrMagnitude <= 16f) ? 1 : 0); } else { num = 1; } bool flag2 = (byte)num != 0; Slot[] array = new Slot[2] { Slot.Inventory, Slot.CraftInventory }; foreach (Slot slot in array) { List list = Items(item, slot); if (list != null) { string key = Key(item, slot); int num2 = Signature(list); int value; bool num3 = signatures.TryGetValue(key, out value); signatures[key] = num2; if (num3 && value != num2 && flag2) { Send(item, slot, list, onlyTo); } } } } } public static void SendChangedEverywhere() { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { PlayerController playerController = MainGame.PlayerController; string text = ((playerController == null) ? null : playerController.PlayerData?.currentGameSceneId); if (!string.IsNullOrEmpty(text)) { SendChanged(text, null, ignoreReach: true); } } } private static int SendScene(string sceneId, ulong clientId) { int num = 0; foreach (WgoData item in Containers(sceneId)) { Watch(item); Slot[] array = new Slot[2] { Slot.Inventory, Slot.CraftInventory }; foreach (Slot slot in array) { List list = Items(item, slot); if (list != null && list.Count != 0) { signatures[Key(item, slot)] = Signature(list); Send(item, slot, list, clientId); num++; } } } return num; } private unsafe static void Send(WgoData data, Slot slot, List items, ulong? onlyTo, ulong? except = null) { //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_0032: 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_0043: 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_0087: 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_00a5: Unknown result type (might be due to invalid IL or missing references) byte[] bytes; try { bytes = SerializationUtility.SerializeValue>(items, (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Serializing the contents of " + ((ObjectLinkedToDefinition)(object)data).id + " failed", e); return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, data.UniqueId.Id); b = (byte)slot; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, bytes); if (CoopSession.Role == CoopRole.Client) { CoopMessages.SendToHost("GK2Coop.Containers", writer, (NetworkDelivery)3); } else if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Containers", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Containers", writer, (NetworkDelivery)4, except); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Debug($"Sent contents of {((ObjectLinkedToDefinition)(object)data).id} ({slot}, {items.Count} stacks)."); } public static void OnContainers(ulong sender, FastBufferReader reader) { //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_0026: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (b != 1) { return; } string text = CoopMessages.ReadString(ref reader); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); byte[] array = CoopMessages.ReadBytes(ref reader); Slot slot = (Slot)b2; if (CoopSession.Role == CoopRole.Client && !CoopSession.ClientInWorld) { return; } WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text)) : null); Inventory val2 = ((val == null) ? null : ((slot == Slot.Inventory) ? val.Inventory : val.CraftInventory)); if (((val2 != null) ? val2.Data : null) == null) { CoopLog.Debug($"Container {text} ({slot}) isn't in this world."); return; } try { List list = SerializationUtility.DeserializeValue>(array, (DataFormat)0, (DeserializationContext)null) ?? new List(); Replace(val2, list, ((ObjectLinkedToDefinition)(object)val).id); signatures[Key(val, slot)] = Signature(Items(val, slot)); Applied++; CoopLog.Debug(string.Format("Updated {0} ({1}) from {2}: {3} stacks.", ((ObjectLinkedToDefinition)(object)val).id, slot, (CoopSession.Role == CoopRole.Host) ? $"client {sender}" : "host", list.Count)); if (CoopSession.Role == CoopRole.Host) { Send(val, slot, Items(val, slot), null, sender); } } catch (Exception e) { CoopLog.Error("Applying the contents of " + ((ObjectLinkedToDefinition)(object)val).id + " failed", e); } } public static List TakeChangedInventory(WgoData data) { List list = ((((data != null) ? data.UniqueId : null) != (SGuid)null) ? Items(data, Slot.Inventory) : null); if (list == null) { return null; } string key = Key(data, Slot.Inventory); int num = Signature(list); int value; bool num2 = signatures.TryGetValue(key, out value); signatures[key] = num; if (!num2 || value != num) { return list; } return null; } public static void ApplyInventory(WgoData data, List items) { Inventory val = ((data != null) ? data.Inventory : null); if (((val != null) ? val.Data : null) != null) { Replace(val, items, ((ObjectLinkedToDefinition)(object)data).id); signatures[Key(data, Slot.Inventory)] = Signature(Items(data, Slot.Inventory)); } } private static void Replace(Inventory inventory, List items, string what) { List list = inventory.Data.RemoveAllItems() ?? new List(); if (items != null && items.Count > 0) { inventory.Data.AddItemsToInventory(items); } bool num = CoopSession.Role == CoopRole.Client; bool applyingRemote = CraftSync.ApplyingRemote; if (num) { CraftSync.ApplyingRemote = true; } try { if (list.Count > 0) { inventory.NotifyItemsRemoved(list); } inventory.NotifyItemsAdded(items ?? new List()); } catch (Exception ex) { CoopLog.Debug("Redrawing " + what + "'s contents failed: " + ex.Message); } finally { CraftSync.ApplyingRemote = applyingRemote; } } public static string Describe() { return $"tracked={signatures.Count} sent={Sent} applied={Applied}"; } } internal static class ConveyorSync { private struct Moving { public bool Has; public int Direction; public string ItemId; public bool IsCommon; } private const byte OpStep = 1; private const byte OpDump = 2; private static readonly FieldInfo UpdatedEventField = AccessTools.Field(typeof(ConveyorSystem), "OnUpdated"); public static bool ApplyingRemote; public static int StepsSent; public static int StepsApplied; public static int Dumps; public static void Reset() { ApplyingRemote = false; StepsSent = 0; StepsApplied = 0; Dumps = 0; } public static void Tick() { } public static void OnClientReady(ulong clientId) { } public static bool IsBelt(WgoData data) { WgoData obj = ((data is ConveyorWgoData) ? data : null); ConveyorComponent val = ((obj != null) ? ((ConveyorWgoData)obj).ConveyorComponent : null); if (!(val is ConveyorCellComponent) && !(val is ConveyorCellUndergroundComponent) && !(val is ConveyorCellStationComponent)) { return val is ConveyorSplitterComponent; } return true; } private static Moving Read(ConveyorMovableItemData item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown if (item != null) { return new Moving { Has = true, Direction = (int)item.direction, ItemId = (item.itemId ?? ""), IsCommon = item.isCommon }; } return default(Moving); } public unsafe static void OnHostStep() { //IL_01ac: 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_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00a9: 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || !CoopSession.IsInGame || !Plugin.HostRunsWorld.Value) { return; } List list = MainGame.Instance.GameSave?.conveyorSystemData?.conveyorComponents; if (list == null || list.Count == 0) { return; } HashSet scenes = NpcSync.ScenesWithPartners(); List list2 = list.Where(delegate(ConveyorComponent c) { object obj; if (c == null) { obj = null; } else { ConveyorWgoData wgoData2 = c.WgoData; obj = ((wgoData2 != null) ? ((WgoData)wgoData2).UniqueId : null); } return (SGuid)obj != (SGuid)null && scenes.Contains(((WgoData)c.WgoData).WorldId); }).ToList(); if (list2.Count == 0) { return; } for (int num = 0; num < list2.Count; num += 60) { List list3 = list2.Skip(num).Take(60).ToList(); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = ((num + 60 >= list2.Count) ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list3.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (ConveyorComponent item in list3) { ConveyorWgoData wgoData = item.WgoData; CoopMessages.WriteString(ref writer, ((WgoData)wgoData).UniqueId.Id); WriteMoving(ref writer, Read(item.InItem)); WriteMoving(ref writer, Read(item.OutItem)); byte[] array = null; List list4 = ContainerSync.TakeChangedInventory((WgoData)(object)wgoData); if (list4 != null) { try { array = SerializationUtility.SerializeValue>(list4, (DataFormat)0, (SerializationContext)null); } catch (Exception ex) { CoopLog.Warn("Couldn't send what " + ((ObjectLinkedToDefinition)(object)wgoData).id + " holds: " + ex.Message); } } b = ((array != null) ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); if (array != null) { CoopMessages.WriteBytes(ref writer, array); } } CoopMessages.SendToReadyClients("GK2Coop.Conveyors", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } StepsSent++; } private static void WriteMoving(ref FastBufferWriter w, Moving moving) { //IL_0010: 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_002e: 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_0056: 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) byte b = (moving.Has ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); if (moving.Has) { ((FastBufferWriter)(ref w)).WriteValueSafe(ref moving.Direction, default(ForPrimitives)); CoopMessages.WriteString(ref w, moving.ItemId); b = (moving.IsCommon ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); } } private static ConveyorMovableItemData ReadMoving(ref FastBufferReader reader) { //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_001b: 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_0032: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (b == 0) { return null; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); string text = CoopMessages.ReadString(ref reader); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); return new ConveyorMovableItemData(text, (Direction)num, b2 != 0); } private static void ApplyStep(ref FastBufferReader reader) { //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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); byte b2 = default(byte); for (int i = 0; i < num; i++) { string text = CoopMessages.ReadString(ref reader); ConveyorMovableItemData inItem = ReadMoving(ref reader); ConveyorMovableItemData outItem = ReadMoving(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); byte[] array = ((b2 != 0) ? CoopMessages.ReadBytes(ref reader) : null); WorldData worldData = MainGame.WorldData; WgoData obj = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text)) : null); ConveyorWgoData val = (ConveyorWgoData)(object)((obj is ConveyorWgoData) ? obj : null); ConveyorComponent val2 = ((val != null) ? val.ConveyorComponent : null); if (val2 == null) { continue; } val2.InItem = inItem; val2.OutItem = outItem; if (array != null) { try { ContainerSync.ApplyInventory((WgoData)(object)val, SerializationUtility.DeserializeValue>(array, (DataFormat)0, (DeserializationContext)null)); } catch (Exception ex) { CoopLog.Warn("Couldn't apply what " + ((ObjectLinkedToDefinition)(object)val).id + " holds: " + ex.Message); } } } if (b == 0) { return; } ApplyingRemote = true; try { ConveyorSystem conveyorSystem = MainGame.Instance.conveyorSystem; (UpdatedEventField?.GetValue(conveyorSystem) as Action)?.Invoke(); LazySingleton.Instance.SetState("Out"); ConveyorSoundSystem instance = LazySingleton.Instance; instance.PauseSounds(); instance.PlaySounds(); StepsApplied++; } catch (Exception e) { CoopLog.Error("Playing the conveyor step failed", e); } finally { ApplyingRemote = false; } } public static void OnLocalDump(Item item, RiverBodyReceiver receiver, Vector3 throwStartPos) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (ApplyingRemote || item == null || (Object)(object)receiver == (Object)null) { return; } Wgo wgo = receiver.Wgo; object obj; if (wgo == null) { obj = null; } else { WgoData data = wgo.Data; obj = ((data != null) ? data.UniqueId : null); } if (!((SGuid)obj == (SGuid)null)) { bool num = CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients; bool flag = CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld; if (num || flag) { SendDump(item, receiver.Wgo.Data.UniqueId.Id, throwStartPos, null); CoopLog.Info("Dumped " + ((ObjectLinkedToDefinition)(object)item).id + " in the river at " + ((ObjectLinkedToDefinition)(object)receiver.Wgo.Data).id + "; mirrored."); } } } private unsafe static void SendDump(Item item, string receiverUid, Vector3 throwStartPos, ulong? except) { //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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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) byte[] bytes; try { bytes = SerializationUtility.SerializeValue(item, (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Serializing the dumped " + ((ObjectLinkedToDefinition)(object)item).id + " failed", e); return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, receiverUid); CoopMessages.WriteVector3(ref writer, throwStartPos); CoopMessages.WriteBytes(ref writer, bytes); if (CoopSession.Role == CoopRole.Host) { CoopMessages.SendToReadyClients("GK2Coop.Conveyors", writer, (NetworkDelivery)4, except); } else { CoopMessages.SendToHost("GK2Coop.Conveyors", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void ApplyDump(ulong sender, ref FastBufferReader reader) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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) string text = CoopMessages.ReadString(ref reader); Vector3 val = CoopMessages.ReadVector3(ref reader); Item val2 = SerializationUtility.DeserializeValue(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); if (val2 == null) { return; } if (CoopSession.Role == CoopRole.Host) { MainGame.PlayerData.SubRes("cur_bodies_count", 1f); SendDump(val2, text, val, sender); CoopLog.Info((PlayerSync.Get(sender)?.Label ?? sender.ToString()) + " dumped " + ((ObjectLinkedToDefinition)(object)val2).id + " in the river."); } Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(text)); RiverBodyReceiver val3 = (((Object)(object)wgoViewGlobal != (Object)null) ? wgoViewGlobal.RiverBodyReceiver : null); if ((Object)(object)val3 == (Object)null) { return; } ApplyingRemote = true; try { MainGame.Instance.riverDropSystem.BeginDump(val2, val3, val); Dumps++; } finally { ApplyingRemote = false; } } public static void OnConveyors(ulong sender, FastBufferReader reader) { //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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role != CoopRole.Client || CoopSession.ClientInWorld) { if (b == 1 && CoopSession.Role == CoopRole.Client) { ApplyStep(ref reader); } else if (b == 2) { ApplyDump(sender, ref reader); } } } public static string Describe() { if (CoopSession.Role != CoopRole.Host) { return $"steps applied={StepsApplied} dumps={Dumps}"; } return $"steps sent={StepsSent} dumps={Dumps}"; } } [HarmonyPatch(typeof(ConveyorSystemAnimationOrchestrator), "SetState")] internal static class MirrorConveyorStep { private static void Postfix(string name) { if (name != "Out" || ConveyorSync.ApplyingRemote) { return; } try { ConveyorSync.OnHostStep(); } catch (Exception e) { CoopLog.Error("Sending the conveyor step failed", e); } } } [HarmonyPatch(typeof(RiverDropSystem), "BeginDump")] internal static class MirrorRiverDump { private static void Postfix(bool __result, Item item, RiverBodyReceiver receiver, Vector3 throwStartPos) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!__result) { return; } try { ConveyorSync.OnLocalDump(item, receiver, throwStartPos); } catch (Exception e) { CoopLog.Error("Mirroring the river dump failed", e); } } } internal static class CoopLog { [CompilerGenerated] private static class <>O { public static LogCallback <0>__OnUnityLog; } private const int KeepFiles = 12; private static StreamWriter writer; private static readonly Dictionary sent = new Dictionary(); private static readonly Dictionary received = new Dictionary(); private static readonly Dictionary repeatedErrors = new Dictionary(); public static string FilePath { get; private set; } public static bool Verbose { get { if (Plugin.VerboseLogging != null) { return Plugin.VerboseLogging.Value; } return true; } } public static void Init() { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown try { string text = Path.Combine(Paths.BepInExRootPath, "GK2Coop-logs"); Directory.CreateDirectory(text); foreach (FileInfo item in (from f in new DirectoryInfo(text).GetFiles("coop_*.log") orderby f.CreationTimeUtc descending select f).Skip(11)) { try { item.Delete(); } catch { } } FilePath = Path.Combine(text, $"coop_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_pid{Process.GetCurrentProcess().Id}.log"); writer = new StreamWriter(new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)) { AutoFlush = true }; writer.WriteLine(string.Format("GK2 Co-op {0} debug log. Game folder: {1} pid {2}", "0.27.0", Path.GetDirectoryName(Application.dataPath), Process.GetCurrentProcess().Id)); object obj2 = <>O.<0>__OnUnityLog; if (obj2 == null) { LogCallback val = OnUnityLog; <>O.<0>__OnUnityLog = val; obj2 = (object)val; } Application.logMessageReceived += (LogCallback)obj2; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Couldn't create the co-op debug log: " + ex.Message)); } } public static void Info(string message) { Write("INFO ", message); Plugin.Log.LogInfo((object)message); } public static void Warn(string message) { Write("WARN ", message); Plugin.Log.LogWarning((object)message); } public static void Error(string message, Exception e = null) { Write("ERROR", (e == null) ? message : $"{message}: {e}"); Plugin.Log.LogError((object)((e == null) ? message : $"{message}: {e}")); } public static void Debug(string message) { if (Verbose) { Write("DEBUG", message); } } public static void CountSent(string channel, int bytes) { Bump(sent, channel); } public static void CountReceived(string channel, int bytes) { Bump(received, channel); } public static string Counters() { return "sent[" + Format(sent) + "] recv[" + Format(received) + "]"; static string Format(Dictionary d) { if (d.Count != 0) { return string.Join(" ", from p in d orderby p.Key select $"{p.Key}={p.Value}"); } return "-"; } } public static void ResetCounters() { sent.Clear(); received.Clear(); } private static void Bump(Dictionary counts, string key) { counts.TryGetValue(key, out var value); counts[key] = value + 1; } private static void OnUnityLog(string condition, string stackTrace, LogType type) { //IL_0007: 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_000c: Invalid comparison between Unknown and I4 //IL_003d: 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: Invalid comparison between Unknown and I4 if (CoopSession.Role != CoopRole.None && ((int)type == 0 || (int)type == 4 || (int)type == 1)) { repeatedErrors.TryGetValue(condition, out var value); repeatedErrors[condition] = value + 1; if (value < 3) { Write("UNITY", $"{type}: {condition}\n{stackTrace}".TrimEnd()); } else if (value == 3) { Write("UNITY", "(further repeats of this error are counted, not logged) " + condition); } } } private static void Write(string level, string message) { if (writer == null) { return; } try { string text = ((CoopSession.Role == CoopRole.None) ? "----" : CoopSession.Role.ToString().ToUpperInvariant()); writer.WriteLine($"{DateTime.Now:HH:mm:ss.fff} f{Time.frameCount,-7} {text,-6} {level} {message}"); } catch { } } } internal static class CoopMessages { public const string PlayerState = "GK2Coop.State"; public const string PlayerInfo = "GK2Coop.Info"; public const string Time = "GK2Coop.Time"; public const string Story = "GK2Coop.Story"; public const string World = "GK2Coop.World"; public const string Request = "GK2Coop.Request"; public const string Npc = "GK2Coop.Npc"; public const string Drops = "GK2Coop.Drops"; public const string Economy = "GK2Coop.Economy"; public const string Knowledge = "GK2Coop.Knowledge"; public const string Containers = "GK2Coop.Containers"; public const string Sleep = "GK2Coop.Sleep"; public const string WorldState = "GK2Coop.WorldState"; public const string Vendors = "GK2Coop.Vendors"; public const string Effects = "GK2Coop.Effects"; public const string Crafts = "GK2Coop.Crafts"; public const string Locks = "GK2Coop.Locks"; public const string Combat = "GK2Coop.Combat"; public const string Conveyors = "GK2Coop.Conveyors"; public const string Progress = "GK2Coop.Progress"; public const string Zombies = "GK2Coop.Zombies"; public const string Director = "GK2Coop.Director"; public const string Tutorial = "GK2Coop.Tutorial"; public const string Travel = "GK2Coop.Travel"; public const string Troops = "GK2Coop.Troops"; public const string ItemScripts = "GK2Coop.ItemScripts"; public const string Sermons = "GK2Coop.Sermons"; private static readonly Dictionary> handlers = new Dictionary> { { "GK2Coop.State", PlayerSync.OnState }, { "GK2Coop.Info", PlayerSync.OnInfo }, { "GK2Coop.Time", TimeSync.OnTime }, { "GK2Coop.Story", StorySync.OnStory }, { "GK2Coop.World", WorldSync.OnWorld }, { "GK2Coop.Request", StorySync.OnRequest }, { "GK2Coop.Npc", NpcSync.OnNpc }, { "GK2Coop.Drops", DropSync.OnDrops }, { "GK2Coop.Economy", EconomySync.OnEconomy }, { "GK2Coop.Knowledge", KnowledgeSync.OnKnowledge }, { "GK2Coop.Containers", ContainerSync.OnContainers }, { "GK2Coop.Sleep", SleepSync.OnSleep }, { "GK2Coop.WorldState", WorldStateSync.OnWorldState }, { "GK2Coop.Vendors", VendorSync.OnVendors }, { "GK2Coop.Effects", EffectSync.OnEffects }, { "GK2Coop.Crafts", CraftSync.OnCrafts }, { "GK2Coop.Locks", ChestLocks.OnLocks }, { "GK2Coop.Combat", CombatSync.OnCombat }, { "GK2Coop.Conveyors", ConveyorSync.OnConveyors }, { "GK2Coop.Progress", ProgressSync.OnProgress }, { "GK2Coop.Zombies", InteractionProxy.OnZombies }, { "GK2Coop.Troops", TroopSync.OnTroops }, { "GK2Coop.ItemScripts", GK2Coop.ItemScripts.OnItemScripts }, { "GK2Coop.Sermons", SermonSync.OnSermons }, { "GK2Coop.Director", StandIn.OnDirector }, { "GK2Coop.Tutorial", TutorialSync.OnTutorial }, { "GK2Coop.Travel", SceneWait.OnTravel }, { "GK2Coop.Profile", PartnerProfiles.OnMessage } }; public static void Register() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; foreach (KeyValuePair> handler2 in handlers) { string name = handler2.Key; Action handler = handler2.Value; customMessagingManager.RegisterNamedMessageHandler(name, (HandleNamedMessageDelegate)delegate(ulong sender, FastBufferReader reader) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) CoopLog.CountReceived(Short(name), ((FastBufferReader)(ref reader)).Length); try { handler(sender, reader); } catch (Exception e) { CoopLog.Error($"Handling {name} from {sender} failed", e); } }); } } public static FastBufferWriter NewWriter() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) return new FastBufferWriter(256, (Allocator)2, 1048576); } public static void SendTo(string name, ulong clientId, FastBufferWriter writer, NetworkDelivery delivery = (NetworkDelivery)3) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_0021: 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_001e: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsListening) { if ((int)delivery == 2 || (int)delivery == 3) { delivery = (NetworkDelivery)4; } else if (((FastBufferWriter)(ref writer)).Length > 1100) { delivery = (NetworkDelivery)4; } singleton.CustomMessagingManager.SendNamedMessage(name, clientId, writer, delivery); CoopLog.CountSent(Short(name), ((FastBufferWriter)(ref writer)).Length); } } public static void SendToHost(string name, FastBufferWriter writer, NetworkDelivery delivery = (NetworkDelivery)3) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) SendTo(name, 0uL, writer, delivery); } public static void SendToReadyClients(string name, FastBufferWriter writer, NetworkDelivery delivery = (NetworkDelivery)3, ulong? except = null) { //IL_002f: 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) foreach (ulong item in CoopSession.ReadyClients.ToList()) { if (!except.HasValue || item != except.Value) { SendTo(name, item, writer, delivery); } } } public static void WriteVector3(ref FastBufferWriter writer, Vector3 v) { //IL_000a: 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_0020: 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_0036: 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) ((FastBufferWriter)(ref writer)).WriteValueSafe(ref v.x, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref v.y, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref v.z, default(ForPrimitives)); } public static Vector3 ReadVector3(ref FastBufferReader reader) { //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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0036: Unknown result type (might be due to invalid IL or missing references) float num = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); float num2 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); float num3 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); return new Vector3(num, num2, num3); } public static void WriteString(ref FastBufferWriter writer, string s) { ((FastBufferWriter)(ref writer)).WriteValueSafe(s ?? "", false); } public static string ReadString(ref FastBufferReader reader) { string result = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref result, false); return result; } public static void WriteBytes(ref FastBufferWriter writer, byte[] bytes) { //IL_0015: 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) bytes = bytes ?? Array.Empty(); int num = bytes.Length; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteBytesSafe(bytes, bytes.Length, 0); } public static byte[] ReadBytes(ref FastBufferReader reader) { //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) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); byte[] result = new byte[num]; if (num > 0) { ((FastBufferReader)(ref reader)).ReadBytesSafe(ref result, num, 0); } return result; } private static string Short(string name) { if (!name.StartsWith("GK2Coop.")) { return name; } return name.Substring(8); } } internal static class CoopNetwork { private static readonly FieldRef TransportField = AccessTools.FieldRefAccess("networkTransport"); private static readonly FieldRef IsCoopGameField = AccessTools.FieldRefAccess("isCoopGame"); private static GameObject root; private static UnityTransport transport; private static SteamTransport steamTransport; private static UNetworkManager gameNetworkManager; public static bool IsInitialized { get { if (LazyNetwork.IsInitialized) { return (Object)(object)gameNetworkManager != (Object)null; } return false; } } public static bool IsListening { get { if ((Object)(object)NetworkManager.Singleton != (Object)null) { return NetworkManager.Singleton.IsListening; } return false; } } public static ulong LocalClientId => NetworkManager.Singleton.LocalClientId; public static bool UsingSteam { get; private set; } public static string DisconnectReason { get { try { NetworkManager singleton = NetworkManager.Singleton; return (singleton != null) ? singleton.DisconnectReason : null; } catch { return null; } } } public static void EnsureInitialized() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0068: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown if (!IsInitialized) { if (LazyNetwork.IsInitialized) { throw new InvalidOperationException("The game's network layer was initialized by something else; the co-op mod can't take it over."); } root = new GameObject("GK2Coop.Network"); root.SetActive(false); Object.DontDestroyOnLoad((Object)(object)root); transport = root.AddComponent(); transport.DisconnectTimeoutMS = 60000; NetworkManager val = root.AddComponent(); val.NetworkConfig = new NetworkConfig { NetworkTransport = (NetworkTransport)(object)transport, EnableSceneManagement = false, ConnectionApproval = false }; if ((Object)(object)ConnectionNotificationManager.Singleton == (Object)null) { root.AddComponent(); } gameNetworkManager = root.AddComponent(); TransportField.Invoke(gameNetworkManager) = transport; root.SetActive(true); if ((Object)(object)NetworkManager.Singleton != (Object)(object)val) { val.SetSingleton(); } LazyNetwork instance = LazySingleton.Instance; if (!((Component)instance).gameObject.activeSelf) { ((Component)instance).gameObject.SetActive(true); } instance.Init((INetworkManager)(object)gameNetworkManager, (BaseNetworkMessageChannelManager)new UNetworkMessageChannelManager()); Plugin.Log.LogInfo((object)"Game network layer initialized (Netcode for GameObjects + Unity Transport)."); } } public static bool StartHost(ushort port, bool steam, out string error) { EnsureInitialized(); if (IsListening) { error = "Already connected."; return false; } if (!UseTransport(steam, out error)) { return false; } if (!LazyNetwork.NetworkManager.StartHostGame("0.0.0.0", port)) { error = (steam ? "Couldn't start hosting on Steam. Check the BepInEx log." : $"Couldn't start hosting on port {port}. Is another program using it? Check the BepInEx log."); ResetCoopFlag(); return false; } error = null; return true; } public static bool Connect(string address, ushort port, out string error) { EnsureInitialized(); if (IsListening) { error = "Already connected."; return false; } if (!TryResolveIPv4(address, out var ip)) { error = "Couldn't find an IPv4 address for \"" + address + "\"."; return false; } if (!UseTransport(steam: false, out error)) { return false; } if (!LazyNetwork.NetworkManager.ConnectToHost(ip, port)) { error = "Netcode refused to start the client. Check the BepInEx log."; ResetCoopFlag(); return false; } error = null; return true; } public static bool ConnectSteam(ulong hostSteamId, out string error) { EnsureInitialized(); if (IsListening) { error = "Already connected."; return false; } if (!UseTransport(steam: true, out error)) { return false; } steamTransport.HostSteamId = hostSteamId; if (!LazyNetwork.NetworkManager.ConnectToHost("127.0.0.1", (ushort)Plugin.Port.Value)) { error = "Couldn't start connecting over Steam. Check the BepInEx log."; ResetCoopFlag(); return false; } error = null; return true; } private static bool UseTransport(bool steam, out string error) { error = null; NetworkConfig networkConfig = NetworkManager.Singleton.NetworkConfig; if (!steam) { networkConfig.NetworkTransport = (NetworkTransport)(object)transport; UsingSteam = false; return true; } if (!SteamLobby.Available) { error = "Steam isn't running, or the game wasn't started through it. Start Steam (and the game from Steam or r2modman), or use an IP address."; return false; } if ((Object)(object)steamTransport == (Object)null) { steamTransport = root.AddComponent(); } networkConfig.NetworkTransport = (NetworkTransport)(object)steamTransport; UsingSteam = true; return true; } public static void Shutdown() { if (IsListening) { gameNetworkManager.DisconnectFromHost(); } ResetCoopFlag(); } public static void ResetCoopFlag() { if (!((Object)(object)gameNetworkManager == (Object)null)) { IsCoopGameField.Invoke(gameNetworkManager) = false; gameNetworkManager.OtherClients.Clear(); } } public static bool SuspendCoopFlag() { if ((Object)(object)gameNetworkManager == (Object)null || !IsCoopGameField.Invoke(gameNetworkManager)) { return false; } IsCoopGameField.Invoke(gameNetworkManager) = false; return true; } public static void RestoreCoopFlag() { if ((Object)(object)gameNetworkManager != (Object)null && IsListening) { IsCoopGameField.Invoke(gameNetworkManager) = true; } } public static List GetLocalAddresses() { 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) { list.Add($"{unicastAddress.Address} ({networkInterface.Name})"); } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Couldn't list network interfaces: " + ex.Message)); } if (list.Count == 0) { try { string localIpAddress = LobbyHelper.GetLocalIpAddress(); if (!string.IsNullOrEmpty(localIpAddress)) { list.Add(localIpAddress); } } catch { } } return list; } private static bool TryResolveIPv4(string address, out string ip) { ip = null; address = (address ?? "").Trim(); if (address.Length == 0) { return false; } if (IPAddress.TryParse(address, out IPAddress address2) && address2.AddressFamily == AddressFamily.InterNetwork) { ip = address2.ToString(); return true; } try { ip = Dns.GetHostAddresses(address).FirstOrDefault((IPAddress a) => a.AddressFamily == AddressFamily.InterNetwork)?.ToString(); return ip != null; } catch { return false; } } } public enum ConnectVia { Steam, IP } internal class CoopRunner : MonoBehaviour { private const int WindowId = 7025153; private static readonly List<(string text, float until)> toasts = new List<(string, float)>(); private Rect windowRect = new Rect(24f, 24f, 420f, 10f); private bool visible; private string addressText; private string portText; private List localAddresses = new List(); private string initError; private static GUIStyle nameTagStyle; private static GUIStyle wrapStyle; private static GUIStyle toastStyle; private static readonly FieldInfo BubbleInstanceField = AccessTools.Field(typeof(Bubble), "instance"); private static readonly FieldRef> BubblePresetsField = AccessTools.FieldRefAccess>("speechBubblePresets"); private static string lastThought; private static float lastThoughtAt = -100f; public static CoopRunner Instance { get; private set; } public static void Toast(string text, float seconds = 5f) { if (!string.IsNullOrEmpty(text) && !GameNotice(text)) { toasts.Add((text, Time.unscaledTime + seconds)); if (toasts.Count > 4) { toasts.RemoveAt(0); } } } private static bool GameNotice(string text) { try { MainGame instance = MainGame.Instance; if (((instance != null) ? instance.GameSave : null) == null || (Object)(object)MainGame.PlayerController == (Object)null) { return false; } UINotificator instance2 = LazySingleton.Instance; if ((Object)(object)instance2 == (Object)null || !((Component)instance2).gameObject.activeInHierarchy) { return false; } foreach (UIBaseNotification displayingItem in instance2.DisplayingItems) { UISimpleTextNotification val = (UISimpleTextNotification)(object)((displayingItem is UISimpleTextNotification) ? displayingItem : null); if (val != null && val.Text == text) { return true; } } instance2.ShowSimpleTextNotification(text); return true; } catch (Exception ex) { CoopLog.Debug("The game's notification feed couldn't show '" + text + "': " + ex.Message); return false; } } public static void Think(string text) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(text) || (text == lastThought && Time.unscaledTime - lastThoughtAt < 2.5f)) { return; } lastThought = text; lastThoughtAt = Time.unscaledTime; try { object? obj = BubbleInstanceField?.GetValue(null); Bubble val = (Bubble)((obj is Bubble) ? obj : null); List list = (((Object)(object)val != (Object)null) ? BubblePresetsField.Invoke(val) : null); if ((Object)(object)MainGame.PlayerController == (Object)null || list == null || list.Count < 4) { Toast(text); } else { UIDialogBubble.ShowMessage(new PhraseData(true, (WgoData)null, text, (Action)delegate { }, list[3], (SpeechBubbleType)0, (ForceCornerPosition)0, 0f, false)); } } catch (Exception ex) { CoopLog.Debug("Thinking '" + text + "' failed: " + ex.Message); Toast(text); } } private void Awake() { Instance = this; addressText = Plugin.LastHostAddress.Value; portText = Plugin.Port.Value.ToString(); } private void Update() { try { CoopSession.Tick(); } catch (Exception e) { CoopSession.NoteError(e); } if (visible || StorySync.WantsCursor) { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } } private void OnGUI() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0082: 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_00a2: Expected O, but got Unknown //IL_009d: 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) EnsureStyles(); Event current = Event.current; if ((int)current.type == 4 && (int)current.keyCode == 289) { visible = !visible; if (visible) { OnOpened(); } current.Use(); } try { PlayerSync.DrawNameTags(nameTagStyle); StorySync.DrawPanel(wrapStyle); StorySync.DrawGather(wrapStyle); DrawToasts(); } catch (Exception e) { CoopSession.NoteError(e); } if (visible) { windowRect = GUILayout.Window(7025153, windowRect, new WindowFunction(DrawWindow), "GK2 Co-op prototype 0.27.0 (F8 to hide)", Array.Empty()); } } private void DrawToasts() { //IL_007e: 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) toasts.RemoveAll(((string text, float until) t) => Time.unscaledTime > t.until); if (toasts.Count == 0) { return; } float num = Mathf.Min(640f, (float)Screen.width - 40f); float num2 = 40f; foreach (var toast in toasts) { Rect val = new Rect(((float)Screen.width - num) / 2f, num2, num, 30f); GUI.Box(val, GUIContent.none); GUI.Label(val, toast.text, toastStyle); num2 += 34f; } } public static void ShowWindow() { if ((Object)(object)Instance != (Object)null && !Instance.visible) { Instance.visible = true; Instance.OnOpened(); } } private void OnOpened() { localAddresses = CoopNetwork.GetLocalAddresses(); if (string.IsNullOrWhiteSpace(Plugin.PlayerName.Value)) { string text = SteamLobby.LocalName?.Trim(); if (!string.IsNullOrEmpty(text)) { Plugin.PlayerName.Value = ((text.Length > 24) ? text.Substring(0, 24) : text); } } try { CoopNetwork.EnsureInitialized(); initError = null; } catch (Exception ex) { initError = ex.Message; CoopLog.Error("Network setup failed", ex); } } private void DrawWindow(int id) { //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) if (initError != null) { GUILayout.Label("Network setup failed: " + initError, wrapStyle, Array.Empty()); } GUILayout.Label(CoopSession.Status, wrapStyle, Array.Empty()); switch (CoopSession.Role) { case CoopRole.None: DrawOffline(); break; case CoopRole.Host: DrawHost(); break; case CoopRole.Client: DrawClient(); break; } if (StorySync.GatherActive && GUILayout.Button("Cancel the talk request", Array.Empty())) { StorySync.CancelGather(); } if (CoopSession.Role != CoopRole.None && CoopSession.IsInGame && GUILayout.Button("Change my look", Array.Empty())) { try { visible = false; PlayerSync.OpenCustomization(); } catch (Exception ex) { CoopLog.Error("Opening the customization window failed", ex); Toast("Couldn't open the customization window here: " + ex.Message); } } if (CoopSession.Role != CoopRole.None) { foreach (RemotePlayer remote in PlayerSync.Remotes) { object obj; if (remote.HasState) { if (!remote.Visible) { obj = "in another area"; } else { Vector3 position = remote.Position; PlayerController playerController = MainGame.PlayerController; Vector3? obj2; if (playerController == null) { obj2 = null; } else { PlayerPhysicalBody physicalBody = playerController.PhysicalBody; obj2 = ((physicalBody != null) ? new Vector3?(((Component)physicalBody).transform.position) : ((Vector3?)null)); } obj = $"{Vector3.Distance(position, (Vector3)(((??)obj2) ?? remote.Position)):0} m away"; } } else { obj = "no position yet"; } string text = (string)obj; GUILayout.Label(remote.Label + ": " + text, Array.Empty()); } GUILayout.Label("Messages: " + CoopLog.Counters(), wrapStyle, Array.Empty()); } if (!string.IsNullOrEmpty(CoopSession.LastError)) { GUILayout.Label("Last error: " + CoopSession.LastError, wrapStyle, Array.Empty()); } GUILayout.Space(6f); GUILayout.Label("Synced: players (movement, outfits, carried items, work animations), villagers and zombies, time, weather, sleep, money and tech, objects and their damage, items on the ground, chests, crafting, conveyors, shops, fights, cutscenes and dialogue. Each player keeps their own inventory.", wrapStyle, Array.Empty()); if (!string.IsNullOrEmpty(CoopLog.FilePath)) { GUILayout.Label("Debug log: " + CoopLog.FilePath, wrapStyle, Array.Empty()); } GUI.DragWindow(); } private void DrawOffline() { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Your name", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) }); string text = GUILayout.TextField(Plugin.PlayerName.Value ?? "", 24, Array.Empty()); if (text != Plugin.PlayerName.Value) { Plugin.PlayerName.Value = text; } GUILayout.EndHorizontal(); GUILayout.Label("Shown over your character. When you join, the host saves your character (inventory, money, energy) under this name, so use the same one each time.", wrapStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Connect", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) }); ConnectVia connectVia = ((GUILayout.Toolbar((Plugin.Connection.Value != ConnectVia.Steam) ? 1 : 0, new string[2] { "Steam friends", "IP address" }, Array.Empty()) != 0) ? ConnectVia.IP : ConnectVia.Steam); if (connectVia != Plugin.Connection.Value) { Plugin.Connection.Value = connectVia; } GUILayout.EndHorizontal(); if (connectVia == ConnectVia.Steam) { if (SteamLobby.Available) { DrawSteamOffline(); } else { GUILayout.Label("Steam isn't running here, or the game wasn't started through it, so Steam hosting is off. Start the game with Steam running, or use an IP address.", wrapStyle, Array.Empty()); } return; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Port", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); portText = GUILayout.TextField(portText ?? "", 5, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.EndHorizontal(); if (CoopSession.IsInGame) { if (GUILayout.Button("Host this world", Array.Empty()) && TryGetPort(out var port)) { CoopSession.Host(port); } GUILayout.Label("Your friend joins with one of these addresses:", Array.Empty()); foreach (string localAddress in localAddresses) { GUILayout.Label(" " + localAddress, Array.Empty()); } GUILayout.Label("Windows Firewall may ask to allow the game the first time you host.", wrapStyle, Array.Empty()); } else if (CoopSession.IsInMainMenu) { if (CoopSession.BlockSaves) { GUILayout.Label("Return to the main menu fully before joining again.", wrapStyle, Array.Empty()); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Host IP", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); addressText = GUILayout.TextField(addressText ?? "", 64, Array.Empty()); GUILayout.EndHorizontal(); if (GUILayout.Button("Join", Array.Empty()) && TryGetPort(out var port2)) { Plugin.LastHostAddress.Value = addressText.Trim(); CoopSession.Join(addressText, port2); } GUILayout.Space(6f); if (GUILayout.Button("Host from here (partners join first)", Array.Empty()) && TryGetPort(out var port3)) { CoopSession.Host(port3, fromMenu: true); } GUILayout.Label("Host from the menu, let your partners join, then start a new game (or load a save): they come in with you, and a new game's intro waits until everyone has loaded. Or load your save first and host from inside it.", wrapStyle, Array.Empty()); } else { GUILayout.Label("Load into your world to host, or go to the main menu to join.", wrapStyle, Array.Empty()); } } private void DrawSteamOffline() { ushort port = (ushort)Plugin.Port.Value; if (CoopSession.IsInGame) { if (GUILayout.Button("Host this world on Steam", Array.Empty())) { CoopSession.Host(port, fromMenu: false, steam: true); } GUILayout.Label("Your Steam friends then join from their co-op window, with Join Game on your Steam profile, or with an invite.", wrapStyle, Array.Empty()); return; } if (!CoopSession.IsInMainMenu) { GUILayout.Label("Load into your world to host, or go to the main menu to join.", wrapStyle, Array.Empty()); return; } if (CoopSession.BlockSaves) { GUILayout.Label("Return to the main menu fully before joining again.", wrapStyle, Array.Empty()); } GUI.enabled = !SteamLobby.Joining; if (SteamLobby.InviteLobby != 0L && GUILayout.Button("Join " + SteamLobby.InviteFrom + "'s world (their invite)", Array.Empty())) { SteamLobby.Join(SteamLobby.InviteLobby, SteamLobby.InviteFrom); } GUILayout.Label("Friends hosting:", Array.Empty()); List list = SteamLobby.FriendsHosting(); if (list.Count == 0) { GUILayout.Label(" No one right now. Steam friends show up here once they host.", wrapStyle, Array.Empty()); } foreach (SteamLobby.HostedWorld item in list) { if (!item.SameVersion) { GUILayout.Label(" " + item.Host + ": GK2 Co-op " + item.Version + " (you have 0.27.0). You both need the same version.", wrapStyle, Array.Empty()); } else if (GUILayout.Button("Join " + item.Host + "'s world", Array.Empty())) { SteamLobby.Join(item.Lobby, item.Host); } } GUILayout.Space(6f); if (GUILayout.Button("Host from here on Steam (friends join first)", Array.Empty())) { CoopSession.Host(port, fromMenu: true, steam: true); } GUI.enabled = true; GUILayout.Label("Host from the menu, let your friends join, then start a new game (or load a save): they come in with you, and a new game's intro waits until everyone has loaded. Or load your save first and host from inside it.", wrapStyle, Array.Empty()); } private void DrawHost() { if (CoopNetwork.UsingSteam) { GUILayout.Label("On Steam. " + SteamLobby.HostStatus, wrapStyle, Array.Empty()); if (SteamLobby.IsOpen && GUILayout.Button("Invite Steam friends", Array.Empty())) { SteamLobby.InviteFriends(); } } else { GUILayout.Label("Your addresses: " + string.Join(", ", localAddresses.Select((string a) => a.Split(' ')[0])), wrapStyle, Array.Empty()); } foreach (KeyValuePair item in WorldTransfer.SendProgress) { GUILayout.Label($"Sending world to player {item.Key}: {item.Value:P0}", Array.Empty()); } List list = CoopSession.ReadyClients.ToList(); GUILayout.Label((list.Count == 0) ? "No one is in your world yet." : ("In your world: " + string.Join(", ", list.Select((ulong c) => PlayerSync.Get(c)?.Label ?? ("Player " + c)))), Array.Empty()); List list2 = PartnerProfiles.LobbyClients.ToList(); if (list2.Count > 0) { GUILayout.Label("In your lobby: " + string.Join(", ", list2.Select((ulong c) => PartnerProfiles.NameOf(c) ?? ("Player " + c))) + (CoopSession.IsInGame ? "" : ". Start a new game or load a save; they'll come in with you."), wrapStyle, Array.Empty()); } if (Lobby.Holding) { GUILayout.Label("Your intro starts once everyone has loaded.", wrapStyle, Array.Empty()); } if (GUILayout.Button("Stop hosting", Array.Empty())) { CoopSession.Leave(); } } private void DrawClient() { if (WorldTransfer.IsReceiving) { GUILayout.Label($"Download: {WorldTransfer.ReceiveProgress:P0}", Array.Empty()); } if (StorySync.Watching) { GUILayout.Label("Watching the host's story scene. Your controls come back when it ends.", wrapStyle, Array.Empty()); } if (GUILayout.Button("Disconnect", Array.Empty())) { CoopSession.Leave(); } } private bool TryGetPort(out ushort port) { if (ushort.TryParse(portText, out port) && port > 0) { Plugin.Port.Value = port; return true; } CoopSession.Status = "Enter a port between 1 and 65535."; return false; } private static void EnsureStyles() { //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_0023: Expected O, but got Unknown //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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0056: 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_0062: 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_0076: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (wrapStyle == null) { wrapStyle = new GUIStyle(GUI.skin.label) { wordWrap = true }; toastStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, wordWrap = true, fontStyle = (FontStyle)1 }; nameTagStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontStyle = (FontStyle)1, fontSize = 14 }; nameTagStyle.normal.textColor = new Color(1f, 0.93f, 0.55f); } } } internal enum CoopRole { None, Host, Client } internal static class CoopSession { private const string ClientSlotName = "gk2coop_client"; private const float SnapshotInterval = 2f; private static readonly HashSet readyClients = new HashSet(); private static bool eventsHooked; private static bool clientLoadPending; private static float nextSnapshot; private static GameSave pendingWorld; private const float HelloTimeout = 15f; private static readonly Dictionary awaitingHello = new Dictionary(); public static string LastError; private static readonly Dictionary lastModuleError = new Dictionary(); public static CoopRole Role { get; private set; } public static string Status { get; set; } = "Not connected."; public static bool BlockSaves { get; private set; } public static bool ClientInWorld { get; private set; } public static bool ClientWaitingForWorld { get { if (Role == CoopRole.Client && !ClientInWorld && !clientLoadPending) { return pendingWorld == null; } return false; } } public static IEnumerable ReadyClients => readyClients; public static bool HasReadyClients => readyClients.Count > 0; public static bool IsInGame { get { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if ((Object)(object)MainGame.Instance != (Object)null) { return (int)MainGame.Instance.gameState == 1; } return false; } } public static bool IsInMainMenu { get { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if ((Object)(object)MainGame.Instance != (Object)null) { return (int)MainGame.Instance.gameState == 0; } return false; } } public static bool KeepWorldRunning { get { if (Role != CoopRole.Host || !HasReadyClients) { if (Role == CoopRole.Client) { return ClientInWorld; } return false; } return true; } } public static bool Host(ushort port, bool fromMenu = false, bool steam = false) { if (!IsInGame && (!fromMenu || !IsInMainMenu)) { Status = "Load into your world first, then host it."; return false; } if (PartnerProfiles.Key(Plugin.PlayerName.Value).Length == 0) { Status = "Enter your name first."; return false; } try { CoopNetwork.EnsureInitialized(); HookEvents(); if (!CoopNetwork.StartHost(port, steam, out var error)) { Status = error; CoopLog.Warn(error); return false; } Role = CoopRole.Host; ResetSessionState(); WorldTransfer.RegisterHandlers(); CoopMessages.Register(); if (steam) { SteamLobby.Open(); } string text = (steam ? "on Steam" : $"on port {port}"); Status = (fromMenu ? ("Hosting " + text + " from the menu. Partners can join now; start a new game (or load one) and they come in with you.") : ("Hosting " + text + ". Waiting for a player to join...")); CoopLog.Info(steam ? Status : (Status + " Addresses: " + string.Join(", ", CoopNetwork.GetLocalAddresses()))); return true; } catch (Exception e) { Fail("Hosting failed", e); return false; } } public static bool CanJoin(out string why) { if (!IsInMainMenu) { why = "Go to the main menu to join someone's world."; return false; } if (PartnerProfiles.Key(Plugin.PlayerName.Value).Length == 0) { why = "Enter your name first. The host saves your character under it."; return false; } why = null; return true; } public static bool Join(string address, ushort port) { string error; return BeginJoin($"{address}:{port}", () => (!CoopNetwork.Connect(address, port, out error)) ? error : null); } public static bool JoinSteam(ulong hostSteamId, string hostLabel) { string error; return BeginJoin(hostLabel + " over Steam", () => (!CoopNetwork.ConnectSteam(hostSteamId, out error)) ? error : null); } private static bool BeginJoin(string target, Func connect) { if (!CanJoin(out var why)) { Status = why; return false; } try { CoopNetwork.EnsureInitialized(); HookEvents(); Role = CoopRole.Client; ResetSessionState(); string text = connect(); if (text != null) { Role = CoopRole.None; Status = text; CoopLog.Warn(text); return false; } WorldTransfer.RegisterHandlers(); CoopMessages.Register(); Status = "Connecting to " + target + "..."; CoopLog.Info(Status); return true; } catch (Exception e) { Role = CoopRole.None; Fail("Joining failed", e); return false; } } public static void Leave(bool returnToMenu = true) { string text = ((Role == CoopRole.Host) ? "Stopped hosting." : "Disconnected."); CoopLog.Info("Leaving session (" + text + ")"); if (Role == CoopRole.Client && ClientInWorld) { PartnerProfiles.UploadCharacter(); } else if (Role == CoopRole.Host) { PartnerProfiles.SaveAll("stopped hosting"); } bool num = Role == CoopRole.Client && returnToMenu; CleanUp(); CoopNetwork.Shutdown(); SteamLobby.Close(); Role = CoopRole.None; Status = text; if (num) { ReturnToMenu(); } } private static void ReturnToMenu() { if (!IsInGame) { return; } try { CoopLog.Info("Unloading the host's world (returning to the main menu)."); MainGame.Instance.GoToMenu((Action)null, false, (FadeFlag)0); } catch (Exception e) { CoopLog.Error("Returning to the main menu failed", e); Status += " Return to the main menu yourself; saving stays off until you do."; } } public static void OnClientReady(ulong clientId) { if (Role == CoopRole.Host && IsInGame) { readyClients.Add(clientId); EnsureWorldRunning(); NpcSync.OnClientReady(); EconomySync.OnClientReady(clientId); WorldStateSync.OnClientReady(); VendorSync.OnClientReady(clientId); CraftSync.OnClientReady(); CombatSync.OnClientReady(clientId); ConveyorSync.OnClientReady(clientId); ProgressSync.OnClientReady(clientId); TutorialSync.OnClientReady(clientId); SleepSync.OnPlayersChanged(); PlayerSync.SendAllInfoTo(clientId); StorySync.OnClientReady(clientId); string text = PartnerProfiles.NameOf(clientId) ?? $"Player {clientId}"; Status = "Hosting. " + text + " is in your world."; CoopLog.Info(Status); CoopRunner.Toast(text + " joined your world."); } } public static void OnWorldReceived(GameSave gameSave) { //IL_0068: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (Role != CoopRole.Client) { return; } if (!PartnerProfiles.CharacterArrived) { CoopLog.Info("World arrived before our character; waiting for it."); pendingWorld = gameSave; return; } try { gameSave.hostPlayer = null; gameSave.clientPlayers?.Clear(); PartnerProfiles.ApplyToWorld(gameSave); StandIn.NoteJoinSpot(gameSave.playerData?.currentGameSceneId, gameSave.playerData?.position.Value ?? Vector3.zero, PartnerProfiles.StartedAtOwnSpot); BlockSaves = true; clientLoadPending = true; SaveSlotData val = new SaveSlotData { slotName = "gk2coop_client" }; EnvironmentData environmentData = gameSave.environmentData; CoopLog.Info($"Loading the host's world (day {((environmentData != null) ? new int?(environmentData.Day) : ((int?)null))}, player at {gameSave.playerData?.position.Value})."); StandIn.WantsBlackOut = !PartnerProfiles.StartedAtOwnSpot; MainGame.Instance.ContinueGame(val, gameSave); try { ((LazyWindow)(object)LazyUI.GetWindow())?.Close(); } catch (Exception ex) { CoopLog.Warn("Couldn't close the main menu: " + ex.Message); } Status = "Loading the host's world..."; } catch (Exception e) { Fail("Loading the host's world failed", e); Leave(); } } public static void Tick() { if (clientLoadPending && Role == CoopRole.Client && IsInGame && !IsLoadingOverlayShown()) { clientLoadPending = false; FinishClientLoad(); } Run("lobby (always)", Lobby.TickAlways); Run("steam", SteamLobby.Tick); if (Role != CoopRole.None) { if (pendingWorld != null && PartnerProfiles.CharacterArrived) { GameSave gameSave = pendingWorld; pendingWorld = null; OnWorldReceived(gameSave); } Run("hello timeouts", CheckHelloTimeouts); Run("players", PlayerSync.Tick); Run("time", TimeSync.Tick); Run("story", StorySync.Tick); Run("npcs", NpcSync.Tick); Run("economy", EconomySync.Tick); Run("profiles", PartnerProfiles.Tick); Run("containers", ContainerSync.Tick); Run("drops", DropSync.Tick); Run("sleep", SleepSync.Tick); Run("world state", WorldStateSync.Tick); Run("vendors", VendorSync.Tick); Run("crafts", CraftSync.Tick); Run("chest locks", ChestLocks.Tick); Run("object state", WorldSync.Tick); Run("combat", CombatSync.Tick); Run("conveyors", ConveyorSync.Tick); Run("progress", ProgressSync.Tick); Run("stand-in", StandIn.Tick); Run("scene wait", SceneWait.Tick); Run("effects", EffectSync.Tick); Run("troops", TroopSync.Tick); Run("tutorial", TutorialSync.Tick); Run("lobby", Lobby.Tick); if (Time.unscaledTime >= nextSnapshot) { nextSnapshot = Time.unscaledTime + 2f; LogSnapshot(); } } } private static void Run(string module, Action tick) { try { tick(); } catch (Exception ex) { string text = ex.GetType().Name + ": " + ex.Message; if (!lastModuleError.TryGetValue(module, out var value) || value != text) { lastModuleError[module] = text; LastError = module + ": " + text; CoopLog.Error("Co-op " + module + " update failed", ex); } } } public static void NoteError(Exception e) { string text = e.GetType().Name + ": " + e.Message; if (text != LastError) { LastError = text; CoopLog.Error("Unhandled co-op error", e); } } private static void FinishClientLoad() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) ClientInWorld = true; PlayerController playerController = MainGame.PlayerController; CoopLog.Info($"Finished loading the host's world. Local player at {((playerController == null) ? ((Vector3?)null) : playerController.PlayerData?.position.Value)} " + $"scene '{((playerController == null) ? null : playerController.PlayerData?.currentGameSceneId)}', controls enabled {((playerController != null) ? new bool?(playerController.IsControlsEnabled) : ((bool?)null))}."); WorldSync.FlushPending(); StandIn.AfterJoinLoad(); EnsureWorldRunning(); if (CoopNetwork.IsListening) { WorldTransfer.SendReady(); PlayerSync.SendLocalInfo(); Status = "In the host's world. Saving is off on this side."; } else { Status = "Loaded, but the host has disconnected."; } CoopLog.Info(Status); } private static void LogSnapshot() { //IL_004a: 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_006a: Unknown result type (might be due to invalid IL or missing references) if (!CoopLog.Verbose) { return; } try { PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); string text = ((val == null) ? "no local player" : ($"pos={(((Object)(object)playerController.PhysicalBody != (Object)null) ? ((Component)playerController.PhysicalBody).transform.position : val.position.Value)} " + $"scene='{val.currentGameSceneId}' anim={val.charState.Value} controls={playerController.IsControlsEnabled}")); CoopLog.Debug($"[snapshot] listening={CoopNetwork.IsListening} inGame={IsInGame} clientInWorld={ClientInWorld} " + "ready=[" + string.Join(",", readyClients) + "] local(" + text + ") remotes(" + PlayerSync.Describe() + ") " + $"time({TimeSync.Describe()}) story({StorySync.Describe()}) world(sent={WorldSync.Sent} applied={WorldSync.Applied} missing={WorldSync.Missing}) " + $"drops(sent={DropSync.Sent} applied={DropSync.Applied}) npcs({NpcSync.Describe()}) economy({EconomySync.Describe()}) tech({KnowledgeSync.Describe()}) containers({ContainerSync.Describe()}) sleep({SleepSync.Describe()}) vendors({VendorSync.Describe()}) effects({EffectSync.Describe()}) crafts({CraftSync.Describe()}) combat({CombatSync.Describe()}) conveyors({ConveyorSync.Describe()}) progress({ProgressSync.Describe()}) zombies({InteractionProxy.Describe()}) standIn({StandIn.Describe()}) sceneWait({SceneWait.Describe()}) tutorial({TutorialSync.Describe()}) lobby({Lobby.Describe()}) profiles({PartnerProfiles.Describe()}) " + "msgs(" + CoopLog.Counters() + ")"); } catch (Exception ex) { CoopLog.Debug("[snapshot] failed: " + ex.Message); } } private static void HookEvents() { if (eventsHooked) { return; } eventsHooked = true; INetworkManager networkManager = LazyNetwork.NetworkManager; networkManager.OnServerStopped += OnNetworkStopped; networkManager.OnClientStopped += OnNetworkStopped; networkManager.OnClientStarted += delegate { if (Role == CoopRole.Client) { Status = "Connected. Downloading the host's world..."; CoopLog.Info($"{Status} Our client id is {CoopNetwork.LocalClientId}."); PartnerProfiles.SendHello(); } }; networkManager.OnClientConnected += delegate(ulong id) { if (Role == CoopRole.Host) { awaitingHello[id] = Time.unscaledTime + 15f; Status = $"Player {id} connected. Waiting for their name..."; CoopLog.Info(Status); } }; networkManager.OnClientDisconnected += delegate(ulong id) { if (Role == CoopRole.Host) { string obj = PartnerProfiles.NameOf(id) ?? $"Player {id}"; awaitingHello.Remove(id); readyClients.Remove(id); PlayerSync.Remove(id); PlayerSync.BroadcastLeft(id); PartnerProfiles.SaveAll(obj + " left"); PartnerProfiles.ForgetClient(id); ChestLocks.OnPlayerLeft(id); SleepSync.OnPlayersChanged(); Status = obj + " left. Still hosting."; CoopLog.Info(Status); CoopRunner.Toast(obj + " left."); } }; MainGame.OnGoToMainMenu = (Action)Delegate.Combine(MainGame.OnGoToMainMenu, new Action(OnGoToMainMenu)); } private static void CheckHelloTimeouts() { if (Role != CoopRole.Host || awaitingHello.Count == 0) { return; } foreach (KeyValuePair item in awaitingHello.ToList()) { if (PartnerProfiles.NameOf(item.Key) != null) { awaitingHello.Remove(item.Key); } else if (Time.unscaledTime > item.Value) { awaitingHello.Remove(item.Key); CoopLog.Warn($"Client {item.Key} never sent its name; disconnecting it (different mod version?)."); NetworkManager singleton = NetworkManager.Singleton; if (singleton != null) { singleton.DisconnectClient(item.Key, "No hello received. Is the co-op mod the same version on both sides?"); } } } } private static void OnNetworkStopped() { if (Role != CoopRole.None) { string disconnectReason = CoopNetwork.DisconnectReason; bool num = Role == CoopRole.Client; CleanUp(); CoopNetwork.ResetCoopFlag(); SteamLobby.Close(); Role = CoopRole.None; if (num) { Status = ((ClientInWorld || clientLoadPending) ? "The host disconnected." : "Couldn't connect to the host.") + (string.IsNullOrEmpty(disconnectReason) ? "" : (" (" + disconnectReason + ")")); CoopRunner.Toast(Status); ReturnToMenu(); } else { Status = "Stopped hosting."; } CoopLog.Info("Network stopped: " + Status); } } private static void OnGoToMainMenu() { CoopLog.Info("Returning to the main menu."); if (Role != CoopRole.None) { Leave(returnToMenu: false); } BlockSaves = false; ClientInWorld = false; clientLoadPending = false; Status = "Not connected."; } private static void ResetSessionState() { readyClients.Clear(); ClientInWorld = false; clientLoadPending = false; LastError = null; CoopLog.ResetCounters(); PlayerSync.Clear(); TimeSync.Reset(); WorldSync.Reset(); StorySync.Reset(); NpcSync.Reset(); DropSync.Reset(); EconomySync.Reset(); KnowledgeSync.Reset(); ContainerSync.Reset(); SleepSync.Reset(); WorldStateSync.Reset(); VendorSync.Reset(); EffectSync.Reset(); CraftSync.Reset(); ChestLocks.Reset(); CombatSync.Reset(); ConveyorSync.Reset(); ProgressSync.Reset(); InteractionProxy.Reset(); TroopSync.Reset(); ItemScripts.Reset(); SermonSync.Reset(); StandIn.Reset(); TutorialSync.Reset(); Lobby.Reset(); SceneWait.Reset(); PartnerProfiles.ResetClient(); PartnerProfiles.ResetHost(); awaitingHello.Clear(); pendingWorld = null; } private static void CleanUp() { StorySync.Reset(); PlayerSync.Clear(); readyClients.Clear(); awaitingHello.Clear(); pendingWorld = null; WorldTransfer.Reset(); WorldSync.Reset(); NpcSync.Reset(); DropSync.Reset(); EconomySync.Reset(); KnowledgeSync.Reset(); ContainerSync.Reset(); SleepSync.Reset(); WorldStateSync.Reset(); VendorSync.Reset(); EffectSync.Reset(); CraftSync.Reset(); ChestLocks.Reset(); CombatSync.Reset(); ConveyorSync.Reset(); ProgressSync.Reset(); InteractionProxy.Reset(); TroopSync.Reset(); ItemScripts.Reset(); SermonSync.Reset(); StandIn.Reset(); TutorialSync.Reset(); Lobby.Reset(); SceneWait.Reset(); PartnerProfiles.ResetClient(); PartnerProfiles.ResetHost(); MainGame instance = MainGame.Instance; GameSave val = ((instance != null) ? instance.GameSave : null); if (val != null) { val.hostPlayer = null; val.clientPlayers?.Clear(); } } private static void EnsureWorldRunning() { if (!MainGame.IsGamePaused) { return; } try { AccessTools.Method(typeof(MainGame), "UnpauseGame", (Type[])null, (Type[])null).Invoke(MainGame.Instance, null); CoopLog.Info("Resumed the world (it was paused by an open window)."); } catch (Exception e) { CoopLog.Error("Resuming the paused world failed", e); } } public static bool IsLoadingOverlayShown() { try { UILoadingOverlay val = LazyUI.Get(); return (Object)(object)val != (Object)null && val.IsShown; } catch { return false; } } private static void Fail(string what, Exception e) { Status = what + ": " + e.Message; CoopLog.Error(what, e); } } [HarmonyPatch(typeof(CraftComponent), "AddToQueue")] internal static class CraftQueueOnHost { private static bool Prefix(CraftComponent __instance, CraftElementBase craftElement, bool addToQueueTop, ref CraftElementBase __result) { if (CraftSync.ClientSuppresses) { __result = craftElement; return false; } if (!CraftSync.ClientForwards) { return true; } __result = craftElement; return CraftSync.ClientQueue(__instance, craftElement, addToQueueTop, start: false); } } [HarmonyPatch(typeof(CraftComponent), "TryStartCraft")] internal static class CraftStartOnHost { private static bool Prefix(CraftComponent __instance, CraftElementBase craftElement, ref bool __result) { if (CraftSync.ClientSuppresses) { __result = false; return false; } if (!CraftSync.ClientForwards) { return true; } __result = true; return CraftSync.ClientQueue(__instance, craftElement, top: true, start: true); } } [HarmonyPatch(typeof(CraftComponent), "TryContinueFromQueue")] internal static class CraftContinueOnHost { private static bool Prefix(CraftComponent __instance, ref bool __result) { if (CraftSync.ClientSuppresses) { __result = false; return false; } if (!CraftSync.ClientForwards) { return true; } __result = false; return CraftSync.ClientSimple(__instance, 7); } } [HarmonyPatch(typeof(CraftComponent), "RemoveFromQueue")] internal static class CraftRemoveOnHost { private static bool Prefix(CraftComponent __instance, CraftElementBase craftElement) { if (CraftSync.ClientSuppresses) { return false; } if (CraftSync.ClientForwards) { return CraftSync.ClientRemove(__instance, craftElement); } CraftSync.HostLocalRemove(__instance, craftElement); return true; } } [HarmonyPatch(typeof(CraftComponent), "Cancel")] internal static class CraftCancelRefunds { private static bool Prefix(CraftComponent __instance) { if (CraftSync.ClientSuppresses) { return false; } if (CraftSync.ClientForwards) { return CraftSync.ClientSimple(__instance, 3); } CraftSync.BeginHostCancel(); return true; } private static void Finalizer() { CraftSync.EndHostCancel(); } } [HarmonyPatch(typeof(CraftComponent), "Clear")] internal static class CraftClearOnHost { private static bool Prefix(CraftComponent __instance) { if (CraftSync.ClientSuppresses) { return false; } if (CraftSync.ClientForwards) { return CraftSync.ClientSimple(__instance, 4); } CraftSync.HostLocalClear(__instance); return true; } } [HarmonyPatch(typeof(CraftComponent), "UpdateManual")] internal static class CraftWorkOnHost { private static bool Prefix(CraftComponent __instance, int deltaTicks) { if (CraftSync.ClientSuppresses) { return false; } if (!CraftSync.HostWorks(__instance)) { return false; } if (CraftSync.ClientForwards) { return CraftSync.ClientWork(__instance, deltaTicks); } return true; } } [HarmonyPatch(typeof(CraftComponent), "TryElementUpToQueue")] internal static class CraftMoveUpOnHost { private static bool Prefix(CraftComponent __instance, CraftElementBase craftElement, ref bool __result) { if (CraftSync.ClientSuppresses) { __result = false; return false; } if (!CraftSync.ClientForwards) { return true; } __result = true; return CraftSync.ClientMove(__instance, craftElement, up: true); } } [HarmonyPatch(typeof(CraftComponent), "TryElementDownToQueue")] internal static class CraftMoveDownOnHost { private static bool Prefix(CraftComponent __instance, CraftElementBase craftElement, ref bool __result) { if (CraftSync.ClientSuppresses) { __result = false; return false; } if (!CraftSync.ClientForwards) { return true; } __result = true; return CraftSync.ClientMove(__instance, craftElement, up: false); } } [HarmonyPatch(typeof(CraftComponent), "AddDestroyCraft")] internal static class TeardownOnHost { private static bool Prefix(CraftComponent __instance, CraftElement craftElement) { if (CraftSync.ClientSuppresses) { return false; } if (CraftSync.ClientForwards) { return CraftSync.ClientDestroyCraft(__instance, craftElement); } return true; } } [HarmonyPatch(typeof(CraftComponent), "RemoveDestroyCraft")] internal static class StopTeardownOnHost { private static bool Prefix(CraftComponent __instance) { if (CraftSync.ClientSuppresses) { return false; } if (CraftSync.ClientForwards) { return CraftSync.ClientSimple(__instance, 9); } return true; } } [HarmonyPatch(typeof(CraftComponent), "DropItems")] internal static class CancelRefundsToCanceller { private static bool Prefix(List items) { return !CraftSync.TryRefundInsteadOfDrop(items); } } [HarmonyPatch(typeof(CraftInteractionHandler), "Interact")] internal static class OnePlayerPerStation { private static readonly FieldRef AssignedWgoField = AccessTools.FieldRefAccess("assignedWgo"); [HarmonyPriority(800)] private static bool Prefix(CraftInteractionHandler __instance, PlayerController interactor, ref bool __result) { try { if (CoopSession.Role == CoopRole.None || (Object)(object)interactor == (Object)null || (Object)(object)interactor != (Object)(object)MainGame.PlayerController) { return true; } Wgo obj = AssignedWgoField.Invoke((WGOInteractionHandlerBase)(object)__instance); WgoData val = ((obj != null) ? obj.Data : null); if (val == null) { return true; } if (!CraftSync.MayUse(val, claim: true)) { __result = true; return false; } CraftSync.OnWindowOpened(val); return true; } catch (Exception ex) { CoopLog.Debug("Checking who's using a station failed: " + ex.Message); return true; } } } [HarmonyPatch(typeof(PlayerCraftActivity), "CanUseTool")] internal static class OneWorkerPerStation { private static void Postfix(PlayerCraftActivity __instance, ref bool __result) { try { if (__result && CoopSession.Role != CoopRole.None && !CraftSync.MayUse(((PlayerActivity)__instance).WgoData, claim: false)) { __result = false; } } catch (Exception ex) { CoopLog.Debug("Checking who's working a station failed: " + ex.Message); } } } [HarmonyPatch(typeof(CraftComponent), "Finish")] internal static class HandMadeGoesToMaker { private static void Prefix(CraftComponent __instance) { try { CraftSync.BeginFinish(__instance); } catch (Exception e) { CoopLog.Error("Finding who made a craft failed", e); } } private static void Finalizer() { try { CraftSync.EndFinish(); } catch (Exception e) { CoopLog.Error("Handing a craft's output to its maker failed", e); } } } [HarmonyPatch(typeof(WgoData), "MakeDrop", new Type[] { typeof(Item) })] internal static class HandMadeIntoTheirBag { private static bool Prefix(WgoData __instance, Item item) { try { return !CraftSync.TakeMade(__instance, item); } catch (Exception ex) { CoopLog.Debug("Taking a craft's output failed (" + ex.Message + "); dropping it."); return true; } } } [HarmonyPatch(typeof(TechPointsSpawner), "CreateSpawner")] internal static class PartnersTechPointsFlyToThem { private static bool Prefix(Vector3 pos, int r, int g, int b, int h) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return !CraftSync.CreditTechPoints(pos, r, g, b, h); } catch (Exception ex) { CoopLog.Debug("Sending tech points to a partner failed: " + ex.Message); return true; } } } [HarmonyPatch(typeof(CraftElementBase), "RemoveCraftRequirements")] internal static class UsePrepaidMaterials { private static bool Prefix(CraftElementBase __instance, ICraftable craftable) { return !CraftSync.TryUsePrepaid(__instance, craftable); } } [HarmonyPatch(typeof(CraftElementBase), "CanStartCraft")] internal static class PrepaidCraftsCanStart { private static void Postfix(CraftElementBase __instance, ICraftable craftableObject, ref CraftStatus __result) { if ((int)__result == 1 && CraftSync.HasPrepaid(__instance, craftableObject)) { __result = (CraftStatus)0; } } } [HarmonyPatch(typeof(WgoData), "GetCraftableMultiInventory")] internal static class ChestsFirstCraftMaterials { private static bool Prefix(WgoData __instance, bool excludeWorkerInventory, ref MultiInventory __result) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (CoopSession.Role == CoopRole.None || ((object)__instance).GetType() != typeof(WgoData)) { return true; } try { MultiInventory val = new MultiInventory(); IWorker worker = __instance.Worker; WorldZoneData worldZoneData = __instance.WorldZoneData; if (worldZoneData != null) { val.Add(new MultiInventory(worldZoneData, (WgoData)null, false)); if (!excludeWorkerInventory && !(worker is ZombieWgoData)) { val.Add(__instance.CraftInventory); } if (!excludeWorkerInventory && worker != null) { val.Add(worker.WorkerInventory); } PlayerData playerData = MainGame.PlayerData; if (!excludeWorkerInventory && playerData != null && playerData.CurrentWorldZoneData == worldZoneData && worker is PlayerController != Object.op_Implicit((Object)(object)MainGame.PlayerController)) { val.Add(playerData.Inventory); } } else { val.Add(new MultiInventory((IEnumerable)new List { __instance.Inventory, __instance.CraftInventory })); if (!excludeWorkerInventory && worker != null) { val.Add(worker.WorkerInventory); } } __result = val; return false; } catch (Exception ex) { CoopLog.Debug("Chests-first material order failed, using the game's: " + ex.Message); return true; } } } internal static class CraftSync { [Serializable] public class CraftState { public List queue; public int curIdx; public int status; public int prevQueueCount; public float restartQueueTimer; public float autoCraftTickDuration; public float currentAutoCraftTickTime; public int zombieSubTicks; public bool hasPreFinishUpdate; public float finishHeldTimer; } private class PrepaidUnit { public ulong Owner; public List Items; } private class Holder { public ulong Client; public float LastAction; } private delegate void WriteBody(FastBufferWriter writer); private const byte OpQueue = 1; private const byte OpRemove = 2; private const byte OpCancel = 3; private const byte OpClear = 4; private const byte OpWork = 5; private const byte OpMove = 6; private const byte OpContinue = 7; private const byte OpDestroyCraft = 8; private const byte OpRemoveDestroyCraft = 9; private const byte OpState = 20; private const byte OpRefund = 21; private const byte OpRefused = 22; private const byte OpClaim = 10; private const byte OpOutput = 23; private const byte OpTechPoints = 24; private const byte OpHolders = 25; private const float StateInterval = 0.25f; private const float HoldSeconds = 1f; private const float KeepAliveSeconds = 0.3f; private const float MakerSeconds = 6f; private static readonly FieldRef ToolComponentField = AccessTools.FieldRefAccess("toolComponent"); private static readonly FieldRef> QueueField = AccessTools.FieldRefAccess>("craftElementsQueue"); private static readonly FieldRef CurIdxField = AccessTools.FieldRefAccess("curCraftQueueIdx"); private static readonly FieldRef PrevQueueCountField = AccessTools.FieldRefAccess("prevQueueCount"); private static readonly FieldRef RestartTimerField = AccessTools.FieldRefAccess("restartQueueTimer"); private static readonly FieldRef StatusField = AccessTools.FieldRefAccess("status"); private static readonly FieldRef TickDurationField = AccessTools.FieldRefAccess("autoCraftTickDuration"); private static readonly FieldRef TickTimeField = AccessTools.FieldRefAccess("currentAutoCraftTickTime"); private static readonly FieldRef ZombieTicksField = AccessTools.FieldRefAccess("zombieSubTicks"); private static readonly FieldRef PreFinishField = AccessTools.FieldRefAccess("hasPreFinishUpdate"); private static readonly FieldRef FinishHeldField = AccessTools.FieldRefAccess("finishHeldTimer"); private static readonly FieldRef CraftableField = AccessTools.FieldRefAccess("craftableObject"); private static readonly FieldRef> CraftInputField = AccessTools.FieldRefAccess>("craftInput"); private static readonly Dictionary> prepaid = new Dictionary>(); private static readonly Dictionary sentSignatures = new Dictionary(); private static readonly Dictionary appliedSignatures = new Dictionary(); private static float nextState; private static ulong? refundTarget; private static bool refundActive; public static bool ApplyingRemote; public static int Sent; public static int Applied; private static readonly Dictionary holders = new Dictionary(); private static bool holdersChanged; private static float nextHolders; private static int lastHoldersSent; private static readonly Dictionary refusedAt = new Dictionary(); private static readonly Dictionary> heldBy = new Dictionary>(); private static readonly Dictionary claimSentAt = new Dictionary(); private static WgoData windowStation; private static float nextWindowClaim; private static float nextBusyToast; private static int finishDepth; private static WgoData finishing; private static ulong finishingFor; private static bool finishCredit; private static readonly List made = new List(); private static readonly List credit = new List(); private static readonly Dictionary pendingWork = new Dictionary(); private static float nextWorkFlush; public static bool ClientForwards { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && Plugin.HostRunsWorld.Value && !ApplyingRemote) { return !SermonSync.LocalCraft; } return false; } } public static bool ClientSuppresses { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && Plugin.HostRunsWorld.Value) { return ApplyingRemote; } return false; } } private static bool HostActive { get { if (CoopSession.Role == CoopRole.Host) { return CoopSession.HasReadyClients; } return false; } } private static ulong Me { get { if (CoopSession.Role != CoopRole.Host) { return CoopNetwork.LocalClientId; } return 0uL; } } public static void Reset() { prepaid.Clear(); sentSignatures.Clear(); appliedSignatures.Clear(); refundTarget = null; refundActive = false; ApplyingRemote = false; Sent = 0; Applied = 0; holders.Clear(); holdersChanged = false; nextHolders = 0f; lastHoldersSent = 0; refusedAt.Clear(); heldBy.Clear(); claimSentAt.Clear(); windowStation = null; finishDepth = 0; finishing = null; finishCredit = false; made.Clear(); credit.Clear(); } public static void OnClientReady() { sentSignatures.Clear(); } private static WgoData StationOf(CraftComponent component) { ICraftable obj = CraftableField.Invoke(component); return (WgoData)(object)((obj is WgoData) ? obj : null); } private static bool PlayerNear(WgoData station) { //IL_0026: 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_0036: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); if ((Object)(object)val == (Object)null || station == null) { return false; } Vector3 val2 = ((Component)val).transform.position - station.Position; bool num = ((Vector3)(ref val2)).sqrMagnitude <= 64f; if (!num) { CoopLog.Debug("Ignored a station action on " + ((ObjectLinkedToDefinition)(object)station).id + " that wasn't ours (we're not near it)."); } return num; } private static string Key(string stationUid, string craftId) { return stationUid + "|" + craftId; } public static bool ClientQueue(CraftComponent component, CraftElementBase element, bool top, bool start) { WgoData station = StationOf(component); if (ClientForwards) { WgoData obj = station; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && element != null) { if (!PlayerNear(station) || !ClientMayUse(station, claim: false)) { return false; } List> list = new List>(); if (!element.IsInfinite && element.Requirements != null && element.Requirements.Count > 0) { int num = Math.Max(1, element.Count); for (int i = 0; i < num; i++) { MultiInventory craftableMultiInventory = station.GetCraftableMultiInventory(false); if (!craftableMultiInventory.HasItemsById(element.Requirements, station)) { break; } list.Add(craftableMultiInventory.RemoveItems(element.Requirements, station)); } if (list.Count == 0) { CoopRunner.Think("Not enough materials for that."); return false; } if (list.Count < num) { element.Count = list.Count; CoopRunner.Toast($"Only enough materials for {list.Count}."); } ContainerSync.SendChangedEverywhere(); } byte[] elementBytes; byte[] unitBytes; try { elementBytes = SerializationUtility.SerializeValue(element, (DataFormat)0, (SerializationContext)null); unitBytes = SerializationUtility.SerializeValue>>(list, (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Packing craft " + element.CraftId + " failed", e); return true; } Send(delegate(FastBufferWriter w) { //IL_0008: 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_003c: 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_0059: 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) byte b = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, station.UniqueId.Id); b = (top ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (start ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref w, elementBytes); CoopMessages.WriteBytes(ref w, unitBytes); }); CoopLog.Info($"Queued {element.CraftId} x{element.Count} at {((ObjectLinkedToDefinition)(object)station).id} on the host ({list.Count} paid)."); return false; } } return true; } public static bool ClientWork(CraftComponent component, int ticks) { WgoData val = StationOf(component); if (!ClientForwards || ((val != null) ? val.UniqueId : null) == (SGuid)null || WorldSync.ApplyingRemote) { return true; } if (!PlayerNear(val) || !ClientMayUse(val, claim: false)) { return false; } pendingWork.TryGetValue(val.UniqueId.Id, out var value); pendingWork[val.UniqueId.Id] = value + ticks; return false; } private static void FlushWork() { if (pendingWork.Count == 0 || Time.unscaledTime < nextWorkFlush) { return; } nextWorkFlush = Time.unscaledTime + 0.1f; foreach (KeyValuePair pair in pendingWork.ToList()) { Send(delegate(FastBufferWriter w) { //IL_0008: 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_0038: 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) byte b = 5; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, pair.Key); int value = pair.Value; ((FastBufferWriter)(ref w)).WriteValueSafe(ref value, default(ForPrimitives)); CoopMessages.WriteString(ref w, ""); }); } pendingWork.Clear(); } public static bool ClientSimple(CraftComponent component, byte op, int value = 0, string craftId = "") { WgoData station = StationOf(component); if (ClientForwards) { WgoData obj = station; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && !WorldSync.ApplyingRemote) { if (!PlayerNear(station) || (op != 7 && !ClientMayUse(station, claim: false))) { return false; } Send(delegate(FastBufferWriter w) { //IL_000a: 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_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) ((FastBufferWriter)(ref w)).WriteValueSafe(ref op, default(ForPrimitives)); CoopMessages.WriteString(ref w, station.UniqueId.Id); ((FastBufferWriter)(ref w)).WriteValueSafe(ref value, default(ForPrimitives)); CoopMessages.WriteString(ref w, craftId ?? ""); }); if (op != 5) { CoopLog.Info($"Sent craft op {op} ({craftId} {value}) at {((ObjectLinkedToDefinition)(object)station).id} to the host."); } return false; } } return true; } public static bool ClientRemove(CraftComponent component, CraftElementBase element) { int num = QueueField.Invoke(component).IndexOf(element); if (num >= 0) { return ClientSimple(component, 2, num, element.CraftId); } return true; } public static bool ClientMove(CraftComponent component, CraftElementBase element, bool up) { int num = QueueField.Invoke(component).IndexOf(element); if (num >= 0) { return ClientSimple(component, 6, up ? num : (-num - 1), element.CraftId); } return true; } public static bool ClientDestroyCraft(CraftComponent component, CraftElement element) { WgoData station = StationOf(component); if (ClientForwards) { WgoData obj = station; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && element != null) { if (!PlayerNear(station) || !ClientMayUse(station, claim: false)) { return false; } byte[] bytes = SerializationUtility.SerializeValue(element, (DataFormat)0, (SerializationContext)null); Send(delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 8; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, station.UniqueId.Id); CoopMessages.WriteBytes(ref w, bytes); }); CoopLog.Info("Asked the host to start tearing down " + ((ObjectLinkedToDefinition)(object)station).id + "."); return false; } } return true; } public static bool TryUsePrepaid(CraftElementBase element, ICraftable craftable) { if (CoopSession.Role == CoopRole.Host) { WgoData val = (WgoData)(object)((craftable is WgoData) ? craftable : null); if (val != null && !(val.UniqueId == (SGuid)null)) { if (!prepaid.TryGetValue(Key(val.UniqueId.Id, element.CraftId), out var value) || value.Count == 0) { return false; } PrepaidUnit prepaidUnit = value[0]; value.RemoveAt(0); CraftInputField.Invoke(element) = prepaidUnit.Items ?? new List(); CoopLog.Debug("Started " + element.CraftId + " at " + ((ObjectLinkedToDefinition)(object)val).id + " with " + (PartnerProfiles.NameOf(prepaidUnit.Owner) ?? "a partner") + "'s prepaid materials."); return true; } } return false; } public static bool HasPrepaid(CraftElementBase element, ICraftable craftable) { if (CoopSession.Role == CoopRole.Host) { WgoData val = (WgoData)(object)((craftable is WgoData) ? craftable : null); if (val != null && val.UniqueId != (SGuid)null && prepaid.TryGetValue(Key(val.UniqueId.Id, element.CraftId), out var value)) { return value.Count > 0; } } return false; } public static bool TryRefundInsteadOfDrop(List items) { if (!refundActive || items == null) { return false; } Refund(refundTarget, items); return true; } public static void BeginHostCancel() { if (CoopSession.Role == CoopRole.Host && !refundActive) { refundActive = true; refundTarget = null; } } public static void EndHostCancel() { if (!refundTarget.HasValue) { refundActive = false; } } private unsafe static void Refund(ulong? target, List items) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0081: 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) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) items = items?.Where((Item i) => i != null && !i.IsEmpty).ToList(); if (items == null || items.Count == 0) { return; } if (!target.HasValue || target.Value == 0L) { GiveToLocalPlayer(items); CoopLog.Info($"Refunded {items.Count} stacks to the host."); return; } byte[] bytes = SerializationUtility.SerializeValue>(items, (DataFormat)0, (SerializationContext)null); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 21; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, bytes); CoopMessages.SendTo("GK2Coop.Crafts", target.Value, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info($"Refunded {items.Count} stacks to {PartnerProfiles.NameOf(target.Value) ?? $"player {target.Value}"}."); } private static void GiveToLocalPlayer(List items) { //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_005d: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); if (val == null) { return; } foreach (Item item in items) { if (!val.inventory.AddItemToInventory(item, (Item)null, false)) { Vector3 position = ((Component)MainGame.PlayerController.PhysicalBody).transform.position; MainGame.Instance.dropSystem.DropItem(item, val.currentGameSceneId, position, (List)null); } } } public static void HostLocalRemove(CraftComponent component, CraftElementBase element) { if (CoopSession.Role == CoopRole.Host && !ApplyingRemote && element != null && !element.IsStarted) { WgoData val = StationOf(component); if (!(((val != null) ? val.UniqueId : null) == (SGuid)null) && QueueField.Invoke(component).Contains(element)) { RefundPrepaid(val.UniqueId.Id, element.CraftId, (!element.IsInfinite) ? element.Count : 0, null); } } } public static void HostLocalClear(CraftComponent component) { if (CoopSession.Role != CoopRole.Host || ApplyingRemote) { return; } WgoData val = StationOf(component); if (((val != null) ? val.UniqueId : null) == (SGuid)null) { return; } string uid = val.UniqueId.Id; foreach (string item in prepaid.Keys.Where((string k) => k.StartsWith(uid + "|")).ToList()) { RefundPrepaid(uid, item.Substring(uid.Length + 1), 0, null); } CraftElementBase currentCraftElement = component.CurrentCraftElement; if (currentCraftElement != null && currentCraftElement.IsStarted && CoopSession.HasReadyClients) { Refund(null, CraftInputField.Invoke(currentCraftElement)); } } private static void RefundPrepaid(string stationUid, string craftId, int count, ulong? target) { if (!prepaid.TryGetValue(Key(stationUid, craftId), out var value) || value.Count == 0) { return; } int num = ((count <= 0) ? value.Count : Math.Min(count, value.Count)); List list = new List(); for (int i = 0; i < num; i++) { PrepaidUnit prepaidUnit = value[value.Count - 1]; value.RemoveAt(value.Count - 1); if (prepaidUnit.Items != null) { list.AddRange(prepaidUnit.Items); } } Refund(target, list); } private static string NameOf(ulong id) { if (id == 0L) { object obj; if (CoopSession.Role != CoopRole.Host) { obj = PlayerSync.Host?.Label; if (obj == null) { return "The host"; } } else { if (!string.IsNullOrEmpty(Plugin.PlayerName.Value?.Trim())) { return Plugin.PlayerName.Value.Trim(); } obj = "The host"; } return (string)obj; } return PartnerProfiles.NameOf(id) ?? PlayerSync.Get(id)?.Label ?? $"Player {id}"; } private static bool Lockable(WgoData station) { if (((station != null) ? station.UniqueId : null) != (SGuid)null && station.CraftComponent != null) { return !(station.Worker is ZombieWgoData); } return false; } private static ulong? HolderOf(string uid) { if (uid == null || !holders.TryGetValue(uid, out var value) || Time.unscaledTime - value.LastAction > 1f) { return null; } if (value.Client != 0L && !CoopSession.ReadyClients.Contains(value.Client)) { return null; } return value.Client; } private static bool Claim(WgoData station, ulong who) { if (!HostActive || !Lockable(station)) { return true; } string id = station.UniqueId.Id; ulong? num = HolderOf(id); if (num.HasValue && num.Value != who) { return false; } if (!holders.TryGetValue(id, out var value)) { value = new Holder(); holders[id] = value; } if (!num.HasValue || value.Client != who) { holdersChanged = true; nextHolders = 0f; CoopLog.Info(NameOf(who) + " is using " + ((ObjectLinkedToDefinition)(object)station).id + "."); } value.Client = who; value.LastAction = Time.unscaledTime; return true; } private static void BusyToast(ulong? who) { if (!(Time.unscaledTime < nextBusyToast)) { nextBusyToast = Time.unscaledTime + 2.5f; CoopRunner.Think((who.HasValue ? NameOf(who.Value) : "Someone") + " is using this station."); } } public static bool MayUse(WgoData station, bool claim) { if (((station != null) ? station.UniqueId : null) == (SGuid)null || CoopSession.Role == CoopRole.None) { return true; } if (CoopSession.Role == CoopRole.Client) { return ClientMayUse(station, claim); } if (!HostActive || !Lockable(station)) { return true; } bool flag; if (claim) { flag = Claim(station, 0uL); } else { ulong? num = HolderOf(station.UniqueId.Id); flag = !num.HasValue || num.Value == 0; } if (flag) { return true; } BusyToast(HolderOf(station.UniqueId.Id)); return false; } private static bool ClientMayUse(WgoData station, bool claim) { if (!ClientForwards || ((station != null) ? station.UniqueId : null) == (SGuid)null) { return true; } string uid = station.UniqueId.Id; if (heldBy.TryGetValue(uid, out var value) && value.Key != Me && Time.unscaledTime < value.Value) { BusyToast(value.Key); return false; } if (claim && (!claimSentAt.TryGetValue(uid, out var value2) || Time.unscaledTime - value2 > 0.3f)) { claimSentAt[uid] = Time.unscaledTime; Send(delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) byte b = 10; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, uid); }); } return true; } public static void OnWindowOpened(WgoData station) { windowStation = station; nextWindowClaim = Time.unscaledTime + 0.3f; } private static WgoData StationInUse() { if (windowStation != null && windowStation.UniqueId != (SGuid)null && AnyCraftWindowOpen()) { return windowStation; } windowStation = null; try { PlayerController playerController = MainGame.PlayerController; ToolComponent val = (((Object)(object)playerController != (Object)null) ? ToolComponentField.Invoke(playerController) : null); if (val != null && val.IsActionActive) { IWorkActivity toolActor = val.ToolActor; PlayerCraftActivity val2 = (PlayerCraftActivity)(object)((toolActor is PlayerCraftActivity) ? toolActor : null); if (val2 != null) { return ((PlayerActivity)val2).WgoData; } } } catch { } return null; } private static void TickWindowClaim() { if (Time.unscaledTime < nextWindowClaim || CoopSession.Role == CoopRole.None) { return; } nextWindowClaim = Time.unscaledTime + 0.3f; WgoData val = StationInUse(); if (((val != null) ? val.UniqueId : null) == (SGuid)null) { return; } if (CoopSession.Role == CoopRole.Host) { Claim(val, 0uL); } else { if (CoopSession.Role != CoopRole.Client || !ClientForwards) { return; } string uid = val.UniqueId.Id; if (!claimSentAt.TryGetValue(uid, out var value) || Time.unscaledTime - value > 0.25f) { claimSentAt[uid] = Time.unscaledTime; Send(delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) byte b = 10; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, uid); }); } } } private static bool AnyCraftWindowOpen() { try { UICraftWindow window = LazyUI.GetWindow(); int result; if (window == null || !((LazyWindow)(object)window).IsShown) { UISingleCraftWindow window2 = LazyUI.GetWindow(); if (window2 == null || !((LazyWindow)(object)window2).IsShown) { result = ((((LazyWindow)(object)LazyUI.GetWindow())?.IsShown ?? false) ? 1 : 0); goto IL_003a; } } result = 1; goto IL_003a; IL_003a: return (byte)result != 0; } catch { return false; } } private static void CloseCraftWindows() { try { UICraftWindow window = LazyUI.GetWindow(); if ((Object)(object)window != (Object)null && ((LazyWindow)(object)window).IsShown) { ((LazyWindow)(object)window).Close(); } UISingleCraftWindow window2 = LazyUI.GetWindow(); if ((Object)(object)window2 != (Object)null && ((LazyWindow)(object)window2).IsShown) { ((LazyWindow)(object)window2).Close(); } UIFuelCraftWindow window3 = LazyUI.GetWindow(); if ((Object)(object)window3 != (Object)null && ((LazyWindow)(object)window3).IsShown) { ((LazyWindow)(object)window3).Close(); } } catch (Exception ex) { CoopLog.Debug("Closing the craft window failed: " + ex.Message); } windowStation = null; } public static bool HostWorks(CraftComponent component) { if (CoopSession.Role != CoopRole.Host || ApplyingRemote || !HostActive) { return true; } WgoData station = StationOf(component); if (Lockable(station)) { return MayUse(station, claim: true); } return true; } private unsafe static void RefuseOp(ulong sender, WgoData station, byte op, ref FastBufferReader reader) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0139: 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_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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) if (op == 1) { byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); CoopMessages.ReadBytes(ref reader); List> list = SerializationUtility.DeserializeValue>>(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); Refund(sender, list?.SelectMany((List u) => u).ToList()); } string id = station.UniqueId.Id; holdersChanged = true; nextHolders = 0f; string key = sender + "|" + id; if (refusedAt.TryGetValue(key, out var value) && Time.unscaledTime - value < 2.5f) { return; } refusedAt[key] = Time.unscaledTime; ulong? num = HolderOf(id); string text = (num.HasValue ? NameOf(num.Value) : "Someone"); CoopLog.Info($"{NameOf(sender)} tried to use {((ObjectLinkedToDefinition)(object)station).id} (op {op}), but {text} is using it."); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 22; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, id); CoopMessages.WriteString(ref writer, text); CoopMessages.SendTo("GK2Coop.Crafts", sender, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void SendHolders() { //IL_00a8: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_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_011e: 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_0143: 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_016e: Unknown result type (might be due to invalid IL or missing references) List>> list = new List>>(); foreach (KeyValuePair holder in holders) { ulong? num = HolderOf(holder.Key); if (num.HasValue) { list.Add(new KeyValuePair>(holder.Key, new KeyValuePair(num.Value, 1f - (Time.unscaledTime - holder.Value.LastAction)))); } } if (list.Count == 0 && lastHoldersSent == 0 && !holdersChanged) { return; } lastHoldersSent = list.Count; holdersChanged = false; FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 25; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (KeyValuePair> item in list) { CoopMessages.WriteString(ref writer, item.Key); ulong key = item.Value.Key; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref key, default(ForPrimitives)); float value = item.Value.Value; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref value, default(ForPrimitives)); } CoopMessages.SendToReadyClients("GK2Coop.Crafts", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public static void BeginFinish(CraftComponent component) { if (finishDepth++ > 0) { return; } finishing = null; finishCredit = false; made.Clear(); if (HostActive) { WgoData val = StationOf(component); CraftElementBase currentCraftElement = component.CurrentCraftElement; if (Lockable(val) && ((currentCraftElement != null) ? currentCraftElement.Def : null) != null && !currentCraftElement.Def.isAuto && holders.TryGetValue(val.UniqueId.Id, out var value) && !(Time.unscaledTime - value.LastAction > 6f) && (value.Client == 0L || CoopSession.ReadyClients.Contains(value.Client))) { finishing = val; finishingFor = value.Client; BeginCredit(value.Client); finishCredit = true; } } } public static bool TakeMade(WgoData data, Item item) { if (finishing == null || data != finishing || item == null || item.IsEmpty) { return false; } ItemDef definition = ((ObjectLinkedToDefinition)(object)item).Definition; if (definition == null || definition.isTechPoint || definition.isFuel) { return false; } made.Add(item); return true; } public unsafe static void EndFinish() { //IL_0128: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0167: 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) //IL_019c: Unknown result type (might be due to invalid IL or missing references) if (finishDepth == 0 || --finishDepth > 0) { return; } WgoData val = finishing; finishing = null; if (finishCredit) { finishCredit = false; EndCredit(); } if (val == null || made.Count == 0) { made.Clear(); return; } List list = made.ToList(); made.Clear(); string text = string.Join(", ", list.Select((Item i) => $"{((ObjectLinkedToDefinition)(object)i).id} x{i.Count}")); if (finishingFor == 0L) { GiveMade(list); CoopLog.Info("Made " + text + " at " + ((ObjectLinkedToDefinition)(object)val).id + ": into our bag."); return; } byte[] bytes; try { bytes = SerializationUtility.SerializeValue>(list, (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Packing what " + ((ObjectLinkedToDefinition)(object)val).id + " made failed; dropping it there", e); foreach (Item item in list) { MainGame.Instance.dropSystem.DropItem(item, val.WorldId, val.Position, (List)null); } return; } FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 23; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, ((ObjectLinkedToDefinition)(object)val).id ?? ""); CoopMessages.WriteBytes(ref writer, bytes); CoopMessages.SendTo("GK2Coop.Crafts", finishingFor, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info(NameOf(finishingFor) + " made " + text + " at " + ((ObjectLinkedToDefinition)(object)val).id + ": sent to their bag."); } private static void GiveMade(List items) { //IL_00a5: 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_00aa: 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) PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); if (val == null) { return; } List list = new List(); List list2 = default(List); foreach (Item item in items) { if (item != null && !item.IsEmpty) { if (val.inventory.AddItemToInventory(item, ref list2, (Item)null, false)) { list.AddRange((list2 != null && list2.Count > 0) ? list2 : new List { item }); } else { Vector3 val2 = (((Object)(object)playerController.PhysicalBody != (Object)null) ? ((Component)playerController.PhysicalBody).transform.position : val.position.Value); MainGame.Instance.dropSystem.DropItem(item, val.currentGameSceneId, val2, (List)null); } } } TutorialSync.ShowPickup(list); } public static void BeginCredit(ulong client) { credit.Add(client); } public static void EndCredit() { if (credit.Count > 0) { credit.RemoveAt(credit.Count - 1); } } public unsafe static bool CreditTechPoints(Vector3 pos, int r, int g, int b, int h) { //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_0050: 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_0061: 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_0076: 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_0089: 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_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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (credit.Count == 0 || CoopSession.Role != CoopRole.Host) { return false; } ulong num = credit[credit.Count - 1]; if (num == 0L || !CoopSession.ReadyClients.Contains(num) || r + g + b + h <= 0) { return false; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b2 = 24; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b2, default(ForPrimitives)); CoopMessages.WriteVector3(ref writer, pos); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref r, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref g, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref h, default(ForPrimitives)); CoopMessages.SendTo("GK2Coop.Crafts", num, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info($"Tech points from {NameOf(num)}'s work (red {r}, green {g}, blue {b}, gratitude {h}) sent to fly to them."); return true; } private unsafe static void Send(WriteBody body) { //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_0012: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter writer = CoopMessages.NewWriter(); try { body(writer); CoopMessages.SendToHost("GK2Coop.Crafts", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } public static void OnCrafts(ulong sender, FastBufferReader reader) { //IL_000c: 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_00b3: 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_00c6: 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_01c0: 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_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_0581: Unknown result type (might be due to invalid IL or missing references) //IL_0587: Unknown result type (might be due to invalid IL or missing references) //IL_05a1: Unknown result type (might be due to invalid IL or missing references) //IL_05a7: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Client) { ClientReceive(b, ref reader); } else { if (CoopSession.Role != CoopRole.Host) { return; } string uid = CoopMessages.ReadString(ref reader); WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(uid)) : null); CraftComponent val2 = ((val != null) ? val.CraftComponent : null); string text = PartnerProfiles.NameOf(sender) ?? $"Player {sender}"; if (val2 == null) { CoopLog.Warn($"{text} sent craft op {b} for a station that isn't in the host's world ({uid})."); if (b == 1) { byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); CoopMessages.ReadBytes(ref reader); List> list = SerializationUtility.DeserializeValue>>(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); Refund(sender, list?.SelectMany((List u) => u).ToList()); } return; } if (b == 7) { ulong? num = ((Lockable(val) && HostActive) ? HolderOf(uid) : ((ulong?)null)); if (num.HasValue && num.Value != sender) { return; } } else if (!Claim(val, sender)) { RefuseOp(sender, val, b, ref reader); return; } if (b == 10) { return; } ApplyingRemote = true; try { switch (b) { case 1: { byte b3 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b3, default(ForPrimitives)); byte b4 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b4, default(ForPrimitives)); CraftElementBase val5 = SerializationUtility.DeserializeValue(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); List> list4 = SerializationUtility.DeserializeValue>>(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null) ?? new List>(); if (val5 == null) { Refund(sender, list4.SelectMany((List u) => u).ToList()); break; } string key = Key(uid, val5.CraftId); if (!prepaid.TryGetValue(key, out var value)) { value = new List(); prepaid[key] = value; } foreach (List item in list4) { value.Add(new PrepaidUnit { Owner = sender, Items = item }); } val2.AddToQueue(val5, b3 != 0, -1); if (b4 != 0 || b3 != 0) { val2.TryContinueFromQueue(); } Applied++; CoopLog.Info($"{text} queued {val5.CraftId} x{val5.Count} at {((ObjectLinkedToDefinition)(object)val).id} ({list4.Count} paid)."); break; } case 2: { int num4 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num4, default(ForPrimitives)); string craftId = CoopMessages.ReadString(ref reader); List list3 = QueueField.Invoke(val2); CraftElementBase val4 = (CraftElementBase)((num4 >= 0 && num4 < list3.Count && list3[num4].CraftId == craftId) ? ((object)list3[num4]) : ((object)((IEnumerable)list3).FirstOrDefault((Func)((CraftElementBase val6) => val6.CraftId == craftId && !val6.IsStarted)))); if (val4 != null) { int count = ((!val4.IsInfinite) ? val4.Count : 0); val2.RemoveFromQueue(val4, false); if (!list3.Contains(val4)) { RefundPrepaid(uid, craftId, count, sender); } CoopLog.Info(text + " removed " + craftId + " from " + ((ObjectLinkedToDefinition)(object)val).id + "'s queue."); } break; } case 3: refundActive = true; refundTarget = sender; try { val2.Cancel(); } finally { refundActive = false; refundTarget = null; } CoopLog.Info(text + " cancelled the craft at " + ((ObjectLinkedToDefinition)(object)val).id + "."); break; case 4: { CraftElementBase currentCraftElement = val2.CurrentCraftElement; if (currentCraftElement != null && currentCraftElement.IsStarted) { Refund(sender, CraftInputField.Invoke(currentCraftElement)); } foreach (string item2 in prepaid.Keys.Where((string k) => k.StartsWith(uid + "|")).ToList()) { RefundPrepaid(uid, item2.Substring(uid.Length + 1), 0, sender); } val2.Clear(); CoopLog.Info(text + " cleared " + ((ObjectLinkedToDefinition)(object)val).id + "'s queue."); break; } case 5: { int num5 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num5, default(ForPrimitives)); val2.UpdateManual(num5); break; } case 6: { int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); string text2 = CoopMessages.ReadString(ref reader); List list2 = QueueField.Invoke(val2); bool flag = num2 >= 0; int num3 = (flag ? num2 : (-num2 - 1)); if (num3 >= 0 && num3 < list2.Count && list2[num3].CraftId == text2) { if (flag) { val2.TryElementUpToQueue(list2[num3]); } else { val2.TryElementDownToQueue(list2[num3]); } } break; } case 7: val2.TryContinueFromQueue(); break; case 8: { CraftElementBase obj = SerializationUtility.DeserializeValue(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); CraftElement val3 = (CraftElement)(object)((obj is CraftElement) ? obj : null); if (val3 != null) { val2.AddDestroyCraft(val3); CoopLog.Info(text + " started tearing down " + ((ObjectLinkedToDefinition)(object)val).id + "."); } break; } case 9: val2.RemoveDestroyCraft(); CoopLog.Info(text + " stopped tearing down " + ((ObjectLinkedToDefinition)(object)val).id + "."); break; } } catch (Exception e) { CoopLog.Error($"Craft op {b} from {text} at {((ObjectLinkedToDefinition)(object)val).id} failed", e); } finally { ApplyingRemote = false; } sentSignatures.Remove(uid); } } private static void ClientReceive(byte op, ref FastBufferReader reader) { //IL_01b2: 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_01be: 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_01d0: 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_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_00de: 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_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) switch (op) { case 22: { string text3 = CoopMessages.ReadString(ref reader); string text4 = CoopMessages.ReadString(ref reader); pendingWork.Remove(text3); WgoData obj = windowStation; object obj2; if (obj == null) { obj2 = null; } else { SGuid uniqueId = obj.UniqueId; obj2 = ((uniqueId != null) ? uniqueId.Id : null); } if ((string?)obj2 == text3) { CloseCraftWindows(); } nextBusyToast = Time.unscaledTime + 2.5f; CoopRunner.Think(text4 + " is using this station."); CoopLog.Info("The host turned us away from a station: " + text4 + " is using it."); break; } case 25: { int num6 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num6, default(ForPrimitives)); heldBy.Clear(); ulong key2 = default(ulong); float num8 = default(float); for (int num7 = 0; num7 < num6; num7++) { string key = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref key2, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num8, default(ForPrimitives)); heldBy[key] = new KeyValuePair(key2, Time.unscaledTime + Mathf.Max(0f, num8)); } break; } case 23: { string text2 = CoopMessages.ReadString(ref reader); List list = SerializationUtility.DeserializeValue>(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); if (list != null && list.Count > 0 && CoopSession.ClientInWorld) { GiveMade(list); CoopLog.Info("Made " + string.Join(", ", list.Select((Item i) => $"{((ObjectLinkedToDefinition)(object)i).id} x{i.Count}")) + " at " + text2 + ": into our bag."); } break; } case 24: { Vector3 val3 = CoopMessages.ReadVector3(ref reader); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); int num3 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); int num4 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num4, default(ForPrimitives)); int num5 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num5, default(ForPrimitives)); if (!CoopSession.ClientInWorld) { break; } PlayerController playerController = MainGame.PlayerController; if ((Object)(object)((playerController != null) ? playerController.CurrentGameScene : null) == (Object)null) { break; } try { TechPointsSpawner.CreateSpawner(val3, num2, num3, num4, num5); CoopLog.Debug($"Our tech points from the host's game: red {num2}, green {num3}, blue {num4}, gratitude {num5}."); break; } catch (Exception ex) { CoopLog.Warn("Couldn't spawn our tech points (" + ex.Message + "); adding them straight away."); PlayerData playerData = MainGame.PlayerData; if (playerData != null) { if (num2 > 0) { playerData.AddRes("tech_red", (float)num2); } if (num3 > 0) { playerData.AddRes("tech_green", (float)num3); } if (num4 > 0) { playerData.AddRes("tech_blue", (float)num4); } if (num5 > 0) { playerData.AddRes("happiness", (float)num5); } } break; } } case 21: { List list2 = SerializationUtility.DeserializeValue>(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); if (list2 != null && list2.Count > 0) { GiveToLocalPlayer(list2); TutorialSync.ShowPickup(list2); CoopLog.Info($"Got {list2.Count} stacks back from the host."); } break; } case 20: { if (!CoopSession.ClientInWorld) { break; } string text = CoopMessages.ReadString(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); byte[] array = CoopMessages.ReadBytes(ref reader); if (appliedSignatures.TryGetValue(text, out var value) && value == num) { break; } WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text)) : null); CraftComponent val2 = ((val != null) ? val.CraftComponent : null); if (val2 == null) { break; } try { CraftState state = SerializationUtility.DeserializeValue(array, (DataFormat)0, (DeserializationContext)null); ApplyState(val2, state); appliedSignatures[text] = num; Applied++; break; } catch (Exception e) { CoopLog.Error("Applying " + ((ObjectLinkedToDefinition)(object)val).id + "'s craft state failed", e); break; } } } } public static void Tick() { //IL_017f: Unknown result type (might be due to invalid IL or missing references) TickWindowClaim(); if (CoopSession.Role == CoopRole.Client) { FlushWork(); return; } if (HostActive && Time.unscaledTime >= nextHolders) { nextHolders = Time.unscaledTime + 0.4f; SendHolders(); } if (!HostActive || !CoopSession.IsInGame || Time.unscaledTime < nextState) { return; } nextState = Time.unscaledTime + 0.25f; foreach (string item in new HashSet(from c in CoopSession.ReadyClients select PlayerSync.Get(c)?.Scene into s where !string.IsNullOrEmpty(s) select s)) { WorldData worldData = MainGame.WorldData; GameSceneData val = ((worldData != null) ? worldData.GetGameSceneDataById(item) : null); if (val == null) { continue; } foreach (WgoData item2 in val.wgoDataList.ToList()) { CraftComponent val2 = ((item2 != null) ? item2.CraftComponent : null); if (val2 == null || item2.UniqueId == (SGuid)null) { continue; } string id = item2.UniqueId.Id; int num = Signature(val2); int value; bool flag = sentSignatures.TryGetValue(id, out value); if (!flag || value != num) { sentSignatures[id] = num; if (flag || QueueField.Invoke(val2).Count != 0 || (int)val2.Status != 0) { SendState(item2, val2, num); } } } } } private static int Signature(CraftComponent component) { int num = 17; num = num * 31 + (int)StatusField.Invoke(component); num = num * 31 + CurIdxField.Invoke(component); num = num * 31 + ZombieTicksField.Invoke(component); num = num * 31 + Mathf.FloorToInt(TickTimeField.Invoke(component) * 2f); num = num * 31 + (PreFinishField.Invoke(component) ? 1 : 0); foreach (CraftElementBase item in QueueField.Invoke(component)) { num = num * 31 + ((item != null) ? item.CraftId?.GetHashCode() : ((int?)null)).GetValueOrDefault(); num = num * 31 + ((item != null) ? item.Count : 0); num = num * 31 + ((item != null && item.IsStarted) ? 1 : 0); num = num * 31 + ((item != null) ? item.ProgressTicks : 0); } return num; } private unsafe static void SendState(WgoData station, CraftComponent component, int signature) { //IL_00eb: 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_00f1: 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_00fe: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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) CraftState craftState = new CraftState { queue = QueueField.Invoke(component).ToList(), curIdx = CurIdxField.Invoke(component), status = (int)StatusField.Invoke(component), prevQueueCount = PrevQueueCountField.Invoke(component), restartQueueTimer = RestartTimerField.Invoke(component), autoCraftTickDuration = TickDurationField.Invoke(component), currentAutoCraftTickTime = TickTimeField.Invoke(component), zombieSubTicks = ZombieTicksField.Invoke(component), hasPreFinishUpdate = PreFinishField.Invoke(component), finishHeldTimer = FinishHeldField.Invoke(component) }; byte[] bytes; try { bytes = SerializationUtility.SerializeValue(craftState, (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Packing " + ((ObjectLinkedToDefinition)(object)station).id + "'s craft state failed", e); return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 20; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, station.UniqueId.Id); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref signature, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, bytes); CoopMessages.SendToReadyClients("GK2Coop.Crafts", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } private static void ApplyState(CraftComponent component, CraftState state) { //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Invalid comparison between I4 and Unknown //IL_01d2: 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) ICraftable val = CraftableField.Invoke(component); List list = QueueField.Invoke(component).ToList(); List list2 = state.queue ?? new List(); foreach (CraftElementBase item in list2) { if (item != null) { item.BindCraftable(val); } } foreach (CraftElementBase item2 in list) { Raise(component, "OnCraftRemovedFromQueue", item2); } QueueField.Invoke(component) = list2; CurIdxField.Invoke(component) = state.curIdx; PrevQueueCountField.Invoke(component) = state.prevQueueCount; RestartTimerField.Invoke(component) = state.restartQueueTimer; TickDurationField.Invoke(component) = state.autoCraftTickDuration; TickTimeField.Invoke(component) = state.currentAutoCraftTickTime; ZombieTicksField.Invoke(component) = state.zombieSubTicks; PreFinishField.Invoke(component) = state.hasPreFinishUpdate; FinishHeldField.Invoke(component) = state.finishHeldTimer; foreach (CraftElementBase item3 in list2) { Raise(component, "OnCraftAddedToQueue", item3); } CraftComponentStatus val2 = (CraftComponentStatus)state.status; ApplyingRemote = true; try { if ((int)StatusField.Invoke(component) != (int)val2) { AccessTools.Property(typeof(CraftComponent), "Status").SetValue(component, val2); } else { Raise(component, "OnStatusChanged", val2); } } finally { ApplyingRemote = false; } Raise(component, "OnCurCraftIndexUpdate"); CraftElementBase currentCraftElement = component.CurrentCraftElement; if (currentCraftElement != null) { Raise(component, "OnCraftCurProgressNormalizedChanged", currentCraftElement.ProgressTimeNormalized); } Raise(component, "OnZombieSubTicksChanged", state.zombieSubTicks); } private static void Raise(CraftComponent component, string eventName, params object[] args) { try { (AccessTools.Field(typeof(CraftComponent), eventName)?.GetValue(component) as Delegate)?.DynamicInvoke(args); } catch (Exception ex) { CoopLog.Debug("Raising " + eventName + " on a mirrored station failed: " + (ex.InnerException ?? ex).Message); } } public static string Describe() { return $"stationsSent={sentSignatures.Count} applied={appliedSignatures.Count} prepaidPools={prepaid.Count((KeyValuePair> p) => p.Value.Count > 0)} sent={Sent} recv={Applied}"; } } internal static class DropSync { private class Flight { public GameObject Visual; public Transform Target; public float Started; public float Speed; } private class Glide { public DropData Drop; public string Scene; public Vector3 Target; } private delegate void WriteBody(FastBufferWriter writer); private const byte OpAdd = 1; private const byte OpRemove = 2; private static readonly FieldRef DropTypeField = AccessTools.FieldRefAccess("dropType"); private const byte OpMove = 3; private const byte OpCount = 4; private const float MoveCheckInterval = 0.1f; private const ulong NoCollector = ulong.MaxValue; private static int localCollecting; private static ulong? forwardedCollector; private static readonly Dictionary lastCounts = new Dictionary(); private static readonly List flights = new List(); private static readonly Dictionary gliding = new Dictionary(); private const float ClientReach = 6f; private static readonly Dictionary lastPositions = new Dictionary(); private static float nextMoveCheck; public static int Sent; public static int Applied; private static bool ClientCanForward { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { return !WorldSync.ApplyingRemote; } return false; } } public static void BeginLocalCollect() { localCollecting++; } public static void EndLocalCollect() { localCollecting = Math.Max(0, localCollecting - 1); } public static void Reset() { Sent = 0; Applied = 0; lastPositions.Clear(); lastCounts.Clear(); gliding.Clear(); localCollecting = 0; forwardedCollector = null; foreach (Flight flight in flights) { if ((Object)(object)flight.Visual != (Object)null) { Object.Destroy((Object)(object)flight.Visual); } } flights.Clear(); } public static void Tick() { //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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_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) //IL_019b: 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_019e: 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_01cb: 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_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) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) TickFlights(); bool flag = CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && CoopSession.IsInGame; bool clientCanForward = ClientCanForward; if (!flag && !clientCanForward) { return; } GlideDrops(); if (Time.unscaledTime < nextMoveCheck) { return; } nextMoveCheck = Time.unscaledTime + 0.1f; HashSet hashSet = new HashSet(); if (flag) { foreach (ulong readyClient in CoopSession.ReadyClients) { string text = PlayerSync.Get(readyClient)?.Scene; if (!string.IsNullOrEmpty(text)) { hashSet.Add(text); } } } else { PlayerController playerController = MainGame.PlayerController; if (!string.IsNullOrEmpty((playerController == null) ? null : playerController.PlayerData?.currentGameSceneId)) { hashSet.Add(MainGame.PlayerController.PlayerData.currentGameSceneId); } } PlayerController playerController2 = MainGame.PlayerController; Vector3 val = (((Object)(object)((playerController2 != null) ? playerController2.PhysicalBody : null) != (Object)null) ? ((Component)MainGame.PlayerController.PhysicalBody).transform.position : Vector3.zero); foreach (string item in hashSet) { WorldData worldData = MainGame.WorldData; GameSceneData val2 = ((worldData != null) ? worldData.GetGameSceneDataById(item) : null); if (val2 == null) { continue; } foreach (DropData item2 in val2.droppedItems.ToList()) { if (!(((item2 != null) ? item2.UniqueId : null) == (SGuid)null)) { string id = item2.UniqueId.Id; Vector3 position = item2.Position; Vector3 val3; int num; if (!flag) { val3 = position - val; num = ((((Vector3)(ref val3)).sqrMagnitude <= 36f) ? 1 : 0); } else { num = 1; } bool flag2 = (byte)num != 0; int num2; if (lastPositions.TryGetValue(id, out var value)) { val3 = value - position; num2 = ((((Vector3)(ref val3)).sqrMagnitude > 0.0025f) ? 1 : 0); } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { SendMove(item, id, position, null); } lastPositions[id] = position; int count = item2.Count; if (lastCounts.TryGetValue(id, out var value2) && value2 != count && count > 0 && flag2) { SendCount(item, id, count, null); } lastCounts[id] = count; } } } } private unsafe static void SendMove(string sceneId, string uid, Vector3 position, ulong? except) { //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_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_0010: 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_002e: 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_0041: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, sceneId); CoopMessages.WriteString(ref writer, uid); CoopMessages.WriteVector3(ref writer, position); if (CoopSession.Role == CoopRole.Host) { CoopMessages.SendToReadyClients("GK2Coop.Drops", writer, (NetworkDelivery)4, except); } else { CoopMessages.SendToHost("GK2Coop.Drops", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void SendCount(string sceneId, string uid, int count, ulong? except) { //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_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_0010: 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_0032: 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_005a: 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) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, sceneId); CoopMessages.WriteString(ref writer, uid); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Host) { CoopMessages.SendToReadyClients("GK2Coop.Drops", writer, (NetworkDelivery)4, except); } else { CoopMessages.SendToHost("GK2Coop.Drops", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } private static void FlyToCollector(string sceneId, DropData drop, ulong collector) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00b1: 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) if (collector == ulong.MaxValue || collector == CoopNetwork.LocalClientId || ((drop != null) ? drop.Item : null) == null) { return; } RemotePlayer remotePlayer = PlayerSync.Get(collector); if ((Object)(object)remotePlayer?.Body == (Object)null || !remotePlayer.Visible) { return; } try { GameScene val = ((IEnumerable)LazySingleton.Instance.LoadedGameScenes).FirstOrDefault((Func)((GameScene s) => (Object)(object)s != (Object)null && s.Id == sceneId)); DropView val2 = default(DropView); if (!((Object)(object)val == (Object)null) && val.TryGetDropView(drop.Item, ref val2) && !((Object)(object)val2 == (Object)null)) { GameObject val3 = new GameObject("GK2Coop flyaway"); val3.SetActive(false); GameObject val4 = Object.Instantiate(((Component)val2).gameObject, ((Component)val2).transform.position, ((Component)val2).transform.rotation, val3.transform); StripToVisuals(val4); val4.transform.SetParent((Transform)null, true); Object.Destroy((Object)(object)val3); ((Object)val4).name = "GK2Coop flyaway " + drop.Id; flights.Add(new Flight { Visual = val4, Target = ((Component)remotePlayer.Body).transform, Started = Time.unscaledTime, Speed = 1f }); } } catch (Exception ex) { CoopLog.Warn("Couldn't show " + drop.Id + " flying to " + remotePlayer.Label + ": " + ex.Message); } } private static void StripToVisuals(GameObject root) { MonoBehaviour[] componentsInChildren = root.GetComponentsInChildren(true); foreach (MonoBehaviour val in componentsInChildren) { if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)val); } } Joint[] componentsInChildren2 = root.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } Collider[] componentsInChildren3 = root.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } Rigidbody[] componentsInChildren4 = root.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren4.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren4[i]); } Component[] componentsInChildren5 = root.GetComponentsInChildren(true); foreach (Component val2 in componentsInChildren5) { if (!((Object)(object)val2 == (Object)null) && !(val2 is Transform) && !(val2 is Renderer) && !(val2 is MeshFilter) && !(val2 is SortingGroup)) { try { Object.DestroyImmediate((Object)(object)val2); } catch { } } } } private static void TickFlights() { //IL_0086: 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) //IL_0095: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0110: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) for (int num = flights.Count - 1; num >= 0; num--) { Flight flight = flights[num]; if ((Object)(object)flight.Visual == (Object)null) { flights.RemoveAt(num); } else { Transform transform = flight.Visual.transform; bool num2 = Time.unscaledTime - flight.Started > 1.2f || (Object)(object)flight.Target == (Object)null; Vector3 val = (((Object)(object)flight.Target != (Object)null) ? (flight.Target.position + Vector3.up * 0.5f) : transform.position) - transform.position; flight.Speed += 0.2f; float num3 = (3f + flight.Speed) * Time.deltaTime; if (num2 || ((Vector3)(ref val)).magnitude <= Mathf.Max(0.1f, num3) || ((Vector3)(ref val)).magnitude > 8f) { Object.Destroy((Object)(object)flight.Visual); flights.RemoveAt(num); } else { transform.position += ((Vector3)(ref val)).normalized * num3; } } } } private static void ApplyMove(string sceneId, DropData drop, Vector3 position) { //IL_0001: 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_0041: 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) drop.Position = position; lastPositions[drop.UniqueId.Id] = position; gliding[drop.UniqueId.Id] = new Glide { Drop = drop, Scene = sceneId, Target = position }; } private static void GlideDrops() { //IL_00c4: 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_00d2: 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) //IL_00d9: 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_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) //IL_00fc: 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_0134: 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_0120: 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) //IL_0139: 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_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) if (gliding.Count == 0) { return; } float num = 1f - Mathf.Exp(-14f * Time.deltaTime); List loadedGameScenes = LazySingleton.Instance.LoadedGameScenes; DropView val2 = default(DropView); foreach (KeyValuePair item in gliding.ToList()) { Glide glide = item.Value; GameScene val = ((IEnumerable)loadedGameScenes).FirstOrDefault((Func)((GameScene s) => (Object)(object)s != (Object)null && s.Id == glide.Scene)); if ((Object)(object)val == (Object)null || !val.TryGetDropView(glide.Drop.Item, ref val2) || (Object)(object)val2 == (Object)null) { gliding.Remove(item.Key); continue; } Vector3 position = ((Component)val2).transform.position; Vector3 val3 = glide.Target - position; int num2; Vector3 val4; if (!(((Vector3)(ref val3)).sqrMagnitude < 0.0004f)) { val3 = glide.Target - position; num2 = ((((Vector3)(ref val3)).sqrMagnitude > 25f) ? 1 : 0); if (num2 == 0) { val4 = Vector3.Lerp(position, glide.Target, num); goto IL_0139; } } else { num2 = 1; } val4 = glide.Target; goto IL_0139; IL_0139: Vector3 val5 = val4; ((Component)val2).transform.position = val5; Rigidbody component = ((Component)val2).GetComponent(); if ((Object)(object)component != (Object)null) { component.position = val5; component.linearVelocity = Vector3.zero; } glide.Drop.Position = val5; lastPositions[item.Key] = val5; if (num2 != 0) { gliding.Remove(item.Key); } } } public static void OnLocalAdd(string sceneId, DropData drop) { //IL_0111: Unknown result type (might be due to invalid IL or missing references) DropData obj = drop; if (((obj != null) ? obj.Item : null) == null || drop.UniqueId == (SGuid)null || WorldSync.ApplyingRemote) { return; } bool num = CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients; bool flag = ClientCanForward && drop.IsDroppedFromPlayer; if (num || flag) { byte[] item; try { item = SerializationUtility.SerializeValue(drop.Item, (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Serializing drop " + drop.Id + " failed", e); return; } Send(delegate(FastBufferWriter w) { //IL_0008: 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_0040: 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_0058: Expected I4, but got Unknown //IL_005c: 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) byte b = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, sceneId); CoopMessages.WriteString(ref w, drop.UniqueId.Id); CoopMessages.WriteVector3(ref w, drop.Position); int num2 = (int)drop.DropType; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num2, default(ForPrimitives)); CoopMessages.WriteBytes(ref w, item); }); CoopLog.Debug(string.Format("Drop added: {0} x{1} ({2}) in '{3}' at {4}{5}.", drop.Id, drop.Count, drop.UniqueId.Id, sceneId, drop.Position, flag ? " (sent to host)" : "")); } } public static void OnLocalRemove(string sceneId, DropData drop) { DropData obj = drop; if (((obj != null) ? obj.UniqueId : null) == (SGuid)null || WorldSync.ApplyingRemote || ((CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients) && !ClientCanForward)) { return; } if (localCollecting > 0 && !forwardedCollector.HasValue && TutorialSync.Active && CoopSession.Role == CoopRole.Client && TutorialSync.SharedInIntro(drop.Item)) { CoopLog.Debug("Tutorial: picked up our own copy of " + drop.Id + "; the others keep theirs."); return; } ulong collector = (ulong)(((long?)forwardedCollector) ?? ((localCollecting > 0) ? ((long)CoopNetwork.LocalClientId) : (-1L))); Send(delegate(FastBufferWriter w) { //IL_0008: 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_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) byte b = 2; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, sceneId); CoopMessages.WriteString(ref w, drop.UniqueId.Id); ((FastBufferWriter)(ref w)).WriteValueSafe(ref collector, default(ForPrimitives)); }); CoopLog.Debug("Drop removed: " + drop.Id + " (" + drop.UniqueId.Id + ") in '" + sceneId + "'" + ((collector != ulong.MaxValue) ? $", picked up by {collector}" : "") + "."); } private unsafe static void Send(WriteBody body) { //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_0031: 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) FastBufferWriter writer = CoopMessages.NewWriter(); try { body(writer); if (CoopSession.Role == CoopRole.Host) { CoopMessages.SendToReadyClients("GK2Coop.Drops", writer, (NetworkDelivery)3); } else { CoopMessages.SendToHost("GK2Coop.Drops", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } public static void OnDrops(ulong sender, FastBufferReader reader) { //IL_000c: 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_00a1: 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_00ae: 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_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_00da: 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_00e4: Expected O, but got Unknown //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: 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_025b: 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_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); string text = CoopMessages.ReadString(ref reader); string uid = CoopMessages.ReadString(ref reader); bool flag = CoopSession.Role == CoopRole.Client; if (flag && !CoopSession.ClientInWorld) { return; } WorldData worldData = MainGame.WorldData; GameSceneData val = ((worldData != null) ? worldData.GetGameSceneDataById(text) : null); if (val == null) { CoopLog.Debug($"Drop {b} for unknown area '{text}'."); return; } DropData val2 = ((IEnumerable)val.droppedItems).FirstOrDefault((Func)((DropData d) => ((d != null) ? d.UniqueId : null) != (SGuid)null && d.UniqueId.Id == uid)); bool applyingRemote = WorldSync.ApplyingRemote; WorldSync.ApplyingRemote = flag; try { switch (b) { case 1: { Vector3 val3 = CoopMessages.ReadVector3(ref reader); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); byte[] array = CoopMessages.ReadBytes(ref reader); if (val2 == null) { Item val4 = SerializationUtility.DeserializeValue(array, (DataFormat)0, (DeserializationContext)null); DropData val5 = new DropData(val4, val3, text); DropTypeField.Invoke(val5) = (DropType)num2; val.AddDrop(val5); Applied++; CoopLog.Debug(string.Format("Mirrored drop {0} x{1} ({2}) from {3}.", ((ObjectLinkedToDefinition)(object)val4)?.id, (val4 != null) ? new int?(val4.Count) : ((int?)null), uid, flag ? "host" : $"client {sender}")); } break; } case 3: { Vector3 position = CoopMessages.ReadVector3(ref reader); if (val2 != null) { ApplyMove(text, val2, position); if (!flag) { SendMove(text, uid, position, sender); } } break; } case 4: { int num3 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); if (((val2 != null) ? val2.Item : null) != null && num3 > 0 && val2.Count != num3) { val2.Item.Count = num3; val2.NotifyCountChanged(); lastCounts[uid] = num3; Applied++; if (!flag) { SendCount(text, uid, num3, sender); } } break; } case 2: { ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); if (val2 != null) { if (!flag && num != ulong.MaxValue) { num = sender; } if (flag && num == 0L && CoopSession.Role == CoopRole.Client && TutorialSync.Active && val2.Item != null && !val2.IsResDrop && TutorialSync.SharedInIntro(val2.Item)) { object obj = SerializationUtility.CreateCopy((object)val2.Item); TutorialSync.GiveToMe((Item)(((obj is Item) ? obj : null) ?? ((object)new Item(((ObjectLinkedToDefinition)(object)val2.Item).id, val2.Item.Count)))); CoopLog.Info($"Tutorial: the host picked up {((ObjectLinkedToDefinition)(object)val2.Item).id} x{val2.Item.Count}; we get ours too."); } FlyToCollector(text, val2, num); forwardedCollector = (flag ? ((ulong?)null) : new ulong?(num)); try { MainGame.Instance.dropSystem.RemoveDrop(val2, text); } finally { forwardedCollector = null; } Applied++; CoopLog.Debug("Mirrored pickup of " + val2.Id + " (" + uid + ") from " + (flag ? "host" : $"client {sender}") + "."); } break; } } } catch (Exception e) { CoopLog.Error($"Applying drop {b} {uid} failed", e); } finally { WorldSync.ApplyingRemote = applyingRemote; } } } [HarmonyPatch(typeof(PlayerData), "CollectDrop")] internal static class NoteLocalDropPickup { private static void Prefix(PlayerData __instance, out bool __state) { __state = __instance == MainGame.PlayerData; if (__state) { DropSync.BeginLocalCollect(); } } private static Exception Finalizer(Exception __exception, bool __state) { if (__state) { DropSync.EndLocalCollect(); } return __exception; } } [HarmonyPatch(typeof(PlayerData), "CollectResDrop")] internal static class NoteLocalResPickup { private static void Prefix(PlayerData __instance, out bool __state) { __state = __instance == MainGame.PlayerData; if (__state) { DropSync.BeginLocalCollect(); } } private static Exception Finalizer(Exception __exception, bool __state) { if (__state) { DropSync.EndLocalCollect(); } return __exception; } } [HarmonyPatch(typeof(PlayerData), "CollectDrop")] internal static class StoryItemsStayWithHost { private static float lastNotice; private static bool Prefix(DropView dropView) { try { object obj; if (dropView == null) { obj = null; } else { DropData data = dropView.Data; obj = ((data != null) ? data.Item : null); } Item item = (Item)obj; if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && TutorialSync.IsStoryItem(item)) { if (Time.unscaledTime - lastNotice > 3f) { lastNotice = Time.unscaledTime; CoopRunner.Think("That's for " + (PlayerSync.Host?.Label ?? "the host") + " to pick up (it's part of the story)."); } return false; } if (TutorialSync.PartnerSkipsPickup(item)) { if (Time.unscaledTime - lastNotice > 3f) { lastNotice = Time.unscaledTime; CoopRunner.Think("I already have one of these."); } return false; } } catch (Exception ex) { CoopLog.Debug("Checking a pickup failed: " + ex.Message); } return true; } } [HarmonyPatch(typeof(DropSearcher), "TryFindAndMoveDropToCollector")] internal static class DropsGoToOurCharacter { private static bool Prefix(DropSearcher __instance, TriggerColliderComponentLinker linker) { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController == (Object)null || (Object)(object)__instance == (Object)null || CoopSession.Role == CoopRole.None) { return true; } if (CoopSession.Role == CoopRole.Client) { MonoBehaviour obj = ((linker != null) ? linker.Component : null); DropView val = (DropView)(object)((obj is DropView) ? obj : null); if (val != null) { DropData data = val.Data; if (!TutorialSync.IsStoryItem((data != null) ? data.Item : null)) { DropData data2 = val.Data; if (!TutorialSync.PartnerSkipsPickup((data2 != null) ? data2.Item : null)) { goto IL_0072; } } return false; } } goto IL_0072; IL_0072: PlayerPhysicalBody componentInParent = ((Component)__instance).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null)) { return (Object)(object)componentInParent == (Object)(object)playerController.PhysicalBody; } return true; } } internal static class EconomySync { private const byte OpValues = 1; private const byte OpDeltas = 2; private const float PollInterval = 0.25f; private const float ResyncInterval = 8f; private static readonly FieldRef ResField = AccessTools.FieldRefAccess("res"); private static readonly Dictionary lastKnown = new Dictionary(); private static readonly Dictionary ownChangeUntil = new Dictionary(); private static float nextPoll; private static float nextResync; public static int DeltasSent; public static int DeltasApplied; private static readonly HashSet OrbKeys = new HashSet { "tech_red", "tech_green", "tech_blue", "happiness" }; private const int MaxOrbs = 30; private static readonly Dictionary inFlight = new Dictionary(); private static readonly Dictionary flightActivity = new Dictionary(); private static readonly Dictionary flightGeneration = new Dictionary(); private static readonly Dictionary hostValues = new Dictionary(); private static float orbsFrom = float.MaxValue; private static IEnumerable Patterns(string list) { return from p in (list ?? "").Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries) select p.Trim(); } private static bool Matches(string type, string list) { foreach (string item in Patterns(list)) { if (item.StartsWith("*") && type.EndsWith(item.Substring(1), StringComparison.OrdinalIgnoreCase)) { return true; } if (item.EndsWith("*") && type.StartsWith(item.Substring(0, item.Length - 1), StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(item, type, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static bool IsPersonal(string type) { if (type != null) { if (!type.StartsWith("gk2coop_", StringComparison.Ordinal)) { return Matches(type, Plugin.PersonalResources.Value); } return true; } return false; } public static bool IsCurrency(string type) { if (type != null && !IsPersonal(type)) { return Matches(type, Plugin.SharedCurrencies.Value); } return false; } public static void Reset() { lastKnown.Clear(); inFlight.Clear(); flightActivity.Clear(); flightGeneration.Clear(); hostValues.Clear(); orbsFrom = float.MaxValue; nextPoll = 0f; nextResync = 0f; DeltasSent = 0; DeltasApplied = 0; } private static GameRes LocalRes() { PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); if (val == null) { return null; } return ResField.Invoke(val); } private static Dictionary SharedValues(GameRes res) { Dictionary dictionary = new Dictionary(); foreach (string item in res.TypesList.ToList()) { if (!string.IsNullOrEmpty(item) && !IsPersonal(item) && !dictionary.ContainsKey(item)) { dictionary[item] = res.GetWithoutSystemsCheck(item, 0f); } } return dictionary; } public static GameRes BuildPartnerRes(GameRes hostRes, GameRes personal) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) GameRes val = (GameRes)((hostRes != (GameRes)null) ? ((object)hostRes.Clone()) : ((object)new GameRes())); if (personal != (GameRes)null) { foreach (string item in personal.TypesList.ToList()) { if (IsPersonal(item)) { val.SetWithoutSystemsCheck(item, personal.GetWithoutSystemsCheck(item, 0f)); } } } return val; } private static int InFlight(string key) { if (!inFlight.TryGetValue(key, out var value)) { return 0; } return value; } private static Vector3 OrbSource(RemotePlayer who) { //IL_0028: 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_002d: 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_0066: 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_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) //IL_0081: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); Vector3 val2 = (((Object)(object)val != (Object)null) ? ((Component)val).transform.position : Vector3.zero); if (who != null && who.Visible && (Object)(object)who.Body != (Object)null && who.Scene == MainGame.PlayerData?.currentGameSceneId) { Vector3 val3 = who.Position - val2; if (((Vector3)(ref val3)).sqrMagnitude < 900f) { return who.Position; } } return val2; } private static int LaunchOrbs(string key, int count, Vector3 from) { //IL_005b: 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_0093: 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) if (count <= 0 || !OrbKeys.Contains(key) || (Object)(object)LazyUI.Get() == (Object)null) { return 0; } int num = Math.Min(count, 30); flightGeneration.TryGetValue(key, out var generation); int num2 = 0; for (int i = 0; i < num; i++) { Vector3 val = from + new Vector3(Random.Range(-0.4f, 0.4f), Random.Range(0.6f, 1.2f), Random.Range(-0.4f, 0.4f)); try { FlyingTechPoint.Drop(val, key, (Action)delegate { OrbLanded(key, generation); }, -1); num2++; } catch (Exception ex) { CoopLog.Debug("An orb for " + key + " failed to fly: " + ex.Message); break; } } if (num2 > 0) { inFlight[key] = InFlight(key) + num2; flightActivity[key] = Time.unscaledTime; } return num2; } private static void OrbLanded(string key, int generation) { flightGeneration.TryGetValue(key, out var value); if (generation != value) { GK2GameResSystem system = GK2GameResSystem.GetSystem(key); if (system != null) { ((GameResSystemBase)system).Add(-1f, true); } HUD obj = LazyUI.Get(); if (obj != null) { obj.UpdateTechPointsInstant(); } return; } inFlight[key] = Math.Max(0, InFlight(key) - 1); flightActivity[key] = Time.unscaledTime; if (CoopSession.Role == CoopRole.Client) { lastKnown.TryGetValue(key, out var value2); lastKnown[key] = value2 + 1f; if (InFlight(key) == 0) { SettleOnHost(key); } } } private static void SettleOnHost(string key) { PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); if (val == null || !hostValues.TryGetValue(key, out var value) || (ownChangeUntil.TryGetValue(key, out var value2) && Time.unscaledTime < value2)) { return; } GameRes val2 = ResField.Invoke(val); if (!Mathf.Approximately(val2.GetWithoutSystemsCheck(key, 0f), value)) { try { val.SetRes(key, value); } catch { val2.SetWithoutSystemsCheck(key, value); } HUD obj2 = LazyUI.Get(); if (obj2 != null) { obj2.UpdateTechPointsInstant(); } } lastKnown[key] = value; } private static void TickOrbs() { if (inFlight.Count == 0) { return; } foreach (string item in inFlight.Keys.ToList()) { int num = InFlight(item); if (num <= 0 || !flightActivity.TryGetValue(item, out var value) || Time.unscaledTime - value < 8f) { continue; } flightGeneration.TryGetValue(item, out var value2); flightGeneration[item] = value2 + 1; inFlight[item] = 0; try { GK2GameResSystem system = GK2GameResSystem.GetSystem(item); if (system != null) { ((GameResSystemBase)system).Add((float)num, true); } HUD obj = LazyUI.Get(); if (obj != null) { obj.UpdateTechPointsInstant(); } } catch (Exception ex) { CoopLog.Debug($"Adding {num} {item} straight away failed: {ex.Message}"); } CoopLog.Debug($"{num} {item} orb(s) never landed: added straight away."); if (CoopSession.Role == CoopRole.Client) { lastKnown.TryGetValue(item, out var value3); lastKnown[item] = value3 + (float)num; SettleOnHost(item); } } } private static bool ClientOrbs(string key, float value, PlayerData data, GameRes res, out bool changed) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) changed = false; if (!OrbKeys.Contains(key)) { return false; } hostValues[key] = value; float withoutSystemsCheck = res.GetWithoutSystemsCheck(key, 0f); int num = InFlight(key); float num2 = value - (withoutSystemsCheck + (float)num); float value2; bool flag = ownChangeUntil.TryGetValue(key, out value2) && Time.unscaledTime < value2; if (num2 >= 0.5f && !flag && Time.unscaledTime >= orbsFrom) { int count = Mathf.RoundToInt(num2); int num3 = LaunchOrbs(key, count, OrbSource(PlayerSync.Host)); if (num3 > 0) { float num4 = num2 - (float)num3; if (num4 > 0.001f) { SetValue(data, res, key, withoutSystemsCheck + num4); } lastKnown[key] = withoutSystemsCheck + Math.Max(0f, num4); changed = true; return true; } } if (num > 0) { float num5 = Math.Max(0f, value - (float)num); if (!Mathf.Approximately(withoutSystemsCheck, num5)) { SetValue(data, res, key, num5); changed = true; } lastKnown[key] = num5; return true; } return false; } private static void SetValue(PlayerData data, GameRes res, string key, float value) { try { data.SetRes(key, value); } catch { res.SetWithoutSystemsCheck(key, value); } } public unsafe static void Tick() { //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) TickOrbs(); bool flag = CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && CoopSession.IsInGame; bool flag2 = CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld; if ((!flag && !flag2) || Time.unscaledTime < nextPoll) { return; } nextPoll = Time.unscaledTime + 0.25f; GameRes val = LocalRes(); if (val == (GameRes)null) { return; } Dictionary dictionary = SharedValues(val); if (flag) { bool resync = Time.unscaledTime >= nextResync; if (resync) { nextResync = Time.unscaledTime + 8f; } float value2; List> list = dictionary.Where((KeyValuePair p) => resync || !lastKnown.TryGetValue(p.Key, out value2) || !Mathf.Approximately(value2, p.Value)).ToList(); foreach (KeyValuePair item in dictionary) { lastKnown[item.Key] = item.Value; } if (list.Count > 0) { SendValues(list, null); if (!resync) { CoopLog.Debug("Shared values changed on host: " + Format(list)); } } return; } List> list2 = new List>(); foreach (KeyValuePair item2 in dictionary) { if (lastKnown.TryGetValue(item2.Key, out var value) && !Mathf.Approximately(value, item2.Value) && IsCurrency(item2.Key)) { list2.Add(new KeyValuePair(item2.Key, item2.Value - value)); } lastKnown[item2.Key] = item2.Value; } foreach (KeyValuePair item3 in list2) { ownChangeUntil[item3.Key] = Time.unscaledTime + 2f; } if (list2.Count > 0) { FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); WritePairs(ref writer, list2); CoopMessages.SendToHost("GK2Coop.Economy", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } DeltasSent += list2.Count; CoopLog.Info("Sent our currency changes to the host: " + Format(list2)); } } public static void AbsorbLocalChanges() { GameRes val = LocalRes(); if (val == (GameRes)null) { return; } foreach (KeyValuePair item in SharedValues(val)) { lastKnown[item.Key] = item.Value; } } public static void OnClientReady(ulong clientId) { GameRes val = LocalRes(); if (val != (GameRes)null) { SendValues(SharedValues(val).ToList(), clientId); } } private unsafe static void SendValues(List> values, ulong? onlyTo) { //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_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_002c: 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_0064: 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) for (int i = 0; i < values.Count; i += 80) { List> pairs = values.Skip(i).Take(80).ToList(); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); WritePairs(ref writer, pairs); if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Economy", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Economy", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } } public static void OnEconomy(ulong sender, FastBufferReader reader) { //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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); List> list = ReadPairs(ref reader); PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); if (val == null) { return; } if (b == 2 && CoopSession.Role == CoopRole.Host) { string text = PartnerProfiles.NameOf(sender) ?? $"Player {sender}"; foreach (KeyValuePair item in list.Where((KeyValuePair p) => IsCurrency(p.Key))) { GameRes val2 = ResField.Invoke(val); float withoutSystemsCheck = val2.GetWithoutSystemsCheck(item.Key, 0f); try { val.AddRes(item.Key, item.Value); } catch (Exception ex) { CoopLog.Debug("AddRes(" + item.Key + ") threw (" + ex.Message + "); adding without the game's resource system."); val2.SetWithoutSystemsCheck(item.Key, withoutSystemsCheck + item.Value); } DeltasApplied++; } CoopLog.Info(text + " changed shared currencies: " + Format(list)); } else { if (b != 1 || CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld) { return; } if (orbsFrom == float.MaxValue) { orbsFrom = Time.unscaledTime + 3f; } GameRes val3 = ResField.Invoke(val); List> list2 = new List>(); foreach (KeyValuePair item2 in list) { if (IsPersonal(item2.Key)) { continue; } if (ClientOrbs(item2.Key, item2.Value, val, val3, out var changed)) { if (changed) { list2.Add(item2); } continue; } lastKnown[item2.Key] = item2.Value; if (!Mathf.Approximately(val3.GetWithoutSystemsCheck(item2.Key, 0f), item2.Value)) { try { val.SetRes(item2.Key, item2.Value); } catch (Exception ex2) { CoopLog.Debug("SetRes(" + item2.Key + ") threw (" + ex2.Message + "); setting without the game's resource system."); val3.SetWithoutSystemsCheck(item2.Key, item2.Value); } list2.Add(item2); } } if (list2.Count > 0) { CoopLog.Debug("Shared values from host: " + Format(list2)); } } } private static void WritePairs(ref FastBufferWriter writer, List> pairs) { //IL_000c: 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_0044: 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) int count = pairs.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (KeyValuePair pair in pairs) { CoopMessages.WriteString(ref writer, pair.Key); float value = pair.Value; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref value, default(ForPrimitives)); } } private static List> ReadPairs(ref FastBufferReader reader) { //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_0029: 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) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List> list = new List>(num); float value = default(float); for (int i = 0; i < num; i++) { string key = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref value, default(ForPrimitives)); list.Add(new KeyValuePair(key, value)); } return list; } private static string Format(IEnumerable> pairs) { return string.Join(", ", from p in pairs.Take(12) select $"{p.Key}={p.Value:0.##}"); } public static string Describe() { GameRes val = LocalRes(); string text = ((val != (GameRes)null) ? val.GetWithoutSystemsCheck("money", 0f).ToString("0") : "?"); return $"money={text} tracked={lastKnown.Count} deltasSent={DeltasSent} deltasApplied={DeltasApplied}"; } } internal static class EffectSync { private delegate void WriteBody(FastBufferWriter writer); private const byte OpHit = 1; private const byte OpDeath = 2; private const byte OpAnimState = 3; private const byte OpAnimLayer = 4; private const byte OpAnimTrigger = 5; private const byte OpCustomTrigger = 6; private const byte OpPlayerTrigger = 7; private const byte OpObjectTrigger = 8; private const byte OpInteractionEvent = 9; private const byte OpNote = 10; private const byte OpInteractionEventList = 11; private const byte OpAnimController = 12; private const byte OpArmor = 13; private const byte OpSector = 14; private const byte OpDirectorPlay = 15; private const byte OpAttackEmulation = 16; private const byte OpPlayerState = 17; public static bool ApplyingRemote; public static ulong? KillFromClient; public static int Sent; public static int Applied; private static readonly Dictionary pendingDirectors = new Dictionary(); private static bool noteOpen; private static UINotesWindow noteWindow; private static bool noteFromHost; private static bool HostSends { get { if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients) { return !ApplyingRemote; } return false; } } private static bool ClientSends { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && !ApplyingRemote) { return !WorldSync.ApplyingRemote; } return false; } } public static void Reset() { ApplyingRemote = false; KillFromClient = null; noteOpen = false; noteWindow = null; noteFromHost = false; pendingDirectors.Clear(); Sent = 0; Applied = 0; } public static void OnLocalHit(WgoData data, bool isFirstHit) { WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && (HostSends || ClientSends)) { int hp = ((data.HpComponent != null) ? data.HpComponent.Hp : (-1)); int max = ((data.HpComponent != null) ? data.HpComponent.MaxHpValue : (-1)); CombatSync.NoteLocalHit(data); Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_003c: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) byte b = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); b = (isFirstHit ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref hp, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref max, default(ForPrimitives)); }); } } public static void OnLocalDeath(WgoData data) { WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && HostSends) { Send(KillFromClient, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 2; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); }); CoopLog.Debug("Death of " + ((ObjectLinkedToDefinition)(object)data).id + " sent for replay."); } } public static void OnLocalAnimState(WgoData data, AnimationState state) { //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) WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && HostSends) { Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected I4, but got Unknown //IL_0038: 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) byte b = 3; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); int num = (int)state; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); }); } } public static void OnLocalAnimLayer(WgoData data, int layer, float weight) { WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && HostSends) { Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_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_004b: 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) byte b = 4; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); ((FastBufferWriter)(ref w)).WriteValueSafe(ref layer, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref weight, default(ForPrimitives)); }); } } public static void OnLocalAnimTrigger(WgoData data, string trigger, bool persistent) { WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && HostSends) { Send(null, delegate(FastBufferWriter w) { //IL_0013: 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) byte b = (byte)(persistent ? 6 : 5); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); CoopMessages.WriteString(ref w, trigger ?? ""); }); } } public static void OnHostPlayerTrigger(string trigger) { if (HostSends && !string.IsNullOrEmpty(trigger)) { Send(null, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 7; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, ""); CoopMessages.WriteString(ref w, trigger); }); CoopLog.Debug("Host character animation trigger '" + trigger + "' sent."); } } public static void OnHostPlayerState(AnimationState state) { //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_0035: Unknown result type (might be due to invalid IL or missing references) if (HostSends) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown //IL_002e: 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) byte b = 17; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, ""); int num = (int)state; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); }); CoopLog.Debug($"Host character animation state '{state}' sent."); } } public static void OnHostObjectTrigger(GameObject target, string trigger) { if (HostSends && !((Object)(object)target == (Object)null) && !string.IsNullOrEmpty(trigger)) { string path = PathOf(target.transform); Send(null, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 8; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, path); CoopMessages.WriteString(ref w, trigger); }); } } private static string PathOf(Transform transform) { List list = new List(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return "/" + string.Join("/", list); } private unsafe static void Send(ulong? except, WriteBody body) { //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_0029: 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) FastBufferWriter writer = CoopMessages.NewWriter(); try { body(writer); if (CoopSession.Role == CoopRole.Host) { CoopMessages.SendToReadyClients("GK2Coop.Effects", writer, (NetworkDelivery)4, except); } else { CoopMessages.SendToHost("GK2Coop.Effects", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } public static void OnHostInteractionEvent(WgoData data, string eventId, bool isFake, bool add) { if (!HostSends) { return; } WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null) && !string.IsNullOrEmpty(eventId)) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) byte b = 9; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); CoopMessages.WriteString(ref w, eventId); b = (isFake ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (add ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); }); } } public static void OnHostInteractionEventsUsed(WgoData data) { if (!HostSends) { return; } WgoData obj = data; if (((obj != null) ? obj.UniqueId : null) == (SGuid)null) { return; } List events = data.Events?.Where((InteractionEvent e) => e != null).ToList() ?? new List(); Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_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) byte b = 11; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); int count = events.Count; ((FastBufferWriter)(ref w)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (InteractionEvent item in events) { CoopMessages.WriteString(ref w, item.str ?? ""); b = (item.isFake ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); } }); } public static void OnHostAnimController(WgoData data, bool enable) { if (!HostSends) { return; } WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null)) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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) byte b = 12; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); b = (enable ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); }); CoopLog.Debug("Animation controller of " + ((ObjectLinkedToDefinition)(object)data).id + " " + (enable ? "on" : "off") + " sent."); } } public static void OnHostDirectorPlay(WgoData data) { if (!HostSends) { return; } WgoData obj = data; if (!(((obj != null) ? obj.UniqueId : null) == (SGuid)null)) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) byte b = 15; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.UniqueId.Id); }); CoopLog.Debug("Timeline of " + ((ObjectLinkedToDefinition)(object)data).id + " started: sent."); } } public static void OnHostAttackEmulation(bool sword, WgoData attacker, List targets, int action, float delay) { if (!HostSends) { return; } WgoData obj = attacker; if (((obj != null) ? obj.UniqueId : null) == (SGuid)null) { return; } List ids = (from t in targets ?? new List() where ((t != null) ? t.UniqueId : null) != (SGuid)null select t.UniqueId.Id).ToList(); Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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) //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_0069: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) byte b = 16; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, attacker.UniqueId.Id); b = (sword ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref action, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref delay, default(ForPrimitives)); int count = ids.Count; ((FastBufferWriter)(ref w)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item in ids) { CoopMessages.WriteString(ref w, item); } }); } private static bool PlayDirector(string uid) { WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(uid)) : null); Wgo val2 = ((val != null) ? GameScene.GetWgoViewGlobal(val.UniqueId) : null); PlayableDirector val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponentInChildren(true) : null); if ((Object)(object)val3 == (Object)null) { return false; } ChunkedObjectUtility.UpdateFlag((IChunkableObject)(object)val2, (ChunkingIgnoreType)2, true); val3.stopped -= OnTimelineStopped; val3.stopped += OnTimelineStopped; val3.Play(); CoopLog.Info("Playing the timeline of " + ((ObjectLinkedToDefinition)(object)val).id + " (the host's story)."); return true; } private static void OnTimelineStopped(PlayableDirector director) { if (!((Object)(object)director == (Object)null)) { director.stopped -= OnTimelineStopped; Wgo componentInParent = ((Component)director).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { ChunkedObjectUtility.UpdateFlag((IChunkableObject)(object)componentInParent, (ChunkingIgnoreType)2, false); } } } private static void ApplyAttackEmulation(WgoData attacker, bool sword, int action, float delay, List targets) { Wgo val = ((attacker != null) ? GameScene.GetWgoViewGlobal(attacker.UniqueId) : null); if ((Object)(object)val == (Object)null) { return; } AttackComponent componentInChildren = ((Component)val).GetComponentInChildren(); AnimationComponent componentInChildren2 = ((Component)val).GetComponentInChildren(); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null) { return; } if (sword) { SwordAttackEmulationComponent val2 = ((Component)val).GetComponentInChildren(); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)val).gameObject.AddComponent(); val2.Init(componentInChildren, componentInChildren2); } switch (action) { case 0: val2.PerformAttack(); break; case 1: val2.StartLoop(delay); break; default: val2.StopLoop(); break; } } else { BowAttackEmulationComponent val3 = ((Component)val).GetComponentInChildren(); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)val).gameObject.AddComponent(); } val3.Init(componentInChildren, componentInChildren2); val3.SetTargets(targets); switch (action) { case 0: val3.PerformAttack(delay, (Action)null); break; case 1: val3.StartLoop(delay); break; default: val3.StopLoop(); break; } } CoopLog.Debug(string.Format("{0}'s scripted {1} attack ({2}) as on the host.", ((ObjectLinkedToDefinition)(object)attacker).id, sword ? "sword" : "bow", action)); } public static void OnHostArmor(bool enabled, bool withArmor, bool withSword, bool withHelmet) { if (HostSends) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_004f: 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_006c: 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_0089: 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) byte b = 13; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, ""); b = (enabled ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (withArmor ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (withSword ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (withHelmet ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); }); CoopLog.Info("Story " + (enabled ? "equipped" : "took off") + " armor" + (withSword ? " and sword" : "") + ": partners too."); } } private static void ApplyArmor(bool enabled, bool withArmor, bool withSword, bool withHelmet) { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController == (Object)null) { return; } if (enabled) { FightGear.EnsureOwn(playerController, withSword); playerController.SetArmorView(true, (int?)null, withHelmet); if (withSword) { FightGear.DrawWeapon(playerController); } } else { playerController.AttackComponent.UnequipWeapon(); if (withArmor) { playerController.SetArmorView(false, (int?)null, true); } } CoopLog.Info((enabled ? "Equipped" : "Took off") + " the story's armor" + (withSword ? " and sword" : "") + ", as the host."); } public static void OnHostSector(FightingCapturePoint point, TeamType team) { //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 (!HostSends || (Object)(object)point == (Object)null) { return; } FightingGameController instance = LazySingleton.Instance; FightingLevel val = ((instance != null) ? instance.CurrentLevel : null); if ((Object)(object)val == (Object)null) { return; } for (int line = 0; line < val.FightingLines.Count; line++) { List list = val.FightingLines[line]?.sectors; if (list == null) { continue; } int sector; for (sector = 0; sector < list.Count; sector++) { if ((Object)(object)list[sector] != (Object)null && (Object)(object)list[sector].point == (Object)(object)point) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0046: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected I4, but got Unknown //IL_0064: 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) byte b = 14; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, ""); ((FastBufferWriter)(ref w)).WriteValueSafe(ref line, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref sector, default(ForPrimitives)); int num = (int)team; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); }); return; } } } } private static void ApplySector(int line, int sector, int team) { FightingGameController instance = LazySingleton.Instance; FightingLevel val = ((instance != null) ? instance.CurrentLevel : null); FightingCapturePoint val2 = ((!((Object)(object)val != (Object)null) || line < 0 || line >= val.FightingLines.Count || val.FightingLines[line]?.sectors == null || sector < 0 || sector >= val.FightingLines[line].sectors.Count) ? null : val.FightingLines[line].sectors[sector]?.point); if ((Object)(object)val2 == (Object)null) { CoopLog.Debug($"Fight sector {line}/{sector} isn't in our fight."); return; } val2.SetOwnedByTeam((TeamType)team); CoopLog.Debug($"Fight sector {line}/{sector} now belongs to {(object)(TeamType)team} (host)."); } public static void OnHostNote(UINotesWindow window, UINotesWindowData data) { if (HostSends && ((data != null) ? data.NoteItem : null) != null) { noteOpen = true; noteWindow = window; SendNote(((BalanceBaseObject)data.NoteItem).id); CoopLog.Info("Note '" + ((BalanceBaseObject)data.NoteItem).id + "' opened; showing it to partners watching."); } } private static void SendNote(string id) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) byte b = 10; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, ""); CoopMessages.WriteString(ref w, id ?? ""); }); } public static void Tick() { if (pendingDirectors.Count > 0) { foreach (string item in pendingDirectors.Keys.ToList()) { bool flag = false; try { flag = PlayDirector(item); } catch (Exception ex) { CoopLog.Debug("Playing a timeline failed: " + ex.Message); flag = true; } if (flag || Time.unscaledTime > pendingDirectors[item]) { pendingDirectors.Remove(item); } } } if (CoopSession.Role == CoopRole.Host && noteOpen && ((Object)(object)noteWindow == (Object)null || !((LazyWindow)(object)noteWindow).IsShown)) { noteOpen = false; noteWindow = null; if (CoopSession.HasReadyClients) { SendNote(""); } } } private static void ApplyNote(string id) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown if (string.IsNullOrEmpty(id)) { if (noteFromHost) { noteFromHost = false; UINotesWindow window = LazyUI.GetWindow(); if ((Object)(object)window != (Object)null && ((LazyWindow)(object)window).IsShown) { ((LazyWindow)(object)window).Close(); } } } else if (StandIn.Showing) { ItemDef data = ((GameBalanceBase)GameBalance.Me).GetData(id); if (data == null) { CoopLog.Warn("The host opened note '" + id + "', which this game doesn't know."); return; } ((LazyWindow)(object)LazyUI.GetWindow())?.Open(new UINotesWindowData(data)); noteFromHost = true; CoopLog.Info("Showing the host's note '" + id + "'."); } } public static void OnEffects(ulong sender, FastBufferReader reader) { //IL_000c: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_00a7: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_017a: 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_018c: 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_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0312: 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_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0336: 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_084a: Unknown result type (might be due to invalid IL or missing references) //IL_0850: Unknown result type (might be due to invalid IL or missing references) //IL_0b4c: Unknown result type (might be due to invalid IL or missing references) //IL_0b52: Unknown result type (might be due to invalid IL or missing references) //IL_0b5e: Unknown result type (might be due to invalid IL or missing references) //IL_0b64: Unknown result type (might be due to invalid IL or missing references) //IL_06ff: Unknown result type (might be due to invalid IL or missing references) //IL_0705: Unknown result type (might be due to invalid IL or missing references) //IL_0711: 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_0415: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_060c: Unknown result type (might be due to invalid IL or missing references) //IL_0612: Unknown result type (might be due to invalid IL or missing references) //IL_0a03: Unknown result type (might be due to invalid IL or missing references) //IL_0a09: Unknown result type (might be due to invalid IL or missing references) //IL_0a11: 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_0442: Unknown result type (might be due to invalid IL or missing references) //IL_0a5d: Unknown result type (might be due to invalid IL or missing references) //IL_0abd: Unknown result type (might be due to invalid IL or missing references) //IL_0aa5: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); string uid = CoopMessages.ReadString(ref reader); if (CoopSession.Role == CoopRole.Client && !CoopSession.ClientInWorld) { return; } object obj; if (b != 7 && b != 8 && !string.IsNullOrEmpty(uid)) { WorldData worldData = MainGame.WorldData; obj = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(uid)) : null); } else { obj = null; } WgoData val = (WgoData)obj; if (b == 13 && CoopSession.Role == CoopRole.Client) { byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); byte b3 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b3, default(ForPrimitives)); byte b4 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b4, default(ForPrimitives)); byte b5 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b5, default(ForPrimitives)); try { ApplyArmor(b2 != 0, b3 != 0, b4 != 0, b5 != 0); return; } catch (Exception e) { CoopLog.Error("Equipping the story's armor failed", e); return; } } if (b == 15 && CoopSession.Role == CoopRole.Client) { try { if (!PlayDirector(uid)) { pendingDirectors[uid] = Time.unscaledTime + 6f; } return; } catch (Exception ex) { CoopLog.Warn("Playing the host's timeline failed: " + ex.Message); return; } } if (b == 16 && CoopSession.Role == CoopRole.Client) { byte b6 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b6, default(ForPrimitives)); int action = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref action, default(ForPrimitives)); float delay = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref delay, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List list = new List(); for (int i = 0; i < num; i++) { WorldData worldData2 = MainGame.WorldData; WgoData val2 = ((worldData2 != null) ? worldData2.GetWgoData(SGuid.Parse(CoopMessages.ReadString(ref reader))) : null); if (val2 != null) { list.Add(val2); } } ApplyingRemote = true; try { ApplyAttackEmulation(val, b6 != 0, action, delay, list); return; } catch (Exception ex2) { CoopLog.Debug("Replaying a scripted attack failed: " + ex2.Message); return; } finally { ApplyingRemote = false; } } if (b == 14 && CoopSession.Role == CoopRole.Client) { int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); int num3 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); int team = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref team, default(ForPrimitives)); ApplyingRemote = true; try { ApplySector(num2, num3, team); return; } catch (Exception ex3) { CoopLog.Warn($"Applying fight sector {num2}/{num3} failed: {ex3.Message}"); return; } finally { ApplyingRemote = false; } } ApplyingRemote = true; WorldSync.ApplyingRemote = CoopSession.Role == CoopRole.Client; try { switch (b) { case 1: { byte first = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref first, default(ForPrimitives)); int num6 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num6, default(ForPrimitives)); int num7 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num7, default(ForPrimitives)); if (val != null) { CombatSync.ApplyHitHp(val, num6, num7); try { val.NotifyApplyTool(first != 0); } catch (Exception ex4) { CoopLog.Debug("The hit effect on " + ((ObjectLinkedToDefinition)(object)val).id + " failed: " + ex4.Message); } Applied++; } if (CoopSession.Role == CoopRole.Host) { ApplyingRemote = false; int hostHp = ((((val != null) ? val.HpComponent : null) != null) ? val.HpComponent.Hp : num6); int hostMax = ((((val != null) ? val.HpComponent : null) != null) ? val.HpComponent.MaxHpValue : num7); Send(sender, delegate(FastBufferWriter w) { //IL_0008: 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_002b: 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_0041: 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_0057: 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) byte b11 = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b11, default(ForPrimitives)); CoopMessages.WriteString(ref w, uid); ((FastBufferWriter)(ref w)).WriteValueSafe(ref first, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref hostHp, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref hostMax, default(ForPrimitives)); }); } break; } case 11: { if (CoopSession.Role != CoopRole.Client) { break; } int num8 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num8, default(ForPrimitives)); List> list2 = new List>(); byte b9 = default(byte); for (int num9 = 0; num9 < num8; num9++) { string key = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b9, default(ForPrimitives)); list2.Add(new KeyValuePair(key, b9 != 0)); } if (val == null) { break; } bool flag2 = val.Events.Count == list2.Count; int num10 = 0; while (flag2 && num10 < list2.Count) { flag2 = val.Events[num10] != null && val.Events[num10].str == list2[num10].Key && val.Events[num10].isFake == list2[num10].Value; num10++; } if (flag2) { break; } while (val.Events.Count > 0) { int count = val.Events.Count; val.RemoveInteractionEvent(val.Events[0]?.str); if (val.Events.Count == count) { val.Events.RemoveAt(0); } } foreach (KeyValuePair item in list2) { val.AddInteractionEvent(item.Key, item.Value); } Applied++; CoopLog.Debug(((ObjectLinkedToDefinition)(object)val).id + "'s story steps are now [" + string.Join(", ", list2.Select((KeyValuePair x) => x.Key)) + "] (host)."); break; } case 12: if (CoopSession.Role == CoopRole.Client) { byte b10 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b10, default(ForPrimitives)); Wgo val7 = ((val != null) ? GameScene.GetWgoViewGlobal(val.UniqueId) : null); LazyAnimationController val8 = (((Object)(object)val7 != (Object)null) ? ((Component)val7).gameObject.GetComponentInChildren(true) : null); if ((Object)(object)val8 != (Object)null) { ((Behaviour)val8).enabled = b10 != 0; Applied++; } CoopLog.Debug("Animation controller of " + ((ObjectLinkedToDefinition)(object)val)?.id + " " + ((b10 != 0) ? "on" : "off") + " (host)" + (((Object)(object)val8 == (Object)null) ? ": not found here" : "") + "."); } break; case 9: { if (CoopSession.Role != CoopRole.Client) { break; } string eventId = CoopMessages.ReadString(ref reader); byte b7 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b7, default(ForPrimitives)); byte b8 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b8, default(ForPrimitives)); if (val != null) { bool flag = val.Events.Any((InteractionEvent val12) => val12 != null && val12.str == eventId); if (b8 != 0 && !flag) { val.AddInteractionEvent(eventId, b7 != 0); Applied++; } else if (b8 == 0 && flag) { val.RemoveInteractionEvent(eventId); Applied++; } CoopLog.Debug(((b8 != 0) ? "Added" : "Removed") + " interaction event '" + eventId + "' on " + ((ObjectLinkedToDefinition)(object)val).id + " (host)."); } break; } case 10: if (CoopSession.Role == CoopRole.Client) { ApplyNote(CoopMessages.ReadString(ref reader)); } break; case 2: if (CoopSession.Role == CoopRole.Client && val != null) { val.ForceDeath(); Applied++; CoopLog.Debug("Replayed the death of " + ((ObjectLinkedToDefinition)(object)val).id + "."); } break; case 3: if (CoopSession.Role == CoopRole.Client) { int num12 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num12, default(ForPrimitives)); if (val != null) { val.SetStateToAnimator((AnimationState)num12); } Applied++; } break; case 5: if (CoopSession.Role == CoopRole.Client) { string triggerToAnimator = CoopMessages.ReadString(ref reader); if (val != null) { val.SetTriggerToAnimator(triggerToAnimator); } Applied++; } break; case 6: if (CoopSession.Role == CoopRole.Client) { string customAnimationTrigger = CoopMessages.ReadString(ref reader); if (val != null) { val.SetCustomAnimationTrigger(customAnimationTrigger); } Applied++; } break; case 7: { if (CoopSession.Role != CoopRole.Client) { break; } string text = CoopMessages.ReadString(ref reader); object obj2; if (!StandIn.ActsAsHost) { RemotePlayer host = PlayerSync.Host; if (host == null) { obj2 = null; } else { PlayerPhysicalBody body = host.Body; obj2 = ((body != null) ? body.PlayerView : null); } } else { PlayerController playerController = MainGame.PlayerController; obj2 = ((playerController != null) ? playerController.View : null); } PlayerView val3 = (PlayerView)obj2; if (val3 != null) { PlayerAnimation playerAnimation = val3.PlayerAnimation; if (playerAnimation != null) { ((AnimationComponentBase)playerAnimation).SetTrigger(text); } } if (StandIn.ActsAsHost && (Object)(object)val3 != (Object)null) { StandIn.NoteOwnPose(text); } RemotePlayer host2 = PlayerSync.Host; object obj3; if (host2 == null) { obj3 = null; } else { PlayerPhysicalBody body2 = host2.Body; obj3 = ((body2 != null) ? body2.PlayerView : null); } PlayerView val4 = (PlayerView)obj3; if ((Object)(object)val4 != (Object)null && (Object)(object)val4 != (Object)(object)val3) { PlayerAnimation playerAnimation2 = val4.PlayerAnimation; if (playerAnimation2 != null) { ((AnimationComponentBase)playerAnimation2).SetTrigger(text); } } PlayerSync.NoteHostTrigger(text); Applied++; CoopLog.Debug("Host character trigger '" + text + "' on " + (StandIn.ActsAsHost ? "our stand-in" : "the host's character") + "."); break; } case 17: { if (CoopSession.Role != CoopRole.Client) { break; } int num11 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num11, default(ForPrimitives)); AnimationState val9 = (AnimationState)num11; object obj4; if (!StandIn.ActsAsHost) { RemotePlayer host3 = PlayerSync.Host; if (host3 == null) { obj4 = null; } else { PlayerPhysicalBody body3 = host3.Body; obj4 = ((body3 != null) ? body3.PlayerView : null); } } else { PlayerController playerController2 = MainGame.PlayerController; obj4 = ((playerController2 != null) ? playerController2.View : null); } PlayerView val10 = (PlayerView)obj4; if (val10 != null) { PlayerAnimation playerAnimation3 = val10.PlayerAnimation; if (playerAnimation3 != null) { ((AnimationComponentBase)playerAnimation3).SetState(val9); } } RemotePlayer host4 = PlayerSync.Host; object obj5; if (host4 == null) { obj5 = null; } else { PlayerPhysicalBody body4 = host4.Body; obj5 = ((body4 != null) ? body4.PlayerView : null); } PlayerView val11 = (PlayerView)obj5; if ((Object)(object)val11 != (Object)null && (Object)(object)val11 != (Object)(object)val10) { PlayerAnimation playerAnimation4 = val11.PlayerAnimation; if (playerAnimation4 != null) { ((AnimationComponentBase)playerAnimation4).SetState(val9); } } Applied++; CoopLog.Debug(string.Format("Host character state '{0}' on {1}.", val9, StandIn.ActsAsHost ? "our stand-in" : "the host's character")); break; } case 8: if (CoopSession.Role == CoopRole.Client) { string trigger = CoopMessages.ReadString(ref reader); GameObject val5 = GameObject.Find(uid); Animator val6 = (((Object)(object)val5 != (Object)null) ? val5.GetComponent() : null); if ((Object)(object)val6 != (Object)null) { val6.SetTrigger(trigger); Applied++; } } break; case 4: if (CoopSession.Role == CoopRole.Client) { int num4 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num4, default(ForPrimitives)); float num5 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num5, default(ForPrimitives)); if (val != null) { val.SetLayerWeightToAnimator(num4, num5); } Applied++; } break; case 13: case 14: case 15: case 16: break; } } catch (Exception e2) { CoopLog.Error($"Applying effect {b} on {((ObjectLinkedToDefinition)(object)val)?.id ?? uid} failed", e2); } finally { ApplyingRemote = false; WorldSync.ApplyingRemote = false; } } public static string Describe() { return $"sent={Sent} applied={Applied}"; } } [HarmonyPatch(typeof(WgoData), "NotifyApplyTool")] internal static class MirrorToolHit { private static void Postfix(WgoData __instance, bool isFirstHit) { EffectSync.OnLocalHit(__instance, isFirstHit); } } [HarmonyPatch(typeof(WgoData), "ForceDeath")] internal static class MirrorDeathVisual { private static void Prefix(WgoData __instance) { EffectSync.OnLocalDeath(__instance); } } [HarmonyPatch(typeof(WgoData), "HandleDeath")] internal static class MirrorCombatDeathVisual { private static void Prefix(WgoData __instance) { EffectSync.OnLocalDeath(__instance); } } [HarmonyPatch(typeof(WgoData), "SetStateToAnimator")] internal static class MirrorAnimatorState { private static void Postfix(WgoData __instance, AnimationState animationState) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) EffectSync.OnLocalAnimState(__instance, animationState); } } [HarmonyPatch(typeof(WgoData), "SetLayerWeightToAnimator")] internal static class MirrorAnimatorLayer { private static void Postfix(WgoData __instance, int layerIndex, float weight) { EffectSync.OnLocalAnimLayer(__instance, layerIndex, weight); } } [HarmonyPatch(typeof(WgoData), "SetTriggerToAnimator")] internal static class MirrorAnimatorTrigger { private static void Postfix(WgoData __instance, string triggerName) { EffectSync.OnLocalAnimTrigger(__instance, triggerName, persistent: false); } } [HarmonyPatch(typeof(WgoData), "SetCustomAnimationTrigger")] internal static class MirrorAnimatorCustomTrigger { private static void Postfix(WgoData __instance, string triggerName) { EffectSync.OnLocalAnimTrigger(__instance, triggerName, persistent: true); } } [HarmonyPatch(typeof(Flow_SetTriggerToAnimator), "SetTrigger")] internal static class MirrorStoryAnimatorTrigger { private static readonly FieldRef> TriggerField = AccessTools.FieldRefAccess>("triggerName"); private static readonly FieldRef> ObjectField = AccessTools.FieldRefAccess>("gameObject"); private static readonly FieldRef> StateField = AccessTools.FieldRefAccess>("animationState"); private static void Prefix(Flow_SetTriggerToAnimator __instance) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_003a: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Invalid comparison between Unknown and I4 try { if (__instance.setState && (int)__instance.animatorType == 2) { ValueInput val = StateField.Invoke(__instance); if (val != null) { EffectSync.OnHostPlayerState(val.value); } } else if (!__instance.setState && (int)__instance.animatorType != 0) { string trigger = TriggerField.Invoke(__instance)?.value; if ((int)__instance.animatorType == 2) { EffectSync.OnHostPlayerTrigger(trigger); return; } ValueInput val2 = ObjectField.Invoke(__instance); EffectSync.OnHostObjectTrigger((val2 != null && (Object)(object)val2.value != (Object)null) ? val2.value : null, trigger); } } catch (Exception ex) { CoopLog.Debug("Couldn't mirror a story animation trigger: " + ex.Message); } } } [HarmonyPatch(typeof(WgoData), "AddInteractionEvent")] internal static class MirrorInteractionEventAdded { private static void Postfix(WgoData __instance, string id, bool isFake) { try { EffectSync.OnHostInteractionEvent(__instance, id, isFake, add: true); } catch (Exception ex) { CoopLog.Debug("Mirroring an interaction event failed: " + ex.Message); } } } [HarmonyPatch(typeof(WgoData), "RemoveInteractionEvent")] internal static class MirrorInteractionEventRemoved { private static void Postfix(WgoData __instance, string id) { try { EffectSync.OnHostInteractionEvent(__instance, id, isFake: false, add: false); } catch (Exception ex) { CoopLog.Debug("Mirroring an interaction event failed: " + ex.Message); } } } [HarmonyPatch(typeof(UINotesWindow), "Draw", new Type[] { typeof(UINotesWindowData) })] internal static class MirrorNote { private static void Postfix(UINotesWindow __instance, UINotesWindowData data) { try { EffectSync.OnHostNote(__instance, data); } catch (Exception ex) { CoopLog.Debug("Mirroring a note failed: " + ex.Message); } } } [HarmonyPatch(typeof(WgoData), "FireInteractionEvent")] internal static class MirrorInteractionEventUsed { private static void Postfix(WgoData __instance, bool __runOriginal) { if (!__runOriginal || CoopSession.Role != CoopRole.Host) { return; } try { EffectSync.OnHostInteractionEventsUsed(__instance); } catch (Exception ex) { CoopLog.Debug("Mirroring a used story step failed: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_EnableLazyAnimationController), "Toggle")] internal static class MirrorAnimController { private static readonly MethodInfo GetWgoDataMethod = AccessTools.Method(typeof(GKCustomFlowNodeWithWgoData), "GetWgoData", (Type[])null, (Type[])null); private static void Prefix(Flow_EnableLazyAnimationController __instance) { try { object? obj = GetWgoDataMethod?.Invoke(__instance, null); EffectSync.OnHostAnimController((WgoData)((obj is WgoData) ? obj : null), __instance.isEnable); } catch (Exception ex) { CoopLog.Debug("Mirroring an animation controller failed: " + ex.Message); } } } [HarmonyPatch(typeof(PlayableDirector), "Play", new Type[] { })] internal static class MirrorObjectTimeline { private static void Postfix(PlayableDirector __instance) { if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || (Object)(object)__instance == (Object)null) { return; } try { Wgo componentInParent = ((Component)__instance).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && componentInParent.Data != null) { EffectSync.OnHostDirectorPlay(componentInParent.Data); } } catch (Exception ex) { CoopLog.Debug("Mirroring a timeline failed: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_EmulateBowAttack), "Execute")] internal static class MirrorBowAttackEmulation { private static readonly FieldRef> AttackerField = AccessTools.FieldRefAccess>("attackerWgo"); private static readonly FieldRef>> TargetsField = AccessTools.FieldRefAccess>>("targets"); private static readonly FieldRef> DelayField = AccessTools.FieldRefAccess>("delay"); private static void Prefix(Flow_EmulateBowAttack __instance) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected I4, but got Unknown if (CoopSession.Role != CoopRole.Host) { return; } try { ValueInput val = DelayField.Invoke(__instance); EffectSync.OnHostAttackEmulation(sword: false, AttackerField.Invoke(__instance)?.value, TargetsField.Invoke(__instance)?.value, (int)__instance.action, val?.value ?? 0f); } catch (Exception ex) { CoopLog.Debug("Mirroring a scripted bow attack failed: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_EmulateSwordAttack), "Execute")] internal static class MirrorSwordAttackEmulation { private static readonly FieldRef> AttackerField = AccessTools.FieldRefAccess>("targetWgo"); private static readonly FieldRef> DelayField = AccessTools.FieldRefAccess>("delay"); private static void Prefix(Flow_EmulateSwordAttack __instance) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected I4, but got Unknown if (CoopSession.Role != CoopRole.Host) { return; } try { ValueInput val = DelayField.Invoke(__instance); EffectSync.OnHostAttackEmulation(sword: true, AttackerField.Invoke(__instance)?.value, null, (int)__instance.action, val?.value ?? 0f); } catch (Exception ex) { CoopLog.Debug("Mirroring a scripted sword attack failed: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_SetPlayerArmorState), "SetArmorState")] internal static class MirrorStoryArmor { private static void Prefix(Flow_SetPlayerArmorState __instance) { try { EffectSync.OnHostArmor(__instance.isEnabled, __instance.withArmor, __instance.withSword, __instance.withHelmet); } catch (Exception ex) { CoopLog.Debug("Mirroring the story's armor failed: " + ex.Message); } } } [HarmonyPatch(typeof(FightingCapturePoint), "SetOwnedByTeam")] internal static class MirrorFightSector { private static void Postfix(FightingCapturePoint __instance, TeamType team) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || EffectSync.ApplyingRemote) { return; } try { EffectSync.OnHostSector(__instance, team); } catch (Exception ex) { CoopLog.Debug("Mirroring a fight sector failed: " + ex.Message); } } } [HarmonyPatch(typeof(PlayerInteractionComponent), "Init")] internal static class SkipPuppetInteractionInit { private static bool Prefix(PlayerInteractionComponent __instance) { return !PlayerSync.IsAvatarTransform(((Component)__instance).transform); } } [HarmonyPatch(typeof(Flow_Body), "DoAction")] internal static class HostRollsBodies { private static readonly FieldRef FailPort = AccessTools.FieldRefAccess("fail"); private static bool Prefix(Flow_Body __instance, Flow flow) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!NpcSync.ClientFollowsHost) { return true; } CoopLog.Debug("Skipped our own body delivery roll; the host's game delivers bodies."); try { FailPort.Invoke(__instance).Call(flow); } catch (Exception e) { CoopLog.Error("Skipping the body roll failed", e); } return false; } } internal static class KnowledgeSync { public enum Kind : byte { RevealTech = 1, Craft, Building, TownBuilding, AlchemyFormula } private delegate void WriteBody(FastBufferWriter writer); private const byte OpTechUnlocked = 1; private const byte OpBuyTech = 2; private const byte OpLearned = 3; private const byte OpBuyFailed = 4; public static bool ApplyingRemote; private static int unlockDepth; private static int revealDepth; public static int Sent; public static int Applied; private static bool HostBroadcasts { get { if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients) { return !ApplyingRemote; } return false; } } public static void Reset() { ApplyingRemote = false; unlockDepth = 0; revealDepth = 0; Sent = 0; Applied = 0; } public static bool BeforeTechUnlock(TechDef def, bool free, out bool counted) { counted = false; if (def == null) { return true; } if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && revealDepth > 0) { CoopLog.Info("Tech '" + ((BalanceBaseObject)def).id + "' unlocks with its reveal: the host's game does that."); return false; } if (ApplyingRemote) { return true; } if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && !free) { Send(null, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 2; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, ((BalanceBaseObject)def).id); }); CoopLog.Info("Asked the host to buy tech '" + ((BalanceBaseObject)def).id + "'."); return false; } unlockDepth++; counted = true; return true; } public static void AfterTechUnlock(TechDef def, bool counted) { if (counted) { unlockDepth = Math.Max(0, unlockDepth - 1); } if (def != null && HostBroadcasts && counted) { BroadcastTech(((BalanceBaseObject)def).id, null); CoopLog.Info("Tech '" + ((BalanceBaseObject)def).id + "' unlocked on the host; sent to everyone."); } } private static void BroadcastTech(string id, ulong? onlyTo) { Send(onlyTo, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, id); }); } public static void BeginReveal() { revealDepth++; } public static void EndReveal() { revealDepth = Math.Max(0, revealDepth - 1); } public static void OnLearned(Kind kind, string id) { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && !ApplyingRemote && unlockDepth == 0 && !string.IsNullOrEmpty(id) && (kind != Kind.RevealTech || revealDepth <= 1)) { Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_0021: 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) byte b = 3; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (byte)kind; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, id); }); CoopLog.Info($"We learned {kind} '{id}'; sent to the host."); } else if (HostBroadcasts && unlockDepth <= 0 && !string.IsNullOrEmpty(id) && (kind != Kind.RevealTech || revealDepth <= 1)) { Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_0021: 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) byte b = 3; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (byte)kind; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, id); }); CoopLog.Info($"Host learned {kind} '{id}'; sent to everyone."); } } private unsafe static void Send(ulong? onlyTo, WriteBody body) { //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_001a: 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_0038: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter writer = CoopMessages.NewWriter(); try { body(writer); if (CoopSession.Role == CoopRole.Client) { CoopMessages.SendToHost("GK2Coop.Knowledge", writer, (NetworkDelivery)3); } else if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Knowledge", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Knowledge", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } public static void OnKnowledge(ulong sender, FastBufferReader reader) { //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_006c: 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_0097: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 2: if (CoopSession.Role == CoopRole.Host) { HostBuy(sender, CoopMessages.ReadString(ref reader)); } break; case 1: if (CoopSession.Role == CoopRole.Client) { ClientApplyTech(CoopMessages.ReadString(ref reader)); } break; case 3: if (CoopSession.Role == CoopRole.Client) { byte kind = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref kind, default(ForPrimitives)); ClientApplyLearned((Kind)kind, CoopMessages.ReadString(ref reader)); } else if (CoopSession.Role == CoopRole.Host) { byte kind2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref kind2, default(ForPrimitives)); HostLearn(sender, (Kind)kind2, CoopMessages.ReadString(ref reader)); } break; case 4: if (CoopSession.Role == CoopRole.Client) { string text = CoopMessages.ReadString(ref reader); string text2 = CoopMessages.ReadString(ref reader); CoopRunner.Think("Couldn't unlock " + TechName(text) + ": " + text2 + "."); CoopLog.Info("Host refused tech '" + text + "': " + text2); } break; } } private static void HostLearn(ulong sender, Kind kind, string id) { MainGame instance = MainGame.Instance; KnowledgeSystem val = ((instance == null) ? null : instance.GameSave?.knowledgeSystem); if (val == null || string.IsNullOrEmpty(id)) { return; } string arg = PartnerProfiles.NameOf(sender) ?? $"Player {sender}"; try { switch (kind) { case Kind.RevealTech: val.RevealTech(id); break; case Kind.Craft: val.UnlockCraft(id); break; case Kind.Building: val.UnlockBuilding(id); break; case Kind.TownBuilding: val.UnlockTownBuilding(id); break; case Kind.AlchemyFormula: val.UnlockAlchemyFormula(id); break; } CoopLog.Info($"{arg} learned {kind} '{id}' in their game: learned here and shared."); } catch (Exception e) { CoopLog.Error($"Learning {arg}'s {kind} '{id}' failed", e); } } private static void HostBuy(ulong sender, string id) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Invalid comparison between Unknown and I4 //IL_00ab: Unknown result type (might be due to invalid IL or missing references) string text = PartnerProfiles.NameOf(sender) ?? $"Player {sender}"; TechDef dataOrNull = ((GameBalanceBase)GameBalance.Me).GetDataOrNull(id); KnowledgeSystem knowledgeSystem = MainGame.Instance.GameSave.knowledgeSystem; string problem = null; if (dataOrNull == null) { problem = "that tech doesn't exist in the host's game"; } else { if (knowledgeSystem.IsTechUnlocked(id)) { BroadcastTech(id, sender); CoopLog.Info(text + " asked for tech '" + id + "', which is already unlocked."); return; } if ((int)dataOrNull.TechState != 2) { problem = $"not available yet ({dataOrNull.TechState})"; } else if (!dataOrNull.EnoughResources) { problem = "not enough shared tech points"; } } if (problem != null) { Send(sender, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 4; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, id); CoopMessages.WriteString(ref w, problem); }); CoopLog.Info("Refused " + text + "'s purchase of tech '" + id + "': " + problem); } else { dataOrNull.Unlock(false); CoopLog.Info(text + " bought tech '" + id + "' with the shared points."); RefreshOpenTechTree(); } } private static void ClientApplyTech(string id) { MainGame instance = MainGame.Instance; KnowledgeSystem val = ((instance == null) ? null : instance.GameSave?.knowledgeSystem); TechDef dataOrNull = ((GameBalanceBase)GameBalance.Me).GetDataOrNull(id); if (val == null || dataOrNull == null || val.IsTechUnlocked(id)) { return; } ApplyingRemote = true; List expressionsAfterUnlock = dataOrNull.expressionsAfterUnlock; try { dataOrNull.expressionsAfterUnlock = PartnerReplay.WithoutSharedWorld(expressionsAfterUnlock, "Tech '" + id + "'"); dataOrNull.Unlock(true); Applied++; } catch (Exception e) { CoopLog.Error("Applying tech '" + id + "' failed", e); } finally { dataOrNull.expressionsAfterUnlock = expressionsAfterUnlock; ApplyingRemote = false; EconomySync.AbsorbLocalChanges(); } CoopLog.Info("Applied tech '" + id + "' from the host."); RefreshOpenTechTree(); } public static void RefreshOpenTechTree() { try { TechTreePageWidget[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (TechTreePageWidget obj in array) { string[] array2 = new string[3] { "RedrawSpheres", "UpdateElements", "UpdateConnectors" }; foreach (string text in array2) { AccessTools.Method(typeof(TechTreePageWidget), text, (Type[])null, (Type[])null)?.Invoke(obj, null); } } } catch (Exception ex) { CoopLog.Debug("Refreshing the open tech tree failed: " + ex.Message); } } private static void ClientApplyLearned(Kind kind, string id) { MainGame instance = MainGame.Instance; KnowledgeSystem val = ((instance == null) ? null : instance.GameSave?.knowledgeSystem); if (val == null) { return; } ApplyingRemote = true; try { switch (kind) { case Kind.RevealTech: val.RevealTech(id); break; case Kind.Craft: val.UnlockCraft(id); break; case Kind.Building: val.UnlockBuilding(id); break; case Kind.TownBuilding: val.UnlockTownBuilding(id); break; case Kind.AlchemyFormula: val.UnlockAlchemyFormula(id); break; } Applied++; CoopLog.Debug($"Applied {kind} '{id}' from the host."); } catch (Exception e) { CoopLog.Error($"Applying {kind} '{id}' failed", e); } finally { ApplyingRemote = false; } } private static string TechName(string id) { try { string text = LLBase.L(id); return (string.IsNullOrEmpty(text) || text == id) ? id : text; } catch { return id; } } public static string Describe() { return $"sent={Sent} applied={Applied}"; } } [HarmonyPatch(typeof(TechDef), "Unlock")] internal static class SyncTechUnlock { private static bool Prefix(TechDef __instance, bool free, out bool __state) { return KnowledgeSync.BeforeTechUnlock(__instance, free, out __state); } private static void Postfix(TechDef __instance, bool __state) { KnowledgeSync.AfterTechUnlock(__instance, __state); } } [HarmonyPatch(typeof(KnowledgeSystem), "RevealTech")] internal static class SyncRevealTech { private static void Prefix() { KnowledgeSync.BeginReveal(); } private static void Postfix(string techId) { KnowledgeSync.OnLearned(KnowledgeSync.Kind.RevealTech, techId); } private static void Finalizer() { KnowledgeSync.EndReveal(); } } [HarmonyPatch(typeof(KnowledgeSystem), "UnlockCraft")] internal static class SyncUnlockCraft { private static void Prefix(KnowledgeSystem __instance, string craftId, out bool __state) { __state = !__instance.unlockedCrafts.Contains(craftId); } private static void Postfix(string craftId, bool __state) { if (__state) { KnowledgeSync.OnLearned(KnowledgeSync.Kind.Craft, craftId); } } } [HarmonyPatch(typeof(KnowledgeSystem), "UnlockBuilding")] internal static class SyncUnlockBuilding { private static void Prefix(KnowledgeSystem __instance, string buildingId, out bool __state) { __state = !__instance.unlockedBuildings.Contains(buildingId); } private static void Postfix(string buildingId, bool __state) { if (__state) { KnowledgeSync.OnLearned(KnowledgeSync.Kind.Building, buildingId); } } } [HarmonyPatch(typeof(KnowledgeSystem), "UnlockTownBuilding")] internal static class SyncUnlockTownBuilding { private static void Prefix(KnowledgeSystem __instance, string buildingId, out bool __state) { __state = !__instance.unlockedTownBuildings.Contains(buildingId); } private static void Postfix(string buildingId, bool __state) { if (__state) { KnowledgeSync.OnLearned(KnowledgeSync.Kind.TownBuilding, buildingId); } } } [HarmonyPatch(typeof(KnowledgeSystem), "UnlockAlchemyFormula")] internal static class SyncUnlockAlchemyFormula { private static void Prefix(KnowledgeSystem __instance, string id, out bool __state) { __state = !__instance.unlockedAlchemyFormulas.Contains(id); } private static void Postfix(string id, bool __state) { if (__state) { KnowledgeSync.OnLearned(KnowledgeSync.Kind.AlchemyFormula, id); } } } internal static class Lobby { private static readonly FieldRef StartingNewGameField = AccessTools.FieldRefAccess("startingNewGame"); private const float MaxHold = 120f; private static bool holding; private static bool releasing; private static float heldSince; private static float inGameSince = -1f; private static float nextNotice; private static float releasedAt = -100f; private static List waitingFor = new List(); private static bool introOwed; private static UIMainMenuWindow menu; private static bool menuLocked; private static readonly FieldRef NewGameButton = AccessTools.FieldRefAccess("startNewGameButton"); private static readonly FieldRef ContinueButton = AccessTools.FieldRefAccess("continueGameButton"); private static readonly FieldRef LoadButton = AccessTools.FieldRefAccess("loadGameButton"); private static readonly FieldRef ConsolesButton = AccessTools.FieldRefAccess("consolesGameButton"); public static bool Holding => holding; public static bool PartnerWaiting => CoopSession.ClientWaitingForWorld; public static void Reset() { introOwed |= holding; holding = false; releasing = false; inGameSince = -1f; waitingFor = new List(); } public static bool BeforeNewGameStarts() { if (releasing || CoopSession.Role != CoopRole.Host) { return true; } bool flag = waitingFor.Any((ulong id) => !CoopSession.ReadyClients.Contains(id) && PartnerProfiles.NameOf(id) != null) && Time.unscaledTime - releasedAt < 60f; if (!PartnerProfiles.LobbyWaiting && !flag) { return true; } holding = true; heldSince = Time.unscaledTime; waitingFor = waitingFor.Concat(PartnerProfiles.ReleaseLobby()).Distinct().ToList(); CoopSession.Status = "Your new game is ready. Sending it to your partners; the intro starts when they've loaded."; CoopLog.Info($"New game started with {waitingFor.Count} partner(s) in the lobby: holding the intro until they've loaded."); return false; } public static void Tick() { if (CoopSession.Role != CoopRole.Host) { return; } if (!CoopSession.IsInGame) { inGameSince = -1f; return; } if (inGameSince < 0f) { inGameSince = Time.unscaledTime; } if (!holding && PartnerProfiles.LobbyWaiting && Time.unscaledTime - inGameSince > 3f && !StartingNewGame() && !CoopSession.IsLoadingOverlayShown()) { waitingFor = PartnerProfiles.ReleaseLobby(); releasedAt = Time.unscaledTime; CoopLog.Info($"Game loaded; sending it to {waitingFor.Count} partner(s) from the lobby."); } if (!holding) { return; } List list = waitingFor.Where((ulong id) => !CoopSession.ReadyClients.Contains(id) && PartnerProfiles.NameOf(id) != null).ToList(); bool flag = Time.unscaledTime - heldSince > 120f; if (list.Count == 0 || flag) { holding = false; CoopLog.Info(flag ? "Partners took too long to load; starting the intro anyway." : "Everyone has loaded: starting the intro."); StartIntro(); CoopSession.Status = "Hosting. Everyone is in."; } else if (Time.unscaledTime >= nextNotice) { nextNotice = Time.unscaledTime + 3f; string text = string.Join(", ", list.Select((ulong id) => PartnerProfiles.NameOf(id) ?? $"Player {id}")); CoopRunner.Toast("Waiting for " + text + " to load before the story starts...", 3.2f); } } private static void StartIntro() { releasing = true; try { GlobalEventsSystem.FireTrigger((Type)1, ""); } finally { releasing = false; } } public static void TickAlways() { if (introOwed && CoopSession.Role == CoopRole.None) { introOwed = false; if (CoopSession.IsInGame) { CoopLog.Info("Stopped hosting while the intro waited for partners: starting it now."); StartIntro(); } } TickMenuLock(); } public static void OnMenuOpened(UIMainMenuWindow window) { menu = window; menuLocked = false; TickMenuLock(); } private static void TickMenuLock() { bool partnerWaiting = PartnerWaiting; if ((Object)(object)menu == (Object)null || partnerWaiting == menuLocked || !((Component)menu).gameObject.activeInHierarchy) { return; } menuLocked = partnerWaiting; try { ShowGameButtons(menu, !partnerWaiting); CoopLog.Info(partnerWaiting ? "Hid the main menu's game buttons while we wait for the host." : "Gave the main menu's game buttons back."); } catch (Exception ex) { CoopLog.Warn("Changing the main menu's buttons failed: " + ex.Message); } } private static void ShowGameButtons(UIMainMenuWindow window, bool show) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown bool flag = SaveSystem.GetLastSaveSlot() != null; bool isLimitedSaveSlotsEnabled = SaveSystem.IsLimitedSaveSlotsEnabled; SetActive(NewGameButton.Invoke(window), show && !isLimitedSaveSlotsEnabled); SetActive(ContinueButton.Invoke(window), show && flag); SetActive(LoadButton.Invoke(window), show && flag && !isLimitedSaveSlotsEnabled); SetActive(ConsolesButton.Invoke(window), show && isLimitedSaveSlotsEnabled); UIExtensions.RefreshContentFitterAndDisable((RectTransform)((Component)window).transform); if (LazyInput.IsGamepadActive) { GamepadNavigationController component = ((Component)window).GetComponent(); if (component != null) { component.ReinitItems(true, (List)null, (GamepadNavigationItem)null); } } } private static void SetActive(LazyButton button, bool active) { if ((Object)(object)button != (Object)null) { ((Component)button).gameObject.SetActive(active); } } public static bool RefuseOwnGame() { if (!PartnerWaiting) { return false; } CoopRunner.Toast("You're connected to a host: their game starts here when they start it. To play on your own, press F8 and Disconnect."); CoopLog.Info("Refused to start or load our own game while waiting for the host's."); return true; } private static bool StartingNewGame() { try { return (Object)(object)MainGame.Instance != (Object)null && StartingNewGameField.Invoke(MainGame.Instance); } catch { return false; } } public static string Describe() { return $"lobby={PartnerProfiles.LobbyWaiting} holding={holding}"; } } [HarmonyPatch(typeof(UIMainMenuWindow), "Open")] internal static class LockMenuForPartner { private static void Postfix(UIMainMenuWindow __instance) { try { Lobby.OnMenuOpened(__instance); } catch (Exception e) { CoopLog.Error("Locking the main menu failed", e); } } } [HarmonyPatch] internal static class RefuseOwnGameForPartner { private static IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(UIMainMenuWindow), "OnStartNewGameButtonClicked", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(UIMainMenuWindow), "OnContinueButtonClicked", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(UIMainMenuWindow), "OnLoadButtonClicked", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(UIMainMenuWindow), "OnConsolesGameButtonClicked", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(UISaveSlotsWindow), "OnSaveSlotSelectedHandler", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(UISaveSlotsWindowLimited), "OnSaveSlotSelectedHandler", (Type[])null, (Type[])null); } private static bool Prefix() { try { return !Lobby.RefuseOwnGame(); } catch (Exception e) { CoopLog.Error("Checking a game start from the menu failed", e); return true; } } } [HarmonyPatch(typeof(GlobalEventsSystem), "FireTrigger")] internal static class HoldIntroForLobby { private static bool Prefix(Type type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)type != 1) { return true; } try { return Lobby.BeforeNewGameStarts(); } catch (Exception e) { CoopLog.Error("Holding the intro for the lobby failed", e); return true; } } } [HarmonyPatch(typeof(UILoadingOverlay), "Hide")] internal static class BlackBeforeJoinReveal { private static void Prefix() { if (CoopSession.Role == CoopRole.Client && StandIn.WantsBlackOut) { StandIn.WantsBlackOut = false; StandIn.BlackOut(); CoopLog.Debug("Blacked out the screen until we're next to the host."); } } } internal static class NpcSync { private class SentMover { public Vector3 Position; public Vector2 Direction; public bool Moving; public float Time; } private class Follower { public WgoData Data; public Vector3 Target; public Vector2 Direction; public bool Moving; public bool AppliedMoving; public Vector2 AppliedDirection; public float LastUpdate; } private struct MoverEntry { public string UniqueId; public string Scene; public Vector3 Position; public Vector2 Direction; public bool Moving; } private const byte OpMovers = 1; private const byte OpStoryRoster = 5; private const byte OpZombieAnims = 2; private const float MoverInterval = 0.1f; private const float ZombieAnimInterval = 0.5f; private const float MoverHeartbeat = 1.5f; private const string WispId = "player_wisp"; private static readonly FieldRef MovableField = AccessTools.FieldRefAccess("assignedMovableObject"); private static readonly FieldRef ZombieAnimField = AccessTools.FieldRefAccess("curAnimState"); private static readonly Dictionary sentMovers = new Dictionary(); private static readonly Dictionary sentZombieAnims = new Dictionary(); private static float nextMovers; private static float nextZombieAnims; private static readonly Dictionary followers = new Dictionary(); public static int MoversSent; public static int MoversApplied; private static readonly Dictionary traced = new Dictionary(); private static float nextTrace; private static float nextRoster; private static string lastRosterKey = ""; private static float lastRosterSent; private static readonly HashSet undrawn = new HashSet(); private const float NearRange = 45f; private static HashSet partnerScenes = new HashSet(); private static HashSet nearScenes = new HashSet(); private static readonly List partnerSpots = new List(); private static float nextNearRefresh; private static string partnerScenesKey = ""; private static int partnerAreasFrame = -1; private const byte OpViewAnims = 3; private static readonly Dictionary sentViewAnims = new Dictionary(); private static float nextViewAnims; private static float nextViewAnimRefresh; private static readonly Dictionary polledPositions = new Dictionary(); private static readonly Dictionary polledDirections = new Dictionary(); private static readonly HashSet everMoved = new HashSet(); private static readonly Dictionary moverSceneSentTo = new Dictionary(); private const byte OpPuff = 4; public static int PuffsShown; public static int ViewAnimsApplied; public static bool ClientFollowsHost { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { return Plugin.HostRunsWorld.Value; } return false; } } public static bool IsWisp(MovementComponent component) { IMovable obj = MovableField.Invoke(component); WgoData val = (WgoData)(object)((obj is WgoData) ? obj : null); if (val != null) { return ((ObjectLinkedToDefinition)(object)val).id == "player_wisp"; } return false; } public static void Reset() { sentMovers.Clear(); sentZombieAnims.Clear(); sentViewAnims.Clear(); followers.Clear(); polledPositions.Clear(); polledDirections.Clear(); undrawn.Clear(); nearScenes = new HashSet(); partnerScenesKey = ""; everMoved.Clear(); moverSceneSentTo.Clear(); MoversSent = 0; MoversApplied = 0; PuffsShown = 0; } private unsafe static void SendStoryRoster() { //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: 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_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_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime < nextRoster || MainGame.WorldData?.gameSceneDataList == null) { return; } nextRoster = Time.unscaledTime + 2f; List list = new List(); Dictionary drawn = new Dictionary(); foreach (GameSceneData gameSceneData in MainGame.WorldData.gameSceneDataList) { if (gameSceneData?.wgoDataList == null) { continue; } foreach (WgoData wgoData in gameSceneData.wgoDataList) { if (((wgoData != null) ? wgoData.UniqueId : null) != (SGuid)null && ((ObjectLinkedToDefinition)(object)wgoData).id != null && ((ObjectLinkedToDefinition)(object)wgoData).id.StartsWith("npc_")) { list.Add(wgoData.UniqueId.Id); drawn[wgoData.UniqueId.Id] = DrawnState(wgoData); } } } list.Sort(StringComparer.Ordinal); string text = list.Count + ":" + string.Join("", list.Select((string u) => u + drawn[u])).GetHashCode(); if (text == lastRosterKey && Time.unscaledTime - lastRosterSent < 15f) { return; } lastRosterKey = text; lastRosterSent = Time.unscaledTime; FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 5; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item in list) { CoopMessages.WriteString(ref writer, item); b = drawn[item]; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); } CoopMessages.SendToReadyClients("GK2Coop.Npc", writer, (NetworkDelivery)4); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static byte DrawnState(WgoData data) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(data.UniqueId); if ((Object)(object)wgoViewGlobal == (Object)null || !((Component)wgoViewGlobal).gameObject.activeInHierarchy) { return 2; } Renderer[] componentsInChildren = ((Component)wgoViewGlobal).GetComponentsInChildren(); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled && !val.forceRenderingOff && ((Component)val).gameObject.activeInHierarchy) { SpriteRenderer val2 = (SpriteRenderer)(object)((val is SpriteRenderer) ? val : null); if (val2 == null || (!((Object)(object)val2.sprite == (Object)null) && !(val2.color.a < 0.02f))) { return 1; } } } return 0; } private static void ApplyStoryDrawn(Dictionary hostDrawn) { //IL_009b: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0119: 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) //IL_012c: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); if ((Object)(object)val == (Object)null) { return; } foreach (KeyValuePair item in hostDrawn) { if (item.Value == 2) { continue; } bool flag = item.Value == 0; if (flag == undrawn.Contains(item.Key)) { continue; } WgoData wgoData = MainGame.WorldData.GetWgoData(SGuid.Parse(item.Key)); Wgo val2 = ((wgoData != null) ? GameScene.GetWgoViewGlobal(wgoData.UniqueId) : null); if ((Object)(object)val2 == (Object)null) { continue; } Vector3 val3 = wgoData.Position - ((Component)val).transform.position; if (((Vector3)(ref val3)).sqrMagnitude > 3600f) { continue; } Renderer[] componentsInChildren = ((Component)val2).GetComponentsInChildren(true); foreach (Renderer val4 in componentsInChildren) { if ((Object)(object)val4 != (Object)null) { val4.forceRenderingOff = flag; } } if (flag) { undrawn.Add(item.Key); if (item.Key != null) { Vector3 position = wgoData.Position; val3 = default(Vector3); WorldFX.Spawn(position, "puff_npc", (Action)null, val3); } } else { undrawn.Remove(item.Key); } CoopLog.Info(((ObjectLinkedToDefinition)(object)wgoData).id + " is " + (flag ? "no longer shown" : "shown again") + " in the host's game: the same here."); } } private static void ApplyStoryRoster(HashSet hostHas) { //IL_0035: 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_00d6: 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) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); if ((Object)(object)val == (Object)null || MainGame.WorldData?.gameSceneDataList == null) { return; } Vector3 position = ((Component)val).transform.position; List list = new List(); foreach (GameSceneData gameSceneData in MainGame.WorldData.gameSceneDataList) { if (gameSceneData?.wgoDataList == null) { continue; } foreach (WgoData wgoData in gameSceneData.wgoDataList) { if (((wgoData != null) ? wgoData.UniqueId : null) != (SGuid)null && ((ObjectLinkedToDefinition)(object)wgoData).id != null && ((ObjectLinkedToDefinition)(object)wgoData).id.StartsWith("npc_") && !hostHas.Contains(wgoData.UniqueId.Id)) { Vector3 val2 = wgoData.Position - position; if (((Vector3)(ref val2)).sqrMagnitude < 3600f && !SermonSync.IsActor(wgoData.UniqueId.Id)) { list.Add(wgoData); } } } } foreach (WgoData item in list) { WorldSync.ApplyingRemote = true; try { MainGame.WorldData.RemoveWgoDataFromGameScene(item, true); CoopLog.Info(((ObjectLinkedToDefinition)(object)item).id + " is gone in the host's game: removed it here too."); } catch (Exception ex) { CoopLog.Warn("Removing " + ((ObjectLinkedToDefinition)(object)item).id + " failed: " + ex.Message); } finally { WorldSync.ApplyingRemote = false; } } } private static void TraceStoryCharacters() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: 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) //IL_019d: 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_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_027c: 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) if (Time.unscaledTime < nextTrace || MainGame.WorldData == null) { return; } nextTrace = Time.unscaledTime + 1f; PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); string value = MainGame.PlayerData?.currentGameSceneId; if ((Object)(object)val == (Object)null || string.IsNullOrEmpty(value)) { return; } Vector3 position = ((Component)val).transform.position; foreach (GameSceneData gameSceneData in MainGame.WorldData.gameSceneDataList) { if (gameSceneData?.wgoDataList == null) { continue; } foreach (WgoData wgoData in gameSceneData.wgoDataList) { if (((wgoData != null) ? wgoData.UniqueId : null) == (SGuid)null || ((ObjectLinkedToDefinition)(object)wgoData).id == null || !((ObjectLinkedToDefinition)(object)wgoData).id.StartsWith("npc_")) { continue; } Vector3 val2 = wgoData.Position - position; if (((Vector3)(ref val2)).sqrMagnitude > 1225f) { continue; } Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(wgoData.UniqueId); Vector3 position2 = wgoData.Position; string text = "-"; string text2 = "-"; if ((Object)(object)wgoViewGlobal != (Object)null) { Renderer[] componentsInChildren = ((Component)wgoViewGlobal).GetComponentsInChildren(true); text = $"{componentsInChildren.Count((Renderer r) => (Object)(object)r != (Object)null && r.enabled && ((Component)r).gameObject.activeInHierarchy)}/{componentsInChildren.Length}"; Vector3 position3 = ((Component)wgoViewGlobal).transform.position; text2 = $"({position3.x:0},{position3.y:0},{position3.z:0})"; } object[] obj = new object[10] { position2.x, position2.y, position2.z, wgoData.WorldId, wgoData.IsHidden, ((Object)(object)wgoViewGlobal == (Object)null) ? "none" : (((Component)wgoViewGlobal).gameObject.activeInHierarchy ? "on" : "off"), text2, text, DrawnState(wgoData), null }; AnimationComponentBase obj2 = Animation(wgoData.UniqueId.Id); obj[9] = ((obj2 != null) ? ((object)obj2.GetState()/*cast due to .constrained prefix*/).ToString() : null) ?? "-"; string text3 = string.Format("({0:0},{1:0},{2:0}) in '{3}' hidden={4} view={5} at {6} drawn={7} shows={8} anim={9}", obj); string id = wgoData.UniqueId.Id; if (!traced.TryGetValue(id, out var value2) || value2 != text3) { traced[id] = text3; CoopLog.Debug("Story character " + ((ObjectLinkedToDefinition)(object)wgoData).id + ": " + text3); } } } foreach (string item in traced.Keys.ToList()) { if (MainGame.WorldData.GetWgoData(SGuid.Parse(item)) == null) { CoopLog.Debug("Story character " + item + ": gone from the world."); traced.Remove(item); } } } public static void Tick() { if ((CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients) || ClientFollowsHost) { TraceStoryCharacters(); } if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && CoopSession.IsInGame) { SendStoryRoster(); } if (CoopSession.Role == CoopRole.Host) { TickHost(); } else if (ClientFollowsHost) { TickClient(); } } public static HashSet ScenesWithPartners() { RefreshPartnerAreas(); HashSet hashSet = new HashSet(partnerScenes); hashSet.UnionWith(nearScenes); return hashSet; } private static void RefreshPartnerAreas() { //IL_0079: 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) if (partnerAreasFrame == Time.frameCount) { return; } partnerAreasFrame = Time.frameCount; partnerScenes = new HashSet(); partnerSpots.Clear(); foreach (ulong readyClient in CoopSession.ReadyClients) { RemotePlayer remotePlayer = PlayerSync.Get(readyClient); if (remotePlayer != null && remotePlayer.HasState && !string.IsNullOrEmpty(remotePlayer.Scene)) { partnerScenes.Add(remotePlayer.Scene); partnerSpots.Add(remotePlayer.Target); } } string text = string.Join(",", partnerScenes.OrderBy((string x) => x)); if (text == partnerScenesKey && Time.unscaledTime < nextNearRefresh) { return; } partnerScenesKey = text; nextNearRefresh = Time.unscaledTime + 3f; HashSet hashSet = new HashSet(); List list = MainGame.WorldData?.gameSceneDataList; if (list != null && partnerSpots.Count > 0) { foreach (GameSceneData item in list) { if (item?.wgoDataList == null || string.IsNullOrEmpty(item.id) || partnerScenes.Contains(item.id)) { continue; } foreach (WgoData wgoData in item.wgoDataList) { if (wgoData != null && NearSpot(wgoData.Position)) { hashSet.Add(item.id); break; } } } } if (!hashSet.SetEquals(nearScenes)) { CoopLog.Debug("Areas with objects next to partners: [" + string.Join(", ", hashSet) + "] (partners in [" + text + "])."); } nearScenes = hashSet; } private static bool NearSpot(Vector3 position) { //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_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) foreach (Vector3 partnerSpot in partnerSpots) { Vector3 val = partnerSpot - position; if (((Vector3)(ref val)).sqrMagnitude < 2025f) { return true; } } return false; } public static bool NearPartners(WgoData data) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (data == null) { return false; } RefreshPartnerAreas(); if (partnerScenes.Contains(data.WorldId)) { return true; } if (nearScenes.Contains(data.WorldId)) { return NearSpot(data.Position); } return false; } public static bool NearUs(WgoData data, string scene) { //IL_003f: 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_004f: 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) PlayerController playerController = MainGame.PlayerController; if (((playerController != null) ? playerController.PlayerData : null) == null) { return false; } if (playerController.PlayerData.currentGameSceneId == scene) { return true; } PlayerPhysicalBody physicalBody = playerController.PhysicalBody; if (data != null && (Object)(object)physicalBody != (Object)null) { Vector3 val = data.Position - ((Component)physicalBody).transform.position; return ((Vector3)(ref val)).sqrMagnitude < 2025f; } return false; } private static void TickHost() { if (!CoopSession.HasReadyClients || !CoopSession.IsInGame || !Plugin.HostRunsWorld.Value) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime >= nextMovers) { nextMovers = unscaledTime + 0.1f; SendMovers(unscaledTime); } if (unscaledTime >= nextZombieAnims) { nextZombieAnims = unscaledTime + 0.5f; SendZombieAnims(); } if (unscaledTime >= nextViewAnims) { bool num = unscaledTime >= nextViewAnimRefresh; if (num) { nextViewAnimRefresh = unscaledTime + 3f; } nextViewAnims = unscaledTime + 0.25f; SendViewAnims(num); } } private unsafe static void SendViewAnims(bool full) { //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_0147: 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_0154: 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_0170: 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_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Expected I4, but got Unknown //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //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_00d4: 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_00c7: 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) List> list = new List>(); foreach (string item in ScenesWithPartners()) { GameSceneData gameSceneDataById = MainGame.WorldData.GetGameSceneDataById(item); if (gameSceneDataById == null) { continue; } foreach (WgoData wgoData in gameSceneDataById.wgoDataList) { if (((wgoData != null) ? wgoData.UniqueId : null) == (SGuid)null || ((ObjectLinkedToDefinition)(object)wgoData).id == "player_wisp" || !NearPartners(wgoData)) { continue; } AnimationComponentBase val = Animation(wgoData.UniqueId.Id); if (!((Object)(object)val == (Object)null)) { AnimationState state = val.GetState(); string id = wgoData.UniqueId.Id; if (full || !sentViewAnims.TryGetValue(id, out var value) || value != state) { sentViewAnims[id] = state; list.Add(new KeyValuePair(id, state)); } } } } for (int i = 0; i < list.Count; i += 80) { List> list2 = list.Skip(i).Take(80).ToList(); FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list2.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (KeyValuePair item2 in list2) { CoopMessages.WriteString(ref writer, item2.Key); count = (int)item2.Value; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); } CoopMessages.SendToReadyClients("GK2Coop.Npc", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } } private static void SendMovers(float now) { //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) //IL_00e1: 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_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: 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_01ac: 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_0105: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: 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_0299: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = ScenesWithPartners(); List list = MainGame.Instance.GameSave?.movementSystemData?.movingObjects; if (list == null || hashSet.Count == 0) { return; } List list2 = new List(); HashSet seen = new HashSet(); foreach (MovementComponent item in list) { IMovable obj = MovableField.Invoke(item); WgoData val = (WgoData)(object)((obj is WgoData) ? obj : null); if (val == null || ((ObjectLinkedToDefinition)(object)val).id == "player_wisp" || val.UniqueId == (SGuid)null) { continue; } string id = val.UniqueId.Id; if (!NearPartners(val)) { continue; } seen.Add(id); bool isMoving = item.IsMoving; Vector3 position = val.Position; Vector2 movableDirection = val.MovableDirection; if (sentMovers.TryGetValue(id, out var value) && value.Moving == isMoving) { Vector3 val2 = value.Position - position; if (((Vector3)(ref val2)).sqrMagnitude < 0.0009f) { Vector2 val3 = value.Direction - movableDirection; if (((Vector2)(ref val3)).sqrMagnitude < 0.0004f && now - value.Time < 1.5f) { continue; } } } sentMovers[id] = new SentMover { Position = position, Direction = movableDirection, Moving = isMoving, Time = now }; list2.Add(new MoverEntry { UniqueId = id, Scene = val.WorldId, Position = position, Direction = val.MovableDirection, Moving = isMoving }); } PollOtherMovers(hashSet, seen, list2); SendMoversToNewArrivals(); foreach (string item2 in sentMovers.Keys.Where((string k) => !seen.Contains(k)).ToList()) { SentMover sentMover = sentMovers[item2]; sentMovers.Remove(item2); if (sentMover.Moving) { WgoData wgoData = MainGame.WorldData.GetWgoData(SGuid.Parse(item2)); if (wgoData != null) { list2.Add(new MoverEntry { UniqueId = item2, Scene = wgoData.WorldId, Position = wgoData.Position, Direction = wgoData.MovableDirection, Moving = false }); } } } SendMoverEntries(list2, null); } private unsafe static void SendMoverEntries(List entries, ulong? onlyTo) { //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_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_002c: 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_0047: 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_008a: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00e1: 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_012d: 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) for (int i = 0; i < entries.Count; i += 60) { List list = entries.Skip(i).Take(60).ToList(); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (MoverEntry item in list) { MoverEntry current = item; CoopMessages.WriteString(ref writer, current.UniqueId); CoopMessages.WriteString(ref writer, current.Scene); CoopMessages.WriteVector3(ref writer, current.Position); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref current.Direction.x, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref current.Direction.y, default(ForPrimitives)); b = (current.Moving ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); } if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Npc", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Npc", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } MoversSent += entries.Count; } private static void PollOtherMovers(HashSet scenes, HashSet movingNow, List entries) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_0103: 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_0128: 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_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_0135: 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_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) Vector2 direction = default(Vector2); foreach (string scene in scenes) { GameSceneData gameSceneDataById = MainGame.WorldData.GetGameSceneDataById(scene); if (gameSceneDataById == null) { continue; } foreach (WgoData wgoData in gameSceneDataById.wgoDataList) { if (((wgoData != null) ? wgoData.UniqueId : null) == (SGuid)null || ((ObjectLinkedToDefinition)(object)wgoData).id == "player_wisp" || (!NearPartners(wgoData) && (!polledPositions.TryGetValue(wgoData.UniqueId.Id, out var value) || !NearSpot(value)))) { continue; } string id = wgoData.UniqueId.Id; Vector3 position = wgoData.Position; Vector2 movableDirection = wgoData.MovableDirection; if (movingNow.Contains(id)) { polledPositions[id] = position; polledDirections[id] = movableDirection; continue; } if (polledPositions.TryGetValue(id, out var value2)) { Vector3 val = value2 - position; if (((Vector3)(ref val)).sqrMagnitude > 0.0009f) { everMoved.Add(id); Vector3 val2 = position - value2; ((Vector2)(ref direction))..ctor(val2.x, val2.z); direction = ((((Vector2)(ref direction)).sqrMagnitude > 0.0004f) ? ((Vector2)(ref direction)).normalized : movableDirection); entries.Add(new MoverEntry { UniqueId = id, Scene = scene, Position = position, Direction = direction, Moving = false }); goto IL_0296; } } Vector2 value3; if (!polledPositions.ContainsKey(id) && ((ObjectLinkedToDefinition)(object)wgoData).id != null && ((ObjectLinkedToDefinition)(object)wgoData).id.StartsWith("npc_")) { everMoved.Add(id); entries.Add(new MoverEntry { UniqueId = id, Scene = scene, Position = position, Direction = movableDirection, Moving = false }); } else if (polledDirections.TryGetValue(id, out value3) && ((Vector2)(ref movableDirection)).sqrMagnitude > 0.0001f) { Vector2 val3 = value3 - movableDirection; if (((Vector2)(ref val3)).sqrMagnitude > 0.0004f) { everMoved.Add(id); entries.Add(new MoverEntry { UniqueId = id, Scene = scene, Position = position, Direction = movableDirection, Moving = false }); } } goto IL_0296; IL_0296: polledPositions[id] = position; polledDirections[id] = movableDirection; } } } private static void SendMoversToNewArrivals() { //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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_017e: 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_0122: 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) foreach (ulong item in CoopSession.ReadyClients.ToList()) { RemotePlayer remotePlayer = PlayerSync.Get(item); if (remotePlayer == null || !remotePlayer.HasState || string.IsNullOrEmpty(remotePlayer.Scene) || (moverSceneSentTo.TryGetValue(item, out var value) && value == remotePlayer.Scene)) { continue; } moverSceneSentTo[item] = remotePlayer.Scene; if (everMoved.Count == 0) { continue; } List list = new List(); foreach (string item2 in ScenesWithPartners()) { GameSceneData gameSceneDataById = MainGame.WorldData.GetGameSceneDataById(item2); if (gameSceneDataById == null) { continue; } foreach (WgoData wgoData in gameSceneDataById.wgoDataList) { if (!(((wgoData != null) ? wgoData.UniqueId : null) != (SGuid)null) || !everMoved.Contains(wgoData.UniqueId.Id)) { continue; } if (!(item2 == remotePlayer.Scene)) { Vector3 val = wgoData.Position - remotePlayer.Target; if (!(((Vector3)(ref val)).sqrMagnitude < 2025f)) { continue; } } list.Add(new MoverEntry { UniqueId = wgoData.UniqueId.Id, Scene = wgoData.WorldId, Position = wgoData.Position, Direction = wgoData.MovableDirection, Moving = false }); } } if (list.Count > 0) { SendMoverEntries(list, item); CoopLog.Debug($"Sent {list.Count} moved objects in '{remotePlayer.Scene}' to {remotePlayer.Label}."); } } } public unsafe static void OnLocalTransitPuff(WgoData data, Vector3 from, Vector3 to) { //IL_0039: 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_003f: 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_0049: 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_005e: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || ((data != null) ? data.UniqueId : null) == (SGuid)null || !Plugin.HostRunsWorld.Value || !NearPartners(data)) { return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int num = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); CoopMessages.WriteString(ref writer, data.UniqueId.Id); CoopMessages.WriteString(ref writer, data.WorldId ?? ""); CoopMessages.WriteVector3(ref writer, from); CoopMessages.WriteVector3(ref writer, to); CoopMessages.SendToReadyClients("GK2Coop.Npc", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void ApplyPuff(string uid, string scene, Vector3 from, Vector3 to) { //IL_003b: 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_004a: 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_005a: 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_001f: 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_0036: Unknown result type (might be due to invalid IL or missing references) WgoData wgoData = MainGame.WorldData.GetWgoData(SGuid.Parse(uid)); if (!NearUs(wgoData, scene)) { return; } if (wgoData != null) { wgoData.MovablePositionWithoutDirectionChange = to; if (followers.TryGetValue(uid, out var value)) { value.Target = to; } } WorldFX.Spawn(from, "puff_npc", (Action)null, default(Vector3)); WorldFX.Spawn(to, "puff_npc", (Action)null, default(Vector3)); PuffsShown++; } private unsafe static void SendZombieAnims() { //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_0103: 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_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected I4, but got Unknown //IL_0166: 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_0076: 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_00a3: 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_0096: 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) HashSet hashSet = ScenesWithPartners(); List> list = new List>(); foreach (string item in hashSet) { GameSceneData gameSceneDataById = MainGame.WorldData.GetGameSceneDataById(item); if (gameSceneDataById == null) { continue; } foreach (WgoData wgoData in gameSceneDataById.wgoDataList) { ZombieWgoData val = (ZombieWgoData)(object)((wgoData is ZombieWgoData) ? wgoData : null); if (val != null && !(((WgoData)val).UniqueId == (SGuid)null) && NearPartners((WgoData)(object)val)) { AnimationState val2 = ZombieAnimField.Invoke(val); string id = ((WgoData)val).UniqueId.Id; if (!sentZombieAnims.TryGetValue(id, out var value) || value != val2) { sentZombieAnims[id] = val2; list.Add(new KeyValuePair(id, val2)); } } } } if (list.Count == 0) { return; } FastBufferWriter val3 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val3; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (KeyValuePair item2 in list) { CoopMessages.WriteString(ref writer, item2.Key); count = (int)item2.Value; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); } CoopMessages.SendToReadyClients("GK2Coop.Npc", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val3))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Debug($"Sent {list.Count} zombie animation changes."); } public static void OnClientReady() { sentZombieAnims.Clear(); sentViewAnims.Clear(); sentMovers.Clear(); moverSceneSentTo.Clear(); } public static void OnNpc(ulong sender, FastBufferReader reader) { //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_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) //IL_0049: 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_016a: 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0187: 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_0130: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: 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_01d6: Invalid comparison between Unknown and I4 byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); switch (b) { case 5: { HashSet hashSet = new HashSet(); Dictionary dictionary = new Dictionary(); byte value = default(byte); for (int j = 0; j < num; j++) { string text = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref value, default(ForPrimitives)); hashSet.Add(text); dictionary[text] = value; } if (ClientFollowsHost) { ApplyStoryRoster(hashSet); ApplyStoryDrawn(dictionary); } break; } case 1: { float num3 = default(float); float num4 = default(float); byte b2 = default(byte); for (int k = 0; k < num; k++) { string uid3 = CoopMessages.ReadString(ref reader); string scene2 = CoopMessages.ReadString(ref reader); Vector3 position = CoopMessages.ReadVector3(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num4, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); if (ClientFollowsHost) { Follow(uid3, scene2, position, new Vector2(num3, num4), b2 != 0); } } break; } case 2: { int num5 = default(int); for (int l = 0; l < num; l++) { string uid4 = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num5, default(ForPrimitives)); if (ClientFollowsHost) { ApplyZombieAnim(uid4, (AnimationState)num5); } } break; } case 4: { string uid2 = CoopMessages.ReadString(ref reader); string scene = CoopMessages.ReadString(ref reader); Vector3 val2 = CoopMessages.ReadVector3(ref reader); Vector3 to = CoopMessages.ReadVector3(ref reader); if (ClientFollowsHost) { ApplyPuff(uid2, scene, val2, to); } break; } case 3: { int num2 = default(int); for (int i = 0; i < num; i++) { string uid = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); if (ClientFollowsHost) { AnimationComponentBase val = Animation(uid); if ((Object)(object)val != (Object)null && (int)val.GetState() != num2) { val.SetState((AnimationState)num2); ViewAnimsApplied++; } } } break; } } } private static void Follow(string uid, string scene, Vector3 position, Vector2 direction, bool moving) { //IL_00a7: 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_014d: 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_0154: 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_0146: Unknown result type (might be due to invalid IL or missing references) Wgo val = (((Object)(object)MainGame.PlayerController != (Object)null) ? MainGame.PlayerController.attachedWgo : null); if ((Object)(object)val != (Object)null) { WgoData data = val.Data; if (((data != null) ? data.UniqueId : null) != (SGuid)null && val.Data.UniqueId.Id == uid) { followers.Remove(uid); return; } } if (!followers.TryGetValue(uid, out var value) || value.Data == null) { WgoData wgoData = MainGame.WorldData.GetWgoData(SGuid.Parse(uid)); if (wgoData == null) { return; } value = new Follower { Data = wgoData, AppliedMoving = !moving, Target = position }; followers[uid] = value; } if (!string.IsNullOrEmpty(scene) && value.Data.WorldId != scene) { CoopLog.Debug(((ObjectLinkedToDefinition)(object)value.Data).id + " is in '" + value.Data.WorldId + "' here but '" + scene + "' on the host; moving it."); WorldSync.ApplyingRemote = true; try { MainGame.WorldData.MoveWgoDataToAnotherGameScene(value.Data, scene); } finally { WorldSync.ApplyingRemote = false; } value.Data.MovablePositionWithoutDirectionChange = position; } value.Target = position; value.Direction = direction; value.Moving = moving; value.LastUpdate = Time.unscaledTime; MoversApplied++; } private static void TickClient() { //IL_0061: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_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_0096: 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) //IL_0088: 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_009b: 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_009f: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_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_00dd: 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_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) float num = 1f - Mathf.Exp(-12f * Time.deltaTime); foreach (KeyValuePair item in followers.ToList()) { Follower value = item.Value; WgoData data = value.Data; if (data == null) { followers.Remove(item.Key); continue; } Vector3 position = data.Position; Vector3 val = value.Target - position; Vector3 val2 = ((((Vector3)(ref val)).sqrMagnitude > 36f) ? value.Target : Vector3.Lerp(position, value.Target, num)); val = val2 - position; if (((Vector3)(ref val)).sqrMagnitude > 1E-08f) { data.MovablePositionWithoutDirectionChange = val2; } if (((Vector2)(ref value.Direction)).sqrMagnitude > 0.0001f) { Vector2 val3 = value.Direction - value.AppliedDirection; if (((Vector2)(ref val3)).sqrMagnitude > 0.0001f) { value.AppliedDirection = value.Direction; data.MovableDirection = value.Direction; AnimationComponentBase obj = Animation(item.Key); if (obj != null) { obj.SetDirection(DirectionExtensions.ConvertFromVector2(value.Direction)); } } } if (value.Moving != value.AppliedMoving) { value.AppliedMoving = value.Moving; if (value.Moving) { AnimationComponentBase obj2 = Animation(item.Key); if (obj2 != null) { obj2.SetState((AnimationState)1); } } else { ZombieWgoData val4 = (ZombieWgoData)(object)((data is ZombieWgoData) ? data : null); if (val4 != null) { val4.InvokeOnAnimationStateChanged(false); } } } if (!value.Moving && Time.unscaledTime - value.LastUpdate > 5f) { val = value.Target - data.Position; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { followers.Remove(item.Key); } } } } private static void ApplyZombieAnim(string uid, AnimationState state) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected I4, but got Unknown WgoData wgoData = MainGame.WorldData.GetWgoData(SGuid.Parse(uid)); ZombieWgoData val = (ZombieWgoData)(object)((wgoData is ZombieWgoData) ? wgoData : null); if (val != null) { ZombieAnimField.Invoke(val) = (AnimationState)(int)state; if (!followers.TryGetValue(uid, out var value) || !value.Moving) { val.InvokeOnAnimationStateChanged(false); } } } private static AnimationComponentBase Animation(string uid) { try { Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(uid)); return ((Object)(object)wgoViewGlobal != (Object)null && (Object)(object)wgoViewGlobal.MainWgoPart != (Object)null) ? wgoViewGlobal.MainWgoPart.AnimationComponent : null; } catch { return null; } } public static string Describe() { if (CoopSession.Role != CoopRole.Host) { return $"following={followers.Count} applied={MoversApplied} viewAnims={ViewAnimsApplied} puffs={PuffsShown} active={ClientFollowsHost}"; } return $"movers tracked={sentMovers.Count} sent={MoversSent}"; } } [HarmonyPatch(typeof(WgoData), "OnTeleportToTransitPoint")] internal static class MirrorTransitPuff { private static void Postfix(WgoData __instance, Vector3 from, Vector3 to) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) try { NpcSync.OnLocalTransitPuff(__instance, from, to); } catch (Exception e) { CoopLog.Error("Mirroring a door puff failed", e); } } } [Serializable] public class PartnerCharacter { public int version = 1; public string name; public Inventory inventory; public Inventory toolBeltInventory; public List overheadItems; public GameRes res; public EnergySystem energySystem; public StaminaSystem staminaSystem; public HPComponent hpComponent; public string[] pinnedItems; public PlayerCustomizationData customization; public bool hasPosition; public float posX; public float posY; public float posZ; public string sceneId; } internal static class PartnerProfiles { private const byte OpHello = 1; private const byte OpReject = 2; private const byte OpCharacter = 3; private const byte OpLobby = 4; public const string Channel = "GK2Coop.Profile"; public const int ProtocolVersion = 30; private const float UploadInterval = 20f; private static readonly FieldRef> OverheadItemsField = AccessTools.FieldRefAccess>("overheadItems"); private static readonly FieldRef ResField = AccessTools.FieldRefAccess("res"); private static readonly Dictionary names = new Dictionary(); private static readonly Dictionary latest = new Dictionary(); private static readonly HashSet dirty = new HashSet(); private static byte[] pendingCharacter; private static bool pendingStartKit; private static bool characterArrived; private static float nextUpload; private static readonly HashSet lobby = new HashSet(); private const string NameMarker = "gk2coop_partner|"; private const int FirstPartnerId = 1000; private static string retireAfterSave; public static bool CharacterArrived => characterArrived; public static bool StartedAtOwnSpot { get; private set; } public static bool LobbyWaiting => lobby.Count > 0; public static IEnumerable LobbyClients => lobby; public static string NameOf(ulong clientId) { if (!names.TryGetValue(clientId, out var value)) { return null; } return value; } public static string Key(string name) { StringBuilder stringBuilder = new StringBuilder(); string text = (name ?? "").Trim().ToLowerInvariant(); foreach (char c in text) { stringBuilder.Append((char.IsLetterOrDigit(c) || c == '-' || c == '_') ? c : '_'); } return stringBuilder.ToString(); } public static void ResetClient() { pendingCharacter = null; characterArrived = false; nextUpload = 0f; } public static void ForgetClient(ulong clientId) { names.Remove(clientId); lobby.Remove(clientId); } public static void ResetHost() { names.Clear(); lobby.Clear(); } public unsafe static void SendHello() { //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_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_0010: 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_0026: 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_0059: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int num = 30; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); CoopMessages.WriteString(ref writer, Plugin.PlayerName.Value.Trim()); CoopMessages.WriteString(ref writer, "0.27.0"); CoopMessages.SendToHost("GK2Coop.Profile", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Sent hello as '" + Plugin.PlayerName.Value.Trim() + "'."); } public static void Tick() { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && Time.unscaledTime >= nextUpload) { nextUpload = Time.unscaledTime + 20f; UploadCharacter(); } } public unsafe static void UploadCharacter() { //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_0032: 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_0043: 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_0061: 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) PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); if (val == null || !CoopNetwork.IsListening) { return; } try { byte[] array = SerializationUtility.SerializeValue(Capture(val), (DataFormat)0, (SerializationContext)null); FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, array); b = 0; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Profile", writer, (NetworkDelivery)4); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Debug($"Uploaded our character to the host ({array.Length} bytes)."); } catch (Exception e) { CoopLog.Error("Uploading our character failed", e); } } public static void ApplyToWorld(GameSave gameSave) { //IL_0125: 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_0151: 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) PlayerData playerData = gameSave.playerData; GameRes hostRes = ResField.Invoke(playerData); PartnerCharacter partnerCharacter = null; if (pendingCharacter != null && pendingCharacter.Length != 0) { try { partnerCharacter = SerializationUtility.DeserializeValue(pendingCharacter, (DataFormat)0, (DeserializationContext)null); } catch (Exception e) { CoopLog.Error("Reading our saved character failed; starting fresh", e); } } if (partnerCharacter == null) { PlayerData val = PlayerData.CreatePlayerData(); val.TryApplyStartState(); partnerCharacter = Capture(pendingStartKit ? val : PlayerData.CreatePlayerData()); partnerCharacter.res = ResField.Invoke(val); partnerCharacter.customization = playerData.customization; CoopLog.Info("No saved character for us on this host save: starting " + (pendingStartKit ? "with the starter kit" : "with an empty inventory") + "."); } else { CoopLog.Info("Restoring our saved character '" + partnerCharacter.name + "'."); } playerData.inventory = partnerCharacter.inventory ?? PlayerData.CreatePlayerData().inventory; playerData.toolBeltInventory = partnerCharacter.toolBeltInventory ?? PlayerData.CreatePlayerData().toolBeltInventory; OverheadItemsField.Invoke(playerData) = partnerCharacter.overheadItems ?? new List(); playerData.energySystem = (EnergySystem)(((object)partnerCharacter.energySystem) ?? ((object)new EnergySystem())); playerData.staminaSystem = (StaminaSystem)(((object)partnerCharacter.staminaSystem) ?? ((object)new StaminaSystem())); playerData.hpComponent = (HPComponent)(((object)partnerCharacter.hpComponent) ?? ((object)new HPComponent(100))); playerData.pinnedItems = partnerCharacter.pinnedItems ?? new string[4]; if (partnerCharacter.customization != null) { playerData.customization = partnerCharacter.customization; } StartedAtOwnSpot = false; if (partnerCharacter.hasPosition && !string.IsNullOrEmpty(partnerCharacter.sceneId) && Plugin.ReturnToSavedSpot.Value) { WorldData worldData = gameSave.worldData; if (((worldData != null) ? worldData.GetGameSceneDataById(partnerCharacter.sceneId) : null) != null) { playerData.position.Value = new Vector3(partnerCharacter.posX, partnerCharacter.posY, partnerCharacter.posZ); playerData.currentGameSceneId = partnerCharacter.sceneId; StartedAtOwnSpot = true; CoopLog.Info($"Starting where we left off: '{partnerCharacter.sceneId}' at ({partnerCharacter.posX:0.0}, {partnerCharacter.posY:0.0}, {partnerCharacter.posZ:0.0})."); } } ResField.Invoke(playerData) = EconomySync.BuildPartnerRes(hostRes, partnerCharacter.res); } private static PartnerCharacter Capture(PlayerData data) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00d6: 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) PartnerCharacter partnerCharacter = new PartnerCharacter { name = Plugin.PlayerName.Value.Trim(), inventory = data.inventory, toolBeltInventory = data.toolBeltInventory, overheadItems = OverheadItemsField.Invoke(data), res = ResField.Invoke(data), energySystem = data.energySystem, staminaSystem = data.staminaSystem, hpComponent = data.hpComponent, pinnedItems = data.pinnedItems, customization = data.customization }; if (data.position != null && !string.IsNullOrEmpty(data.currentGameSceneId) && CoopSession.ClientInWorld && !StorySync.Watching) { Vector3 value = data.position.Value; partnerCharacter.hasPosition = true; partnerCharacter.posX = value.x; partnerCharacter.posY = value.y; partnerCharacter.posZ = value.z; partnerCharacter.sceneId = data.currentGameSceneId; } return partnerCharacter; } public static void OnMessage(ulong sender, FastBufferReader reader) { //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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 1: OnHello(sender, ref reader); break; case 4: if (CoopSession.Role == CoopRole.Client) { string text3 = CoopMessages.ReadString(ref reader); CoopSession.Status = "In " + text3 + "'s lobby. You'll join as soon as they start the game."; CoopRunner.Toast(CoopSession.Status, 8f); CoopLog.Info(CoopSession.Status); } break; case 2: { string text2 = CoopMessages.ReadString(ref reader); CoopSession.Status = "The host turned you away: " + text2; CoopRunner.Toast(CoopSession.Status, 8f); CoopLog.Warn(CoopSession.Status); break; } case 3: { byte[] array = CoopMessages.ReadBytes(ref reader); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Client) { pendingStartKit = b2 != 0; pendingCharacter = ((array.Length != 0) ? array : null); characterArrived = true; CoopLog.Info((array.Length != 0) ? $"Host sent our saved character ({array.Length} bytes)." : "Host has no character for us yet; we'll start fresh."); } else if (CoopSession.Role == CoopRole.Host) { string text = NameOf(sender); if (text != null) { latest[Key(text)] = array; dirty.Add(Key(text)); CoopLog.Debug($"Got {text}'s character ({array.Length} bytes)."); } } break; } } } private unsafe static void OnHello(ulong sender, ref FastBufferReader reader) { //IL_0014: 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_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_019e: 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_0108: 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_0115: 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_0130: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: 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_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); string name = (CoopMessages.ReadString(ref reader) ?? "").Trim(); string text = CoopMessages.ReadString(ref reader); CoopLog.Info($"Hello from client {sender}: '{name}', mod {text}, protocol {num}."); string text2 = null; if (num != 30) { text2 = "different mod version (host 0.27.0, you " + text + ")"; } else if (Key(name).Length == 0) { text2 = "set a player name in the F8 window first"; } else if (Key(name) == Key(Plugin.PlayerName.Value) || names.Values.Any((string n) => Key(n) == Key(name))) { text2 = "someone called '" + name + "' is already playing"; } if (text2 != null) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text2); CoopMessages.SendTo("GK2Coop.Profile", sender, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Warn($"Rejected client {sender} ('{name}'): {text2}"); ((MonoBehaviour)CoopRunner.Instance).StartCoroutine(DisconnectSoon(sender, text2)); return; } names[sender] = name; if (!CoopSession.IsInGame) { lobby.Add(sender); FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer2 = val2; byte b = 4; ((FastBufferWriter)(ref writer2)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer2, Plugin.PlayerName.Value ?? "The host"); CoopMessages.SendTo("GK2Coop.Profile", sender, writer2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } CoopSession.Status = name + " is in your lobby. Start a new game (or load one) and they'll join you."; CoopRunner.Toast(name + " joined your lobby."); CoopLog.Info(name + " is waiting in the lobby."); } else { SendIntoWorld(sender, name); } } public static List ReleaseLobby() { List list = lobby.ToList(); lobby.Clear(); foreach (ulong item in list) { string text = NameOf(item); if (text != null) { SendIntoWorld(item, text); } } return list; } private unsafe static void SendIntoWorld(ulong sender, string name) { //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_000d: 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_0017: 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_0041: 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_0054: Unknown result type (might be due to invalid IL or missing references) byte[] array = LoadCharacter(name); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, array); b = ((Plugin.NewPartnerStart.Value == NewPartnerItems.StarterKit) ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.SendTo("GK2Coop.Profile", sender, writer, (NetworkDelivery)4); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info((array != null) ? $"Sending {name} their saved character ({array.Length} bytes)." : (name + " is new to this save; they'll start fresh.")); CoopSession.Status = name + " connected. Sending your world..."; ((MonoBehaviour)CoopRunner.Instance).StartCoroutine(WorldTransfer.SendWorld(sender)); } private static IEnumerator DisconnectSoon(ulong clientId, string reason) { yield return (object)new WaitForSecondsRealtime(0.5f); try { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null) { singleton.DisconnectClient(clientId, reason); } } catch (Exception ex) { CoopLog.Warn($"Disconnecting client {clientId} failed: {ex.Message}"); } } private static string Folder(string slot) { return Path.Combine(Application.persistentDataPath, "GK2Coop", "partners", Key(string.IsNullOrEmpty(slot) ? "default" : slot)); } public static void OnWorldLoading(GameSave gameSave, string slot) { latest.Clear(); dirty.Clear(); retireAfterSave = null; int num = 0; List list = gameSave?.clientPlayers; if (list != null) { foreach (NetworkPlayer item in list) { if (TryReadPartner(item?.playerData, out var key, out var bytes)) { latest[key] = bytes; num++; } } list.Clear(); } if (num > 0) { CoopLog.Info(string.Format("This save has {0} partner(s): {1}.", num, string.Join(", ", latest.Keys))); return; } string text = Folder(slot); try { if (Directory.Exists(text)) { string[] files = Directory.GetFiles(text, "*.dat"); foreach (string path in files) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); latest[fileNameWithoutExtension] = File.ReadAllBytes(path); dirty.Add(fileNameWithoutExtension); } if (latest.Count > 0) { retireAfterSave = text; CoopLog.Info($"Took {latest.Count} partner(s) from the old folder {text}; they go into the save when it next saves."); } } } catch (Exception e) { CoopLog.Error("Reading old partner files in " + text + " failed", e); } } public static void OnNewGame(string slot) { latest.Clear(); dirty.Clear(); retireAfterSave = null; Retire(Folder(slot), "old"); } private static void Retire(string folder, string why) { try { if (!string.IsNullOrEmpty(folder) && Directory.Exists(folder)) { string text = folder + "." + why + "-" + DateTime.Now.ToString("yyyyMMdd-HHmmss"); Directory.Move(folder, text); CoopLog.Info("Put the old partner folder aside: " + text); } } catch (Exception ex) { CoopLog.Warn("Couldn't put " + folder + " aside: " + ex.Message); } } public static int PutIntoSave(GameSave gameSave) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown if (gameSave == null || latest.Count == 0) { return 0; } if (gameSave.clientPlayers == null) { gameSave.clientPlayers = new List(); } int num = 1000; foreach (KeyValuePair item in latest) { PlayerData val = ToPlayerData(item.Key, item.Value); if (val != null) { gameSave.clientPlayers.Add(new NetworkPlayer(num++, val)); } } return num - 1000; } public static void Saved(int count) { dirty.Clear(); if (count > 0) { CoopLog.Info(string.Format("Saved {0} partner(s) in the save: {1}.", count, string.Join(", ", latest.Keys))); } if (retireAfterSave != null) { Retire(retireAfterSave, "imported"); retireAfterSave = null; } } public static void SaveAll(string why) { if (dirty.Count > 0) { CoopLog.Info("Partner characters kept for the host's next save (" + why + "): " + string.Join(", ", dirty) + "."); } } private static byte[] LoadCharacter(string name) { if (!latest.TryGetValue(Key(name), out var value)) { return null; } return value; } private static PlayerData ToPlayerData(string key, byte[] bytes) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown //IL_01c3: Unknown result type (might be due to invalid IL or missing references) PartnerCharacter partnerCharacter; try { partnerCharacter = SerializationUtility.DeserializeValue(bytes, (DataFormat)0, (DeserializationContext)null); } catch (Exception e) { CoopLog.Error("Reading " + key + "'s character for the save failed", e); return null; } if (partnerCharacter == null) { return null; } PlayerData val = PlayerData.CreatePlayerData(); if (partnerCharacter.inventory != null) { val.inventory = partnerCharacter.inventory; } if (partnerCharacter.toolBeltInventory != null) { val.toolBeltInventory = partnerCharacter.toolBeltInventory; } OverheadItemsField.Invoke(val) = partnerCharacter.overheadItems ?? new List(); GameRes res = partnerCharacter.res; GameRes val2 = new GameRes(((res == null) ? null : res.List?.Where((GameResAtom a) => a != null && a.type != null && !a.type.StartsWith("gk2coop_partner|")).Select((Func)((GameResAtom a) => new GameResAtom(a))).ToList()) ?? new List()); val2.SetWithoutSystemsCheck("gk2coop_partner|" + (string.IsNullOrEmpty(partnerCharacter.name) ? key : partnerCharacter.name.Trim()), 1f); ResField.Invoke(val) = val2; if (partnerCharacter.energySystem != null) { val.energySystem = partnerCharacter.energySystem; } if (partnerCharacter.staminaSystem != null) { val.staminaSystem = partnerCharacter.staminaSystem; } if (partnerCharacter.hpComponent != null) { val.hpComponent = partnerCharacter.hpComponent; } if (partnerCharacter.pinnedItems != null) { val.pinnedItems = partnerCharacter.pinnedItems; } if (partnerCharacter.customization != null) { val.customization = partnerCharacter.customization; } if (partnerCharacter.hasPosition && !string.IsNullOrEmpty(partnerCharacter.sceneId) && val.position != null) { val.position.Value = new Vector3(partnerCharacter.posX, partnerCharacter.posY, partnerCharacter.posZ); val.currentGameSceneId = partnerCharacter.sceneId; } else { val.currentGameSceneId = null; } return val; } private static bool TryReadPartner(PlayerData data, out string key, out byte[] bytes) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //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_0176: 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) key = null; bytes = null; GameRes val = ((data != null) ? ResField.Invoke(data) : null); GameResAtom marker = ((val == null) ? null : val.List?.FirstOrDefault((Func)((GameResAtom a) => a != null && a.type != null && a.type.StartsWith("gk2coop_partner|")))); if (marker == null) { return false; } string text = marker.type.Substring("gk2coop_partner|".Length); PartnerCharacter partnerCharacter = new PartnerCharacter { name = text, inventory = data.inventory, toolBeltInventory = data.toolBeltInventory, overheadItems = OverheadItemsField.Invoke(data), res = new GameRes(val.List.Where((GameResAtom a) => a != null && a != marker).Select((Func)((GameResAtom a) => new GameResAtom(a))).ToList()), energySystem = data.energySystem, staminaSystem = data.staminaSystem, hpComponent = data.hpComponent, pinnedItems = data.pinnedItems, customization = data.customization }; if (!string.IsNullOrEmpty(data.currentGameSceneId) && data.position != null) { Vector3 value = data.position.Value; partnerCharacter.hasPosition = true; partnerCharacter.posX = value.x; partnerCharacter.posY = value.y; partnerCharacter.posZ = value.z; partnerCharacter.sceneId = data.currentGameSceneId; } try { bytes = SerializationUtility.SerializeValue(partnerCharacter, (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Reading partner '" + text + "' from the save failed", e); return false; } key = Key(text); return true; } public static string Describe() { if (CoopSession.Role != CoopRole.Host) { return $"name='{Plugin.PlayerName.Value}' characterArrived={characterArrived}"; } return string.Format("partners=[{0}] cached={1} unsaved={2}", string.Join(",", names.Select((KeyValuePair p) => $"{p.Key}:{p.Value}")), latest.Count, dirty.Count); } } public enum NewPartnerItems { Empty, StarterKit } internal static class PartnerReplay { private static readonly HashSet SharedWorldCalls = new HashSet { "SpawnWgo", "SpawnWgoOnGDPoint", "ReplaceWgoByIds", "ChangeWgo", "DestroyWgoData", "AddWgoPart", "RemoveWgoPart", "RemoveAllWgoParts", "SetWgoPartStateAll", "SetWgoVariation", "SetWgoInteractionState", "SetWgoGameResInt", "AddInteractionEvent", "RemoveInteractionEvent", "AddItemToWgoInv", "DropItem", "DropItemByGroup", "DropBurialRewards", "DropHappiness", "GSRun", "GSFireEvent", "WGOFireEvent", "FireEventOnWgo", "CreateTownBuilding", "DestroySignboard", "RepairTownCluster", "StartCraft", "StartCraftOnWgo", "Spawn_FightingLvl", "Despawn_FightingLvl", "SetStage_FightingLvl", "Replace_To_Broken_Barricade", "AddMoneyToVendor", "LvlUpVendor", "ResetVendors", "ClearTownPalettes", "AddOrderSlot", "StartQuest", "StartQuestDemoDependent", "AwaitQuest", "AwaitQuestDemoDependent", "CompleteQuest", "CompleteQuestDemoDependent", "CancelQuest", "SetWeatherState", "ResetWeatherState", "SetWeatherComponent", "AddRep", "SetRep", "AddRepWGO", "SetRepWGO", "AddWorldPar", "DecWorldPar", "MultiplyWorldPar", "DivideWorldPar", "SetWorldPar", "AddTownQuality", "WZAddAdditionalQuality", "AddCustomRectToWZ", "RemoveCustomRectFromWZ", "AddWGOPar", "DecWGOPar", "MultiplyWGOPar", "DivideWGOPar", "SetWGOPar", "SetWGOParByTag", "SetResWgo", "AddPerkToWgoByTag", "RemovePerkFromWgoByTag", "AddPerkWgoSelf", "RemovePerkWgoSelf", "RemoveAllPerksWgoSelf", "LazyExpressionOnWgo", "TryPlacePlantOrder", "TryPlaceGatherOrder", "Spawning", "SetRandomSpawnWgoStages", "SetRandomVariation", "AddItemToCrowNest" }; private static readonly string[] SharedWorldPrefixes = new string[6] { "SpawnConveyorWgo", "SetWgoHiddenState", "RepairHouses", "NPCSim_", "EnableGDPoint", "DisableGDPoint" }; private static readonly Regex CallName = new Regex("([A-Za-z_][A-Za-z0-9_]*)\\s*\\("); private static readonly Regex ScriptTarget = new Regex("\\G\\s*\\(\\s*\"([^\"]*)\""); private static readonly HashSet CarryCalls = new HashSet { "InsertOvrhdItem", "InsertOvrhdItemWithSkulls", "TakeOvrhdItem", "TakeOvrhdItemByGroup" }; private static readonly Regex StoryStart = new Regex("(GSFireEvent|GSRun)\\s*\\(\\s*\"Event_"); private static readonly Regex Teleport = new Regex("\\b(TeleportTo|TeleportToGD)\\s*\\("); private static bool LocalScript(string expression, Match call) { Match match = ScriptTarget.Match(expression, call.Index + call.Groups[1].Length); if (!match.Success) { return false; } string value = match.Groups[1].Value; if (!(value == "Music")) { return value.StartsWith("GDZone_", StringComparison.Ordinal); } return true; } public static string SharedWorldCall(string expression) { foreach (Match item in CallName.Matches(expression ?? "")) { string value = item.Groups[1].Value; if ((value == "GSFireEvent" || value == "GSRun") && LocalScript(expression, item)) { continue; } if (SharedWorldCalls.Contains(value)) { return value; } string[] sharedWorldPrefixes = SharedWorldPrefixes; foreach (string value2 in sharedWorldPrefixes) { if (value.StartsWith(value2, StringComparison.Ordinal)) { return value; } } } return null; } public static string HostCallIn(List expressions) { if (expressions == null) { return null; } foreach (LazyExpression expression in expressions) { string text = ((expression != null) ? ((LazyExpressionBase)expression).GetRawExpressionString() : null) ?? ""; string text2 = SharedWorldCall(text); if (text2 != null) { return text2; } foreach (Match item in CallName.Matches(text)) { if (CarryCalls.Contains(item.Groups[1].Value)) { return item.Groups[1].Value; } } } return null; } public static bool StartsStory(List expressions) { return expressions?.Any((LazyExpression e) => e != null && StoryStart.IsMatch(((LazyExpressionBase)e).GetRawExpressionString() ?? "")) ?? false; } public static bool StartsStory(string expression) { return StoryStart.IsMatch(expression ?? ""); } public static bool MovesPlayer(List expressions) { return expressions?.Any((LazyExpression e) => e != null && Teleport.IsMatch(((LazyExpressionBase)e).GetRawExpressionString() ?? "")) ?? false; } public static List WithoutSharedWorld(List expressions, string what) { if (expressions == null || expressions.Count == 0) { return expressions; } List list = null; for (int i = 0; i < expressions.Count; i++) { LazyExpression val = expressions[i]; string text = ((val != null) ? ((LazyExpressionBase)val).GetRawExpressionString() : null) ?? ""; string text2 = SharedWorldCall(text); if (text2 == null) { list?.Add(val); continue; } if (list == null) { list = new List(expressions.GetRange(0, i)); } CoopLog.Info(what + ": left '" + text + "' to the host's game (" + text2 + " changes the shared world)."); } return list ?? expressions; } } internal static class ItemScripts { private struct Call { public string Raw; public byte Kind; public Item Item; public string Wgo; } private const byte OpRun = 1; private static readonly FieldRef> OverheadField = AccessTools.FieldRefAccess>("overheadItems"); private static readonly MethodInfo PlayerDataSetter = AccessTools.PropertySetter(typeof(MainGame), "PlayerData"); private static readonly MethodInfo RefreshOverheadMethod = AccessTools.Method(typeof(PlayerData), "RefreshOverheadVisuals", (Type[])null, (Type[])null); public static readonly FieldRef ResField = AccessTools.FieldRefAccess("res"); private static int depth; private static string context; private static bool allowStory; private static bool toHost; private const byte OnItem = 0; private const byte OnNothing = 1; private const byte OnObject = 2; private static readonly List forHost = new List(); public static int Sent; public static int Ran; private static bool ClientActive { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { return Plugin.HostRunsWorld.Value; } return false; } } public static void Reset() { depth = 0; context = null; forHost.Clear(); Sent = 0; Ran = 0; } public static void Begin(string what) { Begin(what, !TutorialSync.Active, sharedToHost: true); } public static void Begin(string what, Item item) { Begin(what, !TutorialSync.Active, !TutorialSync.Active || !TutorialSync.SharedInIntro(item)); } public static void Begin(string what, bool storyToHost) { Begin(what, storyToHost, sharedToHost: true); } public static void Begin(string what, bool storyToHost, bool sharedToHost) { if (ClientActive && depth++ == 0) { context = what; allowStory = storyToHost; toHost = sharedToHost; forHost.Clear(); } } public unsafe static void End() { //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_0043: 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_0076: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if (depth == 0 || --depth > 0) { return; } if (forHost.Count > 0 && ClientActive) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, context ?? ""); int count = forHost.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (Call item in forHost) { Call current = item; CoopMessages.WriteString(ref writer, current.Raw); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref current.Kind, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, (current.Kind == 0 && current.Item != null) ? SerializationUtility.SerializeValue(current.Item, (DataFormat)0, (SerializationContext)null) : new byte[0]); CoopMessages.WriteString(ref writer, current.Wgo ?? ""); } CoopMessages.SendToHost("GK2Coop.ItemScripts", writer, (NetworkDelivery)4); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Info(string.Format("{0}: {1} script call(s) that change the shared world sent to the host's game: {2}.", context, forHost.Count, string.Join(" | ", forHost.Select((Call c) => c.Raw)))); } forHost.Clear(); context = null; } public static bool BeforeEvaluate(LazyExpression expression, Item item) { return Keep(expression, 0, item, null); } public static bool BeforeEvaluateNothing(LazyExpression expression) { return Keep(expression, 1, null, null); } public static bool BeforeEvaluateOn(LazyExpression expression, WgoData wgo) { object wgo2; if (wgo == null) { wgo2 = null; } else { SGuid uniqueId = wgo.UniqueId; wgo2 = ((uniqueId != null) ? uniqueId.Id : null); } return Keep(expression, 2, null, (string)wgo2); } private static bool Keep(LazyExpression expression, byte kind, Item item, string wgo) { if (depth == 0 || expression == null || !ClientActive) { return true; } string text = ((LazyExpressionBase)expression).GetRawExpressionString() ?? ""; if (PartnerReplay.SharedWorldCall(text) == null) { return true; } if (!toHost) { CoopLog.Info(context + ": left out '" + text + "' (the intro: the host's own copy did it)."); return false; } if (!allowStory && PartnerReplay.StartsStory(text)) { CoopLog.Info(context + ": left out '" + text + "' (a story step; the host's game does those)."); return false; } forHost.Add(new Call { Raw = text, Kind = kind, Item = item, Wgo = wgo }); return false; } public static void OnItemScripts(ulong sender, FastBufferReader reader) { //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_002e: 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_0058: 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_018c: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (b != 1 || CoopSession.Role != CoopRole.Host) { return; } string text = CoopMessages.ReadString(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List list = new List(); byte kind = default(byte); for (int i = 0; i < num; i++) { string text2 = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref kind, default(ForPrimitives)); byte[] array = CoopMessages.ReadBytes(ref reader); string wgo = CoopMessages.ReadString(ref reader); Item item = null; try { item = ((array != null && array.Length != 0) ? SerializationUtility.DeserializeValue(array, (DataFormat)0, (DeserializationContext)null) : null); } catch (Exception ex) { CoopLog.Warn("Reading the item for '" + text2 + "' failed: " + ex.Message); } list.Add(new Call { Raw = text2, Kind = kind, Item = item, Wgo = wgo }); } RemotePlayer remotePlayer = PlayerSync.Get(sender); string text3 = remotePlayer?.Label ?? $"Player {sender}"; PlayerData val = (((Object)(object)remotePlayer?.Controller != (Object)null) ? remotePlayer.Controller.PlayerData : null); PlayerData playerData = MainGame.PlayerData; GameRes val2 = null; if (val != null) { val2 = ResField.Invoke(val); ResField.Invoke(val) = ResField.Invoke(playerData); val.position.Value = remotePlayer.Target; val.currentGameSceneId = remotePlayer.Scene; try { OverheadField.Invoke(val) = ((remotePlayer.CarryBytes == null) ? new List() : (SerializationUtility.DeserializeValue>(remotePlayer.CarryBytes, (DataFormat)0, (DeserializationContext)null) ?? new List())); } catch { OverheadField.Invoke(val) = new List(); } PlayerDataSetter.Invoke(null, new object[1] { val }); } try { foreach (Call item2 in list) { try { LazyExpression val3 = LazyExpressionBase.ParseExpression(item2.Raw); if (val3 == null) { continue; } switch (item2.Kind) { case 1: val3.Evaluate(); break; case 2: { object obj2; if (!string.IsNullOrEmpty(item2.Wgo)) { WorldData worldData = MainGame.WorldData; obj2 = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(item2.Wgo)) : null); } else { obj2 = null; } WgoData val4 = (WgoData)obj2; if (val4 == null) { CoopLog.Warn(text3 + "'s '" + item2.Raw + "' (" + text + ") is for an object that isn't here; skipped."); continue; } val3.EvaluateBool(val4); break; } default: val3.Evaluate(item2.Item); break; } Ran++; CoopLog.Info("Ran " + text3 + "'s '" + item2.Raw + "' (" + text + ") here."); } catch (Exception e) { CoopLog.Error("Running " + text3 + "'s '" + item2.Raw + "' (" + text + ") failed", e); } } } finally { if (val != null) { ResField.Invoke(val) = val2; PlayerDataSetter.Invoke(null, new object[1] { playerData }); try { RefreshOverheadMethod?.Invoke(playerData, null); } catch { } } } } public static string Describe() { return $"sent={Sent} ran={Ran}"; } } [HarmonyPatch(typeof(LazyExpression), "Evaluate", new Type[] { typeof(Item) })] internal static class PartnerItemScriptCalls { private static bool Prefix(LazyExpression __instance, Item item, ref string __result) { if (ItemScripts.BeforeEvaluate(__instance, item)) { return true; } __result = ""; return false; } } [HarmonyPatch(typeof(LazyExpression), "Evaluate", new Type[] { })] internal static class PartnerScriptCallsOnNothing { private static bool Prefix(LazyExpression __instance, ref string __result) { if (ItemScripts.BeforeEvaluateNothing(__instance)) { return true; } __result = ""; return false; } } [HarmonyPatch(typeof(LazyExpression), "EvaluateBool", new Type[] { typeof(WgoData) })] internal static class PartnerScriptCallsOnObjects { private static bool Prefix(LazyExpression __instance, WgoData wgoData, ref bool __result) { if (ItemScripts.BeforeEvaluateOn(__instance, wgoData)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(PlayerData), "UseItem")] internal static class PartnerItemUse { private static void Prefix(PlayerData __instance, Item item) { if (__instance == MainGame.PlayerData) { ItemScripts.Begin("Using " + ((ObjectLinkedToDefinition)(object)item)?.id, item); } } private static void Finalizer(PlayerData __instance) { if (__instance == MainGame.PlayerData) { ItemScripts.End(); } } } [HarmonyPatch(typeof(PlayerData), "CollectDrop")] internal static class PartnerItemPickup { private static void Prefix(PlayerData __instance, DropView dropView) { if (__instance == MainGame.PlayerData) { object obj; if (dropView == null) { obj = null; } else { DropData data = dropView.Data; obj = ((data == null) ? null : ((ObjectLinkedToDefinition)(object)data.Item)?.id); } string what = "Picking up " + (string?)obj; object item; if (dropView == null) { item = null; } else { DropData data2 = dropView.Data; item = ((data2 != null) ? data2.Item : null); } ItemScripts.Begin(what, (Item)item); } } private static void Finalizer(PlayerData __instance) { if (__instance == MainGame.PlayerData) { ItemScripts.End(); } } } [HarmonyPatch(typeof(PlayerData), "CollectResDrop")] internal static class PartnerResPickup { private static void Prefix(PlayerData __instance, DropData drop) { if (__instance == MainGame.PlayerData) { ItemScripts.Begin("Picking up " + ((drop == null) ? null : ((ObjectLinkedToDefinition)(object)drop.Item)?.id), (drop != null) ? drop.Item : null); } } private static void Finalizer(PlayerData __instance) { if (__instance == MainGame.PlayerData) { ItemScripts.End(); } } } [HarmonyPatch(typeof(Trading), "DoAcceptDeal")] internal static class PartnerTradeScripts { private static void Prefix() { ItemScripts.Begin("A deal"); } private static void Finalizer() { ItemScripts.End(); } } [HarmonyPatch(typeof(ConnectionManager), "ChangeState")] internal static class SafeConnectionStateChange { private static bool Prefix(ConnectionState nextState, ref ConnectionState ___currentState) { CoopLog.Info("Connection state " + ((object)___currentState)?.GetType().Name + " -> " + ((object)nextState).GetType().Name); try { ConnectionState obj = ___currentState; if (obj != null) { obj.Exit(); } } catch (Exception ex) { CoopLog.Warn("Leaving " + ((object)___currentState)?.GetType().Name + " threw: " + ex.Message); } ___currentState = nextState; try { nextState.Enter(); } catch (Exception e) { CoopLog.Error("Entering " + ((object)nextState).GetType().Name + " threw", e); } return false; } } [HarmonyPatch(typeof(PlayerPhysicalBody), "InitNetworkPlayer")] internal static class RemoteBodyKinematic { private static void Postfix(PlayerPhysicalBody __instance) { if ((Object)(object)__instance.Rb != (Object)null) { __instance.Rb.isKinematic = true; } } } [HarmonyPatch(typeof(CharMoveCommand), "Execute")] internal static class IgnoreGameMoveCommand { private static bool Prefix() { return false; } } [HarmonyPatch(typeof(Microphone), "SetTarget")] internal static class KeepAudioOnLocalPlayer { private static bool Prefix(Transform target) { return !PlayerSync.IsAvatarTransform(target); } } [HarmonyPatch(typeof(MainGame), "PauseGame")] internal static class NoMenuPauseInCoop { private static bool Prefix() { if (CoopSession.KeepWorldRunning) { CoopLog.Debug("A window opened; not pausing the world during a co-op session."); return false; } return true; } } [HarmonyPatch(typeof(SaveSystem), "Save")] internal static class CoopSaveGuard { private static bool partnersIn; private static bool Prefix(GameSave gameSave, ref Action callbackSuccessful, ref NetworkPlayersStash __state) { if (CoopSession.BlockSaves) { CoopLog.Info("Skipped a save: this game is a co-op copy of the host's world."); callbackSuccessful?.Invoke(); return false; } __state = NetworkPlayersStash.Strip(gameSave); partnersIn = false; try { int count = PartnerProfiles.PutIntoSave(gameSave); partnersIn = count > 0; Action original = callbackSuccessful; callbackSuccessful = delegate { PartnerProfiles.Saved(count); original?.Invoke(); }; } catch (Exception e) { CoopLog.Error("Putting partners into the save failed", e); } return true; } private static void Finalizer(GameSave gameSave, NetworkPlayersStash __state) { if (partnersIn) { gameSave?.clientPlayers?.Clear(); partnersIn = false; } __state?.Restore(gameSave); } } [HarmonyPatch(typeof(MainGame), "ContinueGame")] internal static class PartnersFromSave { private static void Prefix(SaveSlotData saveSlotData, GameSave gameSave) { if (CoopSession.Role == CoopRole.Client) { return; } try { PartnerProfiles.OnWorldLoading(gameSave, saveSlotData?.slotName); } catch (Exception e) { CoopLog.Error("Reading partners from the save failed", e); } } } [HarmonyPatch(typeof(MainGame), "CreateGameSaveAndStart")] internal static class NoPartnersInNewGame { private static void Prefix() { if (CoopSession.Role == CoopRole.Client) { return; } try { MainGame instance = MainGame.Instance; PartnerProfiles.OnNewGame((instance == null) ? null : instance.SaveSlotData?.slotName); } catch (Exception e) { CoopLog.Error("Starting the new game's partner list failed", e); } } } [HarmonyPatch(typeof(WgoData), "RunLogicsAfterDeath")] internal static class ClientRunsOwnWorldLogic { private static void Prefix(WgoData __instance, out bool __state) { WorldSync.NoteDeathRun(__instance); __state = CoopSession.Role == CoopRole.Client && !Plugin.HostRunsWorld.Value && CoopNetwork.SuspendCoopFlag(); } private static void Finalizer(bool __state) { if (__state) { CoopNetwork.RestoreCoopFlag(); } } } [HarmonyPatch(typeof(CinematicsSceneDisplayManager), "DisplayCinematicsScene")] internal static class MirrorCinematicStart { private static void Prefix(string id) { StorySync.HostCinematicStarted(id); } } [HarmonyPatch(typeof(CinematicsSceneDisplayManager), "OnDirectorStopped")] internal static class MirrorCinematicEnd { private static void Postfix() { StorySync.HostCinematicEnded(); } } [HarmonyPatch(typeof(Bubble), "Talk")] internal static class MirrorSpeechLines { private static void Postfix(PhraseData data, bool __runOriginal) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!__runOriginal) { return; } try { StorySync.HostPhrase(data); } catch (Exception e) { CoopLog.Error("Mirroring a speech line failed", e); } } } [HarmonyPatch(typeof(UIMultiAnswer), "OnAnswerSelect")] internal static class MirrorAnswerChosen { private static void Postfix(string answerId) { try { StorySync.HostAnswerChosen(answerId); } catch (Exception e) { CoopLog.Error("Mirroring the chosen answer failed", e); } } } [HarmonyPatch] internal static class MirrorAnswers { private static IEnumerable TargetMethods() { return (from m in typeof(UIMultiAnswer).GetMethods(BindingFlags.Static | BindingFlags.Public) where m.Name == "ShowAnswers" && m.GetParameters().Length != 0 && m.GetParameters()[0].ParameterType == typeof(List) && m.GetParameters()[0].Name == "answers" select m).Cast(); } private static void Prefix(List answers, object[] __args) { try { StorySync.HostAnswers(answers, __args); } catch (Exception e) { CoopLog.Error("Mirroring answer choices failed", e); } } } [HarmonyPatch(typeof(ScriptInteractionHandler), "Interact")] internal static class StoryInteractionGate { private static bool Prefix(Wgo ___assignedWgo, ref bool __result) { try { if (!StorySync.AllowInteraction(___assignedWgo)) { __result = true; return false; } } catch (Exception e) { CoopLog.Error("Story interaction gate failed; letting the interaction through", e); } return true; } } internal sealed class RemotePlayer { public ulong ClientId; public string Name = ""; public byte[] Customization; public bool HasState; public Vector3 Target; public Vector2 Direction; public int Anim; public string Scene = ""; public float LastStateTime; public uint LastSeq; public float NextSpawnAttempt; public byte[] CarryBytes; public bool Transitioning; public bool StandingIn; public bool ActsAsHost; public bool ControlsFree; public bool WatchReady; public bool MovementLocked; public byte Gear; public PlayerPhysicalBody Body; public PlayerController Controller; public int AppliedAnim = -1; public Vector2 AppliedDirection; public bool Visible; public string Label { get { if (string.IsNullOrEmpty(Name)) { if (ClientId != 0L) { return $"Player {ClientId}"; } return "Host"; } return Name; } } public bool IsSpawned => (Object)(object)Body != (Object)null; public Vector3 Position { get { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Body != (Object)null)) { return Target; } return ((Component)Body).transform.position; } } } internal static class PlayerSync { public const string AvatarPrefix = "GK2Coop Avatar"; private const byte OpInfo = 1; private const byte OpLeft = 2; private const byte OpCarry = 3; private const float CarryCheckInterval = 0.2f; private const float SendInterval = 0.05f; private const float HeartbeatInterval = 1f; private const float SnapDistance = 6f; private static readonly FieldRef PlayerPrefabField = AccessTools.FieldRefAccess("playerPrefab"); private static readonly FieldRef MovementLockedField = AccessTools.FieldRefAccess("isMovementLocked"); private static readonly FieldRef ArmorViewField = AccessTools.FieldRefAccess("isArmorViewActive"); private static readonly FieldRef ArmorHelmetField = AccessTools.FieldRefAccess("armorViewUsesHelmet"); private static readonly MethodInfo CopyPartMethod = AccessTools.Method(typeof(PlayerSkinHelper), "CopyPart", (Type[])null, (Type[])null); private static byte lastSentGear; private static readonly Dictionary remotes = new Dictionary(); private static float nextSend; private static float lastSentTime; private static Vector3 lastSentPosition; private static Vector2 lastSentDirection; private static int lastSentAnim = -1; private static string lastSentScene; private static byte lastSentFlags; private static uint sequence; private static float nextCarryCheck; private static string lastCarrySignature = ""; private static float nextLookCheck; private static byte[] lastLook; private static float refreshLooksAt = -1f; public static IEnumerable Remotes => remotes.Values; public static bool LocalInWorld { get { if (CoopSession.IsInGame) { PlayerController playerController = MainGame.PlayerController; if (((playerController != null) ? playerController.PlayerData : null) != null) { if (CoopSession.Role != CoopRole.Host) { if (CoopSession.Role == CoopRole.Client) { return CoopSession.ClientInWorld; } return false; } return true; } } return false; } } public static RemotePlayer Host => Get(0uL); public static string LastHostTrigger { get; private set; } public static float LastHostTriggerAt { get; private set; } public static RemotePlayer Get(ulong clientId) { remotes.TryGetValue(clientId, out var value); return value; } public static bool IsAvatarTransform(Transform t) { if ((Object)(object)t != (Object)null && (Object)(object)t.root != (Object)null) { return ((Object)t.root).name.StartsWith("GK2Coop Avatar"); } return false; } public static void Tick() { TickLookRefresh(); if (!LocalInWorld) { return; } SendLocalState(); CheckLocalCarry(); CheckLocalLook(); foreach (RemotePlayer value in remotes.Values) { UpdateAvatar(value); } } private static void CheckLocalLook() { if (Time.unscaledTime < nextLookCheck) { return; } nextLookCheck = Time.unscaledTime + 2f; PlayerController playerController = MainGame.PlayerController; PlayerCustomizationData val = ((playerController == null) ? null : playerController.PlayerData?.customization); if (val != null) { byte[] array; try { array = SerializationUtility.SerializeValue(val, (DataFormat)0, (SerializationContext)null); } catch { return; } if (lastLook == null) { lastLook = array; } else if (!((ReadOnlySpan)array).SequenceEqual((ReadOnlySpan)lastLook)) { lastLook = array; CoopLog.Info("Our look changed; sending it to everyone."); SendLocalInfo(); } } } public static void OpenCustomization() { //IL_0006: 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_0021: Expected O, but got Unknown UICustomizationWindowData val = new UICustomizationWindowData { CurrentData = PlayerCustomizationData.Copy(MainGame.PlayerData.customization) }; UICustomizationWindow window = LazyUI.GetWindow(); Action applied = null; Action cancelled = null; applied = delegate(PlayerCustomizationData customization) { window.OnCustomizationApplied -= applied; window.OnCustomizationCanceled -= cancelled; MainGame.PlayerData.ApplyCustomization(customization); nextLookCheck = 0f; CoopLog.Info("Applied a new look from the customization window."); }; cancelled = delegate { window.OnCustomizationApplied -= applied; window.OnCustomizationCanceled -= cancelled; }; window.OnCustomizationApplied += applied; window.OnCustomizationCanceled += cancelled; ((LazyWindow)(object)window).Open(val); } private static List LocalCarry() { PlayerController playerController = MainGame.PlayerController; object obj; if (playerController == null) { obj = null; } else { PlayerData playerData = playerController.PlayerData; obj = ((playerData != null) ? playerData.OverheadItems : null); } IReadOnlyList readOnlyList = (IReadOnlyList)obj; if (readOnlyList != null) { return readOnlyList.Where((Item i) => i != null && !i.IsEmpty).ToList(); } return new List(); } private static string CarrySignature(List items) { return string.Join(",", items.Select((Item i) => ((ObjectLinkedToDefinition)(object)i).id + "x" + i.Count + "#" + (object)i.UniqueId)); } private static void CheckLocalCarry() { if (Time.unscaledTime < nextCarryCheck) { return; } nextCarryCheck = Time.unscaledTime + 0.2f; List list = LocalCarry(); string text = CarrySignature(list); if (!(text == lastCarrySignature)) { lastCarrySignature = text; CoopLog.Debug("Now carrying [" + string.Join(",", list.Select((Item i) => ((ObjectLinkedToDefinition)(object)i).id + "x" + i.Count)) + "]."); SendCarry(CoopNetwork.LocalClientId, PackCarry(list), null); } } private static byte[] PackCarry(List items) { try { return SerializationUtility.SerializeValue>(items ?? new List(), (DataFormat)0, (SerializationContext)null); } catch (Exception ex) { CoopLog.Warn("Packing carried items failed: " + ex.Message); return SerializationUtility.SerializeValue>(new List(), (DataFormat)0, (SerializationContext)null); } } private unsafe static void SendCarry(ulong source, byte[] carry, ulong? onlyTo, ulong? except = null) { //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_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_0010: 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_0022: 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_0043: 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_0061: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref source, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, carry); if (CoopSession.Role == CoopRole.Client) { CoopMessages.SendToHost("GK2Coop.Info", writer, (NetworkDelivery)3); } else if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Info", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Info", writer, (NetworkDelivery)3, except); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public static void RefreshCarry(RemotePlayer remote) { ApplyCarry(remote); } private static void ApplyCarry(RemotePlayer remote) { if (!remote.IsSpawned || (Object)(object)remote.Controller == (Object)null) { return; } try { List list = ((remote.CarryBytes == null) ? new List() : (SerializationUtility.DeserializeValue>(remote.CarryBytes, (DataFormat)0, (DeserializationContext)null) ?? new List())); if (list.Count == 0) { remote.Controller.RemoveOverheadItem(); } else { remote.Controller.SetOverheadItems((IReadOnlyList)list); } } catch (Exception e) { CoopLog.Error("Showing what " + remote.Label + " carries failed", e); } } private unsafe static void SendLocalState() { //IL_004e: 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_00c6: 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_0055: 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_00df: Expected I4, but got Unknown //IL_00d8: 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_0086: 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_0098: 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) //IL_0196: 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_01a0: 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_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_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0256: 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_025d: Unknown result type (might be due to invalid IL or missing references) //IL_027c: 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_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0285: 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_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (unscaledTime < nextSend) { return; } nextSend = unscaledTime + 0.05f; PlayerController playerController = MainGame.PlayerController; PlayerData playerData = playerController.PlayerData; Vector3 val = (((Object)(object)playerController.PhysicalBody != (Object)null) ? ((Component)playerController.PhysicalBody).transform.position : playerData.position.Value); Vector2 val2 = playerData.Direction; try { PlayerView view = playerController.View; if ((Object)(object)((view != null) ? view.PlayerAnimation : null) != (Object)null) { Vector2 direction = ((AnimationComponentBase)playerController.View.PlayerAnimation).GetDirection(); if (((Vector2)(ref direction)).sqrMagnitude > 0.0001f) { val2 = direction; } } } catch { } int num = (int)(((Object)(object)playerController.View != (Object)null && (Object)(object)playerController.View.PlayerAnimation != (Object)null) ? ((AnimationComponentBase)playerController.View.PlayerAnimation).GetState() : playerData.charState.Value); string text = playerData.currentGameSceneId ?? ""; byte b = ((!playerController.IsControlEnabledByType((TakenControlType)4)) ? ((byte)1) : ((byte)0)); if (StandIn.Active) { b |= 2; } if (StandIn.HidesOwnCharacter) { b |= 4; } if (StandIn.CameraReady) { b |= 0x10; } if (CoopSession.Role == CoopRole.Host && playerController.IsControlEnabledByType((TakenControlType)3) && playerController.IsControlEnabledByType((TakenControlType)11) && playerController.IsControlEnabledByType((TakenControlType)4) && playerController.IsControlEnabledByType((TakenControlType)5)) { b |= 8; } if (CoopSession.Role == CoopRole.Host && (Object)(object)playerController.PhysicalBody != (Object)null && MovementLockedField.Invoke(playerController.PhysicalBody)) { b |= 0x20; } byte b2 = OurGear(playerController); Vector3 val3 = val - lastSentPosition; int num2; if (!(((Vector3)(ref val3)).sqrMagnitude > 0.0004f) && num == lastSentAnim) { Vector2 val4 = val2 - lastSentDirection; if (!(((Vector2)(ref val4)).sqrMagnitude > 0.0001f) && !(text != lastSentScene) && b == lastSentFlags) { num2 = ((b2 != lastSentGear) ? 1 : 0); goto IL_01fb; } } num2 = 1; goto IL_01fb; IL_01fb: lastSentFlags = b; lastSentGear = b2; if (num2 == 0 && unscaledTime - lastSentTime < 1f) { return; } if (text != lastSentScene) { CoopLog.Info($"Local player is now in scene '{text}' at {val}."); refreshLooksAt = Time.unscaledTime + 1f; } lastSentTime = unscaledTime; lastSentPosition = val; lastSentDirection = val2; lastSentAnim = num; lastSentScene = text; sequence++; FastBufferWriter val5 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val5; WriteState(ref writer, CoopNetwork.LocalClientId, sequence, val, val2, num, text, b, b2); if (CoopSession.Role == CoopRole.Host) { CoopMessages.SendToReadyClients("GK2Coop.State", writer, (NetworkDelivery)1); } else { CoopMessages.SendToHost("GK2Coop.State", writer, (NetworkDelivery)1); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val5))/*cast due to .constrained prefix*/).Dispose(); } } private static void WriteState(ref FastBufferWriter writer, ulong source, uint seq, Vector3 position, Vector2 direction, int anim, string scene, byte flags, byte gear) { //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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0039: 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_004f: 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_0060: 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_0079: 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_008a: Unknown result type (might be due to invalid IL or missing references) ((FastBufferWriter)(ref writer)).WriteValueSafe(ref source, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref seq, default(ForPrimitives)); CoopMessages.WriteVector3(ref writer, position); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref direction.x, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref direction.y, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref anim, default(ForPrimitives)); CoopMessages.WriteString(ref writer, scene); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref flags, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref gear, default(ForPrimitives)); } private static byte OurGear(PlayerController controller) { try { if ((Object)(object)controller == (Object)null || !ArmorViewField.Invoke(controller)) { return 0; } int result = 0; PlayerData playerData = controller.PlayerData; object obj; if (playerData == null) { obj = null; } else { Inventory toolBeltInventory = playerData.toolBeltInventory; if (toolBeltInventory == null) { obj = null; } else { Item data = toolBeltInventory.Data; obj = ((data != null) ? data.GetItemByType((ItemType)12) : null); } } Item val = (Item)obj; if (val != null && !val.IsEmpty && ((ObjectLinkedToDefinition)(object)val).id != null) { int num = ((ObjectLinkedToDefinition)(object)val).id.LastIndexOf('_'); if (num >= 0) { int.TryParse(((ObjectLinkedToDefinition)(object)val).id.Substring(num + 1), out result); } } AttackComponent attackComponent = controller.AttackComponent; bool flag = (Object)(object)attackComponent != (Object)null && (Object)(object)attackComponent.weapon != (Object)null; bool flag2 = flag && attackComponent.IsRangedWeapon; return (byte)(1 | (ArmorHelmetField.Invoke(controller) ? 2 : 0) | ((flag && !flag2) ? 4 : 0) | (flag2 ? 8 : 0) | (Mathf.Clamp(result, 0, 15) << 4)); } catch { return 0; } } public static void SendLocalInfo(ulong? onlyTo = null) { PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); if (val != null) { byte[] customization = null; try { customization = SerializationUtility.SerializeValue(val.customization, (DataFormat)0, (SerializationContext)null); } catch (Exception ex) { CoopLog.Warn("Couldn't serialize the player's outfit: " + ex.Message); } SendInfo(CoopNetwork.LocalClientId, Plugin.PlayerName.Value, customization, onlyTo); } } private unsafe static void SendInfo(ulong source, string name, byte[] customization, ulong? onlyTo, ulong? except = null) { //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_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_0010: 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_0022: 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_004b: 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_0069: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref source, default(ForPrimitives)); CoopMessages.WriteString(ref writer, name); CoopMessages.WriteBytes(ref writer, customization); if (CoopSession.Role == CoopRole.Client) { CoopMessages.SendToHost("GK2Coop.Info", writer, (NetworkDelivery)3); } else if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Info", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Info", writer, (NetworkDelivery)3, except); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public static void SendAllInfoTo(ulong clientId) { SendLocalInfo(clientId); SendCarry(CoopNetwork.LocalClientId, PackCarry(LocalCarry()), clientId); foreach (RemotePlayer item in remotes.Values.Where((RemotePlayer r) => r.ClientId != clientId)) { SendInfo(item.ClientId, item.Name, item.Customization, clientId); SendCarry(item.ClientId, item.CarryBytes ?? PackCarry(null), clientId); } } public unsafe static void BroadcastLeft(ulong clientId) { //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_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_0010: 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_0022: 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_0033: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref clientId, default(ForPrimitives)); CoopMessages.SendToReadyClients("GK2Coop.Info", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public unsafe static void OnState(ulong sender, FastBufferReader reader) { //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_0019: 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_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_0034: 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_0047: 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_005a: 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_0076: 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_0089: 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_00a0: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00ca: 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_016c: 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_017b: 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) //IL_0120: Unknown result type (might be due to invalid IL or missing references) ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); uint num2 = default(uint); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); Vector3 val = CoopMessages.ReadVector3(ref reader); float num3 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); float num4 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num4, default(ForPrimitives)); int anim = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref anim, default(ForPrimitives)); string text = CoopMessages.ReadString(ref reader); byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Host) { num = sender; FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; WriteState(ref writer, num, num2, val, new Vector2(num3, num4), anim, text, b, b2); CoopMessages.SendToReadyClients("GK2Coop.State", writer, (NetworkDelivery)1, sender); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } if (num == CoopNetwork.LocalClientId) { return; } RemotePlayer orCreate = GetOrCreate(num); if (orCreate.HasState && orCreate.Scene != text) { CoopLog.Info($"{orCreate.Label} moved to scene '{text}' at {val}."); } else if (!orCreate.HasState) { CoopLog.Info($"First position from {orCreate.Label}: {val} in scene '{text}'."); } orCreate.HasState = true; orCreate.LastSeq = num2; orCreate.Target = val; orCreate.Direction = new Vector2(num3, num4); orCreate.Anim = anim; orCreate.Scene = text; if (orCreate.Transitioning != ((b & 1) != 0)) { orCreate.Transitioning = (b & 1) != 0; CoopLog.Debug(orCreate.Label + " " + (orCreate.Transitioning ? "started" : "finished") + " changing location."); } orCreate.ActsAsHost = (b & 4) != 0; orCreate.ControlsFree = (b & 8) != 0; orCreate.WatchReady = (b & 0x10) != 0; if (orCreate.Gear != b2) { bool flag = (orCreate.Gear & -13) != (b2 & -13); orCreate.Gear = b2; string arg = (((b2 & 4) != 0) ? ", sword out" : (((b2 & 8) != 0) ? ", bow out" : "")); CoopLog.Debug(orCreate.Label + " " + (((b2 & 1) != 0) ? string.Format("has armor {0} on{1}{2}", b2 >> 4, ((b2 & 2) != 0) ? " with the helmet" : "", arg) : "took the armor off") + "."); if (orCreate.IsSpawned) { if (flag) { ApplySkin(orCreate, refresh: true); } else { ApplyArmorLayers(orCreate.Body.PlayerView, b2); } } } if (orCreate.MovementLocked != ((b & 0x20) != 0)) { orCreate.MovementLocked = (b & 0x20) != 0; CoopLog.Debug(orCreate.Label + "'s walking is " + (orCreate.MovementLocked ? "locked by the story" : "free again") + "."); } if (orCreate.StandingIn != ((b & 2) != 0)) { orCreate.StandingIn = (b & 2) != 0; CoopLog.Debug(orCreate.Label + " " + (orCreate.StandingIn ? "is watching the story with the host" : "is back on their own") + "."); if (orCreate.StandingIn && CoopSession.Role == CoopRole.Host) { StandIn.OnPartnerStandingIn(orCreate.ClientId); SceneWait.NoteStandingIn(orCreate.ClientId); } else if (!orCreate.StandingIn && CoopSession.Role == CoopRole.Host) { SceneWait.OnPartnerStoppedStandingIn(orCreate.ClientId); } } orCreate.LastStateTime = Time.unscaledTime; } public static void OnInfo(ulong sender, FastBufferReader reader) { //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_001d: 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_00c2: 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_003f: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 2: { ulong clientId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref clientId, default(ForPrimitives)); Remove(clientId); return; } case 3: { ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); byte[] array = CoopMessages.ReadBytes(ref reader); if (CoopSession.Role == CoopRole.Host) { num = sender; SendCarry(num, array, null, sender); } if (num != CoopNetwork.LocalClientId) { RemotePlayer orCreate = GetOrCreate(num); orCreate.CarryBytes = array; CoopLog.Debug($"{orCreate.Label}'s carried items changed ({array.Length} bytes)."); ApplyCarry(orCreate); } return; } } ulong num2 = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); string text = CoopMessages.ReadString(ref reader); byte[] array2 = CoopMessages.ReadBytes(ref reader); if (CoopSession.Role == CoopRole.Host) { num2 = sender; SendInfo(num2, text, array2, null, sender); } if (num2 != CoopNetwork.LocalClientId) { RemotePlayer orCreate2 = GetOrCreate(num2); orCreate2.Name = text ?? ""; orCreate2.Customization = ((array2 != null && array2.Length != 0) ? array2 : null); CoopLog.Info($"Player info from {orCreate2.Label} (id {num2}), outfit {((array2 != null) ? array2.Length : 0)} bytes."); if (orCreate2.IsSpawned) { ApplySkin(orCreate2, refresh: true); } } } private static RemotePlayer GetOrCreate(ulong clientId) { if (!remotes.TryGetValue(clientId, out var value)) { value = new RemotePlayer { ClientId = clientId }; remotes[clientId] = value; } return value; } private static void UpdateAvatar(RemotePlayer remote) { //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: 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_0139: 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0178: 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_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_019b: 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_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: 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) if (!remote.HasState) { return; } if (!remote.IsSpawned) { if (Time.unscaledTime >= remote.NextSpawnAttempt) { remote.NextSpawnAttempt = Time.unscaledTime + 2f; Spawn(remote); } if (!remote.IsSpawned) { return; } } string currentGameSceneId = MainGame.PlayerController.PlayerData.currentGameSceneId; bool flag = !remote.Transitioning && !remote.ActsAsHost && !StandIn.HidesAvatarOf(remote) && (string.IsNullOrEmpty(remote.Scene) || string.IsNullOrEmpty(currentGameSceneId) || remote.Scene == currentGameSceneId); if (flag != remote.Visible) { remote.Visible = flag; SetDrawn(remote, flag); CoopLog.Debug(remote.Label + " avatar " + (flag ? "shown" : "hidden") + " (their scene '" + remote.Scene + "', ours '" + currentGameSceneId + "')."); if (flag) { remote.AppliedAnim = -1; remote.AppliedDirection = Vector2.zero; ApplyCarry(remote); } } if (!flag) { return; } PlayerPhysicalBody body = remote.Body; Vector3 position = ((Component)body).transform.position; Vector3 val = remote.Target - position; Vector3 position2 = ((((Vector3)(ref val)).sqrMagnitude > 36f) ? remote.Target : Vector3.Lerp(position, remote.Target, 1f - Mathf.Exp(-14f * Time.deltaTime))); ((Component)body).transform.position = position2; if ((Object)(object)body.Rb != (Object)null) { body.Rb.position = position2; } PlayerView playerView = body.PlayerView; if (remote.Anim != remote.AppliedAnim) { remote.AppliedAnim = remote.Anim; ((AnimationComponentBase)playerView.PlayerAnimation).SetState((AnimationState)remote.Anim); } if (((Vector2)(ref remote.Direction)).sqrMagnitude > 0.0001f) { Vector2 val2 = remote.Direction - remote.AppliedDirection; if (((Vector2)(ref val2)).sqrMagnitude > 0.0001f) { remote.AppliedDirection = remote.Direction; playerView.UpdateAnimationDirection(remote.Direction); } } } private static void Spawn(RemotePlayer remote) { //IL_0013: 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_002b: Expected O, but got Unknown //IL_0211: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) try { PlayerData val = PlayerData.CreatePlayerData(); val.TryApplyStartState(); val.position.Value = remote.Target; NetworkPlayer val2 = new NetworkPlayer((int)remote.ClientId, val); GameObject val3 = PlayerPrefabField.Invoke(MainGame.Instance); GameObject val4 = (((Object)(object)val3 != (Object)null) ? Object.Instantiate(val3) : CloneLocalPlayer()); ((Object)val4).name = string.Format("{0} {1}", "GK2Coop Avatar", remote.ClientId); SceneManager.MoveGameObjectToScene(val4, ((Component)MainGame.Instance).gameObject.scene); PlayerPhysicalBody val5 = val4.GetComponent() ?? val4.GetComponentInChildren(true); PlayerController playerController = MainGame.PlayerController; WispController val6 = ((playerController != null) ? playerController.WispController : null); bool flag = (Object)(object)val6 != (Object)null && ((Component)val6).gameObject.activeSelf; val5.InitNetworkPlayer(val2); remote.Body = val5; remote.Controller = val4.GetComponent(); val5.PlayerView.ControllingWispView = false; DisableGameplayParts(val4, remote.Controller); if ((Object)(object)val6 != (Object)null && ((Component)val6).gameObject.activeSelf != flag) { ((Component)val6).gameObject.SetActive(flag); CoopLog.Info(remote.Label + "'s character shares our wisp object; left our wisp " + (flag ? "on" : "off") + "."); } val4.transform.position = remote.Target; val4.SetActive(true); remote.Visible = true; remote.AppliedAnim = -1; remote.AppliedDirection = Vector2.zero; ApplySkin(remote, refresh: false); ApplyCarry(remote); IgnoreCollisions(remote); if (CoopSession.Role == CoopRole.Client && remote.ClientId == 0L && !string.IsNullOrEmpty(LastHostTrigger) && Time.unscaledTime - LastHostTriggerAt < 120f) { try { ((AnimationComponentBase)remote.Body.PlayerView.PlayerAnimation).SetTrigger(LastHostTrigger); CoopLog.Debug("Gave the host's new character their last pose '" + LastHostTrigger + "'."); } catch { } } CoopLog.Info(string.Format("Spawned {0}'s character at {1} (scene '{2}', prefab {3}).", remote.Label, remote.Target, remote.Scene, ((Object)(object)val3 != (Object)null) ? "playerPrefab" : "clone")); } catch (Exception e) { CoopLog.Error("Spawning " + remote.Label + "'s character failed", e); if ((Object)(object)remote.Body != (Object)null) { Object.Destroy((Object)(object)((Component)remote.Body).gameObject); } remote.Body = null; } } private static GameObject CloneLocalPlayer() { CoopLog.Warn("MainGame.playerPrefab is missing; cloning the local player instead."); GameObject val = Object.Instantiate(((Component)MainGame.PlayerController).gameObject); AudioListener[] componentsInChildren = val.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } Camera[] componentsInChildren2 = val.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { ((Behaviour)componentsInChildren2[i]).enabled = false; } return val; } private static void DisableGameplayParts(GameObject avatar, PlayerController controller) { Collider[] componentsInChildren = avatar.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } Behaviour[] componentsInChildren2 = avatar.GetComponentsInChildren(true); foreach (Behaviour val in componentsInChildren2) { if ((Object)(object)val != (Object)null && ((object)val).GetType().Name.StartsWith("RVO")) { val.enabled = false; } } if (!((Object)(object)controller != (Object)null)) { return; } if ((Object)(object)controller.PlayerInteractionComponent != (Object)null) { ((Behaviour)controller.PlayerInteractionComponent).enabled = false; } try { WispController wispController = controller.WispController; if ((Object)(object)wispController != (Object)null && ((Component)wispController).transform.IsChildOf(avatar.transform)) { ((Component)wispController).gameObject.SetActive(false); } } catch { } } private static void SetDrawn(RemotePlayer remote, bool drawn) { if ((Object)(object)remote.Body == (Object)null) { return; } Renderer[] componentsInChildren = ((Component)remote.Body).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val != (Object)null) { val.forceRenderingOff = !drawn; } } } private static void IgnoreCollisions(RemotePlayer remote) { PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); if ((Object)(object)val == (Object)null || (Object)(object)remote?.Body == (Object)null) { return; } List list = (from c in ((Component)val).GetComponentsInChildren(true) where (Object)(object)c != (Object)null && !c.isTrigger select c).ToList(); List list2 = (from c in ((Component)remote.Body).GetComponentsInChildren(true) where (Object)(object)c != (Object)null && !c.isTrigger select c).ToList(); foreach (Collider item in list) { foreach (Collider item2 in list2) { Physics.IgnoreCollision(item, item2, true); } } } private static void TickLookRefresh() { if (!(refreshLooksAt > 0f) || !(Time.unscaledTime >= refreshLooksAt)) { return; } refreshLooksAt = -1f; RefreshOtherLooks(); foreach (RemotePlayer value in remotes.Values) { IgnoreCollisions(value); } CoopLog.Debug("Re-applied the others' looks after changing area."); } public static void RefreshOtherLooks() { foreach (RemotePlayer value in remotes.Values) { if (value.IsSpawned) { try { ApplySkin(value, refresh: true); } catch (Exception ex) { CoopLog.Debug("Refreshing " + value.Label + "'s look failed: " + ex.Message); } } } } public static void NoteHostTrigger(string trigger) { LastHostTrigger = trigger; LastHostTriggerAt = Time.unscaledTime; } public static PlayerCustomizationData CustomizationOf(RemotePlayer remote) { if (remote?.Customization == null) { return null; } try { return SerializationUtility.DeserializeValue(remote.Customization, (DataFormat)0, (DeserializationContext)null); } catch { return null; } } public static void ApplyLook(PlayerView view, PlayerCustomizationData customization) { if (!((Object)(object)view == (Object)null) && customization != null) { SkinPresetGK2 presetForCustomizationData = PlayerSkinHelper.GetPresetForCustomizationData(customization); view.SetPlayerPreset(presetForCustomizationData, false); view.PlayerAnimation.ApplyPlayerColors(PlayerSkinHelper.GetColorReplacementPalette(customization).palette, PlayerSkinHelper.CharacterCustomizationData.affectedPartTypes, presetForCustomizationData); } } private static void ApplyArmorLook(PlayerView view, byte gear, SkinPresetGK2 own) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown try { if ((gear & 1) == 0) { ((AnimationComponent)view.PlayerAnimation).ResetArmorLayers(); return; } bool flag = (gear & 2) != 0; int num = (gear >> 4) & 0xF; SkinPresetGK2 val; if (flag) { val = Object.Instantiate(PlayerSkinHelper.ArmorPreset); } else { val = ScriptableObject.CreateInstance(); val.isPlayerPreset = true; val.body = (SkinPresetPartGK2)CopyPartMethod.Invoke(null, new object[1] { PlayerSkinHelper.ArmorPreset.body }); val.arms = (SkinPresetPartGK2)CopyPartMethod.Invoke(null, new object[1] { PlayerSkinHelper.ArmorPreset.arms }); val.head = (SkinPresetPartGK2)CopyPartMethod.Invoke(null, new object[1] { own.head }); val.hairstyle = (SkinPresetPartGK2)CopyPartMethod.Invoke(null, new object[1] { own.hairstyle }); val.beard = (SkinPresetPartGK2)CopyPartMethod.Invoke(null, new object[1] { own.beard }); } view.SetPlayerPreset(val, false); ArmorPresetData armorPresetData = PlayerSkinHelper.CharacterCustomizationData.armorPresetData; List list = (flag ? armorPresetData.affectedPartTypes : armorPresetData.affectedPartTypes.FindAll((CustomizablePartType t) => (int)t == 2 || (int)t == 3)); view.PlayerAnimation.ApplyPlayerColors(armorPresetData.GetColorReplacePalette(num).palette, list, val); ApplyArmorLayers(view, gear); } catch (Exception ex) { CoopLog.Debug("Showing armor on a player's character failed: " + ex.Message); } } private static void ApplyArmorLayers(PlayerView view, byte gear) { //IL_0014: 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_002c: Unknown result type (might be due to invalid IL or missing references) try { ((AnimationComponent)view.PlayerAnimation).ResetArmorLayers(); if ((gear & 1) != 0) { Layers val = (Layers)18; if ((gear & 2) != 0) { val = (Layers)(((gear & 4) != 0) ? 8 : (((gear & 8) != 0) ? 10 : 7)); } ((AnimationComponent)view.PlayerAnimation).SetLayerWeight(val, 1f); } } catch (Exception ex) { CoopLog.Debug("Showing a player's armor layer failed: " + ex.Message); } } private static void ApplySkin(RemotePlayer remote, bool refresh) { //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) try { PlayerCustomizationData val = null; if (remote.Customization != null) { val = SerializationUtility.DeserializeValue(remote.Customization, (DataFormat)0, (DeserializationContext)null); } val = val ?? MainGame.PlayerController.PlayerData.customization; if (val != null) { PlayerView playerView = remote.Body.PlayerView; SkinPresetGK2 presetForCustomizationData = PlayerSkinHelper.GetPresetForCustomizationData(val); playerView.SetPlayerPreset(presetForCustomizationData, false); playerView.PlayerAnimation.ApplyPlayerColors(PlayerSkinHelper.GetColorReplacementPalette(val).palette, PlayerSkinHelper.CharacterCustomizationData.affectedPartTypes, presetForCustomizationData); ApplyArmorLook(playerView, remote.Gear, presetForCustomizationData); remote.AppliedAnim = -1; remote.AppliedDirection = Vector2.zero; if (refresh && remote.Visible) { ApplyCarry(remote); } CoopLog.Debug("Applied outfit to " + remote.Label + " (" + ((remote.Customization != null) ? "theirs" : "ours as placeholder") + ")."); } } catch (Exception e) { CoopLog.Error("Applying " + remote.Label + "'s outfit failed", e); } } public static void Remove(ulong clientId) { if (remotes.TryGetValue(clientId, out var value)) { remotes.Remove(clientId); DestroyAvatar(value); CoopLog.Info("Removed " + value.Label + "."); } } public static void Clear() { foreach (RemotePlayer value in remotes.Values) { DestroyAvatar(value); } remotes.Clear(); lastCarrySignature = ""; lastLook = null; lastSentScene = null; lastSentAnim = -1; lastSentTime = 0f; } private static void DestroyAvatar(RemotePlayer remote) { if (!((Object)(object)remote.Body == (Object)null)) { TroopSync.OnCopyGone(remote); try { remote.Body.PlayerView.UnPrepareFromGame(); } catch { } ForgetInteractionHandler(remote.Controller); Object.Destroy((Object)(object)((Component)remote.Body).gameObject); remote.Body = null; } } private static void ForgetInteractionHandler(PlayerController controller) { PlayerInteractionComponent val = (((Object)(object)controller != (Object)null) ? controller.PlayerInteractionComponent : null); PlayerController playerController = MainGame.PlayerController; if ((Object)(object)val == (Object)null || (Object)(object)playerController == (Object)null) { return; } try { Action action = (Action)Delegate.CreateDelegate(typeof(Action), val, "OnPlayerControlStateChanged"); playerController.OnControlStateChanged -= action; } catch (Exception ex) { CoopLog.Warn("Couldn't unhook a puppet's interaction handler: " + ex.Message); } } private static bool ScreenCovered() { try { UIFade val = LazyUI.Get(); if ((Object)(object)val != (Object)null && ((UIBasicFade)val).IsFadeShowing) { return true; } UISleepFade val2 = LazyUI.Get(); if ((Object)(object)val2 != (Object)null && ((UIBasicFade)val2).IsFadeShowing) { return true; } UICinematic val3 = LazyUI.Get(); if ((Object)(object)val3 != (Object)null && ((Component)val3).gameObject.activeSelf) { return true; } } catch { } return StorySync.CinematicPlaying; } public static void DrawNameTags(GUIStyle style) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_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_00a3: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) if (!LocalInWorld || LazyWindowsStackController.HasAnyModalWindowOpened || IsLocalAsleep() || ScreenCovered()) { return; } foreach (RemotePlayer value in remotes.Values) { if (!value.IsSpawned || !value.Visible) { continue; } try { Transform val = (((Object)(object)value.Controller != (Object)null) ? value.Controller.BubblePoint : null); Vector3 val2 = CameraSystem.WorldToScreenPoint(((Object)(object)val != (Object)null) ? val.position : (((Component)value.Body).transform.position + Vector3.up * 2f)); if (!(val2.z <= 0f)) { GUI.Label(new Rect(val2.x - 90f, (float)Screen.height - val2.y - 28f, 180f, 22f), value.Label, style); } } catch { } } } private static bool IsLocalAsleep() { PlayerController playerController = MainGame.PlayerController; EnergySystem val = ((playerController == null) ? null : playerController.PlayerData?.energySystem); if (val != null) { if (!val.IsSleeping) { return val.IsInTransitionBetweenSleep; } return true; } return false; } public static string Describe() { if (remotes.Count == 0) { return "no remote players"; } return string.Join(" | ", remotes.Values.Select((RemotePlayer r) => string.Format("{0}#{1} target={2} actual={3} ", r.Label, r.ClientId, r.Target, r.IsSpawned ? ((object)((Component)r.Body).transform.position/*cast due to .constrained prefix*/).ToString() : "unspawned") + $"scene='{r.Scene}' visible={r.Visible} anim={(object)(AnimationState)r.Anim} age={(r.HasState ? (Time.unscaledTime - r.LastStateTime) : (-1f)):0.0}s seq={r.LastSeq}")); } } [BepInPlugin("com.gk2coop.prototype", "GK2 Co-op Prototype", "0.27.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.gk2coop.prototype"; public const string Name = "GK2 Co-op Prototype"; public const string Version = "0.27.0"; internal static ManualLogSource Log; internal static ConfigEntry Connection; internal static ConfigEntry Port; internal static ConfigEntry LastHostAddress; internal static ConfigEntry PlayerName; internal static ConfigEntry VerboseLogging; internal static ConfigEntry RequireBothPlayers; internal static ConfigEntry PartnerRadius; internal static ConfigEntry StoryGateScope; internal static ConfigEntry ClientTalksRunOnHost; internal static ConfigEntry WatchHostScenes; internal static ConfigEntry StandInForHost; internal static ConfigEntry ReturnAfterScenes; internal static ConfigEntry WatchNearbyInPlace; internal static ConfigEntry TeleportDistance; internal static ConfigEntry MirrorSpeech; internal static ConfigEntry HostRunsWorld; internal static ConfigEntry NewPartnerStart; internal static ConfigEntry ReturnToSavedSpot; internal static ConfigEntry PersonalResources; internal static ConfigEntry SharedCurrencies; private void Awake() { //IL_031e: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Expected O, but got Unknown //IL_0329: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Connection = ((BaseUnityPlugin)this).Config.Bind("Network", "ConnectVia", ConnectVia.Steam, "How to host and join. Steam: Steam friends, through Steam's relays (no address or port forwarding needed). IP: an IP address and UDP port (LAN, a VPN such as Tailscale, or a forwarded port)."); Port = ((BaseUnityPlugin)this).Config.Bind("Network", "Port", 8889, "UDP port used for hosting and joining by IP address."); LastHostAddress = ((BaseUnityPlugin)this).Config.Bind("Network", "LastHostAddress", "127.0.0.1", "Last host address typed into the Join box."); PlayerName = ((BaseUnityPlugin)this).Config.Bind("Player", "Name", "", "Your name. Shown over your character, and the key the host saves your co-op character under. Required to host or join."); NewPartnerStart = ((BaseUnityPlugin)this).Config.Bind("Player", "NewPartnerStartsWith", NewPartnerItems.Empty, "Host setting: what a player joining this save for the first time carries. Empty, or the game's new-game StarterKit."); ReturnToSavedSpot = ((BaseUnityPlugin)this).Config.Bind("Player", "ReturnToSavedSpot", true, "When you join again, start where your character was when you left (if that area exists). Off: always start next to the host."); PersonalResources = ((BaseUnityPlugin)this).Config.Bind("Economy", "PersonalResources", "energy,stamina,insanity", "Player resources each player keeps for themselves. Everything else (money, tech points, reputation, progression flags) is shared from the host. Use * at the start or end as a wildcard."); SharedCurrencies = ((BaseUnityPlugin)this).Config.Bind("Economy", "SharedCurrencies", "money,tech_red,tech_green,tech_blue,happiness,*_REP", "Shared resources a joining player can change: what they earn or spend is applied to the host's total. Other shared values just follow the host."); if (PersonalResources.Value.Replace(" ", "") == "energy,stamina,insanity,happiness") { PersonalResources.Value = "energy,stamina,insanity"; } if (SharedCurrencies.Value.Replace(" ", "") == "money,tech_red,tech_green,tech_blue,*_REP") { SharedCurrencies.Value = "money,tech_red,tech_green,tech_blue,happiness,*_REP"; } HostRunsWorld = ((BaseUnityPlugin)this).Config.Bind("World", "HostRunsWorld", true, "Joining copies stop running villagers, zombies, conveyors, timed spawns and object deaths; the host's game runs them and the results are mirrored."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "VerboseLogging", true, "Write detailed co-op events and a state snapshot every 2 seconds to BepInEx/GK2Coop-logs."); RequireBothPlayers = ((BaseUnityPlugin)this).Config.Bind("Story", "RequireBothPlayers", true, "The host can only start NPC conversations (quests, turn-ins) when every partner is nearby."); PartnerRadius = ((BaseUnityPlugin)this).Config.Bind("Story", "PartnerRadius", 8f, "How close (in meters) everyone must be to the NPC for a story conversation."); StoryGateScope = ((BaseUnityPlugin)this).Config.Bind("Story", "Scope", StoryScope.NpcsOnly, "Which interactions count as story: NPCs only, or every scripted object."); ClientTalksRunOnHost = ((BaseUnityPlugin)this).Config.Bind("Story", "ClientTalksRunOnHost", true, "When the joining player talks to an NPC, run the conversation in the host's game (so quest progress is saved)."); WatchHostScenes = ((BaseUnityPlugin)this).Config.Bind("Story", "WatchHostScenes", true, "Lock the joining player and point their camera at the host during the host's cutscenes and scripted scenes."); StandInForHost = ((BaseUnityPlugin)this).Config.Bind("Story", "StandInForHost", true, "During the host's story scenes, the joining player's character takes the host's place and look (in the host's area), so they see the scene exactly as the host does: speech bubbles, answer menus, the wisp, the camera, fades and lighting."); ReturnAfterScenes = ((BaseUnityPlugin)this).Config.Bind("Story", "ReturnAfterScenes", true, "After a story scene, put the joining player back where they were before it. Off: they stay where the scene ended."); WatchNearbyInPlace = ((BaseUnityPlugin)this).Config.Bind("Story", "WatchNearbyInPlace", true, "A partner already in the host's area watches the host's scenes from where they stand (their camera follows the host's; the host's character plays the scene) instead of standing in for the host. Off: they always stand in."); TeleportDistance = ((BaseUnityPlugin)this).Config.Bind("Story", "TeleportDistance", 12f, "When a host scene starts, bring the joining player next to the host if they're farther than this (same area only)."); MirrorSpeech = ((BaseUnityPlugin)this).Config.Bind("Story", "MirrorSpeech", true, "Show the host's dialogue bubbles in the joining player's game."); CoopLog.Init(); ApplyPatches(); GameObject val = new GameObject("GK2Coop"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent(); CoopLog.Info("GK2 Co-op Prototype 0.27.0 loaded. Press F8 to open the co-op window. Debug log: " + CoopLog.FilePath); } private static void ApplyPatches() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown Harmony val = new Harmony("com.gk2coop.prototype"); IEnumerable enumerable = from t in typeof(Plugin).Assembly.GetTypes() where t.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0 select t; int num = 0; foreach (Type item in enumerable) { try { val.CreateClassProcessor(item).Patch(); num++; } catch (Exception e) { CoopLog.Error("Patch " + item.Name + " failed", e); } } CoopLog.Info($"Applied {num} patch groups."); } } internal static class ProgressSync { private struct PendingTrigger { public ulong Sender; public Type Type; public string Id; public float At; } public enum TalentOp : byte { AddToInspiration = 1, AddTalentValue, PurchaseInspiration, PurchaseLevel } private const byte OpQuests = 1; private const byte OpTrigger = 2; private const byte OpTalent = 3; private const byte OpKnowledge = 4; private const float QuestPollInterval = 0.5f; public static bool ApplyingRemote; public static int Sent; public static int Applied; private static readonly FieldInfo QuestStartedField = AccessTools.Field(typeof(QuestSystemData), "OnQuestStarted"); private static readonly FieldInfo QuestCompletedField = AccessTools.Field(typeof(QuestSystemData), "OnQuestCompleted"); private static readonly FieldInfo QuestCanceledField = AccessTools.Field(typeof(QuestSystemData), "OnQuestCanceled"); private static readonly Dictionary sentQuests = new Dictionary(); private static float nextQuestPoll; private static readonly List pendingTriggers = new List(); private static readonly Dictionary recentForwards = new Dictionary(); private static readonly HashSet ForwardedTriggers = new HashSet { (Type)2, (Type)3, (Type)4, (Type)9, (Type)14, (Type)28, (Type)38, (Type)43, (Type)46 }; private static int talentDepth; private static int knowledgeDepth; private static bool ClientInWorld { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { return Plugin.HostRunsWorld.Value; } return false; } } public static bool BlockLocalQuestChange { get { if (ClientInWorld) { return !ApplyingRemote; } return false; } } public static void Reset() { ApplyingRemote = false; sentQuests.Clear(); pendingTriggers.Clear(); recentForwards.Clear(); talentDepth = 0; knowledgeDepth = 0; Sent = 0; Applied = 0; } public static void Tick() { if (CoopSession.Role == CoopRole.Host && CoopSession.IsInGame) { if (Time.unscaledTime >= nextQuestPoll) { nextQuestPoll = Time.unscaledTime + 0.5f; PollQuests(); } RunPendingTriggers(); } } public static void OnClientReady(ulong clientId) { List list = Quests(); if (list == null) { return; } List list2 = list.Where((QuestData q) => q != null && !string.IsNullOrEmpty(((ObjectLinkedToDefinition)(object)q).id)).ToList(); foreach (QuestData item in list2) { sentQuests[((ObjectLinkedToDefinition)(object)item).id] = Pack(item); } SendQuests(list2, clientId); CoopLog.Info($"Sent {list2.Count} quest states to {PlayerSync.Get(clientId)?.Label ?? clientId.ToString()}."); } private static List Quests() { MainGame instance = MainGame.Instance; if (instance == null) { return null; } return instance.GameSave?.questSystemData?.questCollection?.quests; } private static int Pack(QuestData quest) { //IL_0001: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected I4, but got Unknown return quest.status | (quest.isHidden ? 16 : 0) | (quest.isUnknown ? 32 : 0); } private static void PollQuests() { List list = Quests(); if (list == null) { return; } List list2 = new List(); foreach (QuestData item in list) { if (item != null && !string.IsNullOrEmpty(((ObjectLinkedToDefinition)(object)item).id)) { int num = Pack(item); int value; bool num2 = sentQuests.TryGetValue(((ObjectLinkedToDefinition)(object)item).id, out value); sentQuests[((ObjectLinkedToDefinition)(object)item).id] = num; if (num2 && value != num) { list2.Add(item); } } } if (list2.Count > 0 && CoopSession.HasReadyClients) { SendQuests(list2, null); CoopLog.Info("Quest changes: " + string.Join(", ", list2.Select((QuestData q) => ((ObjectLinkedToDefinition)(object)q).id + "=" + ((object)Unsafe.As(ref q.status)/*cast due to .constrained prefix*/).ToString())) + "."); } } private unsafe static void SendQuests(List quests, ulong? onlyTo) { //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_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_002c: 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_0047: 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_0085: 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) //IL_00ce: 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) for (int i = 0; i < quests.Count; i += 100) { List list = quests.Skip(i).Take(100).ToList(); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (QuestData item in list) { CoopMessages.WriteString(ref writer, ((ObjectLinkedToDefinition)(object)item).id); b = (byte)Pack(item); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); } if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Progress", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Progress", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } } private static void ApplyQuest(string id, int packed) { //IL_004d: 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_0066: 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_007f: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Invalid comparison between Unknown and I4 //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Invalid comparison between Unknown and I4 MainGame instance = MainGame.Instance; QuestSystemData val = ((instance == null) ? null : instance.GameSave?.questSystemData); Dictionary dictionary = val?.questCollection?.questsCache; if (dictionary == null || !dictionary.TryGetValue(id, out var value) || value == null) { return; } QuestStatus val2 = (QuestStatus)(packed & 0xF); bool isHidden = (packed & 0x10) != 0; bool isUnknown = (packed & 0x20) != 0; QuestStatus status = value.status; value.status = val2; value.isHidden = isHidden; value.isUnknown = isUnknown; if (status == val2) { return; } Applied++; CoopLog.Info($"Quest '{id}': {status} -> {val2} (from the host)."); FieldInfo fieldInfo = (((int)val2 == 2) ? QuestStartedField : (((int)val2 == 3) ? QuestCompletedField : (((int)val2 == 4) ? QuestCanceledField : null))); try { (fieldInfo?.GetValue(val) as Action)?.Invoke(value); } catch (Exception ex) { CoopLog.Warn("Quest '" + id + "' listeners failed: " + ex.Message); } } public unsafe static void OnLocalTrigger(Type type, string id) { //IL_0013: 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_002f: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected I4, but got Unknown //IL_004e: 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_0070: 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) if (ClientInWorld && !ApplyingRemote && ForwardedTriggers.Contains(type) && !TutorialSync.Active) { ContainerSync.SendChangedEverywhere(); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int num = (int)type; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); CoopMessages.WriteString(ref writer, id ?? ""); CoopMessages.SendToHost("GK2Coop.Progress", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Debug($"Sent quest trigger {type} '{id}' to the host."); } } private static void RunPendingTriggers() { //IL_00d4: 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) if (pendingTriggers.Count == 0) { return; } float now = Time.unscaledTime; List list = pendingTriggers.Where((PendingTrigger t) => t.At <= now).ToList(); pendingTriggers.RemoveAll((PendingTrigger t) => t.At <= now); foreach (PendingTrigger item in list) { string arg = PlayerSync.Get(item.Sender)?.Label ?? item.Sender.ToString(); CoopLog.Info($"Running {arg}'s quest trigger {item.Type} '{item.Id}'."); try { GlobalEventsSystem.FireTrigger(item.Type, item.Id); Applied++; } catch (Exception e) { CoopLog.Error($"{arg}'s quest trigger {item.Type} '{item.Id}' failed", e); } } } public static bool BeforeTalentOp(TalentOp op, string id, int value, out bool outermost) { outermost = false; if (ApplyingRemote) { return true; } if (ClientInWorld) { SendTalentOp(op, id, value, null); CoopLog.Debug($"Sent talent {op} '{id}' {value} to the host."); return false; } outermost = talentDepth == 0; talentDepth++; return true; } public static void AfterTalentOp(TalentOp op, string id, int value, bool outermost, bool happened) { if (!ApplyingRemote && !ClientInWorld) { talentDepth = Math.Max(0, talentDepth - 1); if (outermost && happened && CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients) { SendTalentOp(op, id, value, null); } } } private unsafe static void SendTalentOp(TalentOp op, string id, int value, ulong? onlyTo) { //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_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_0010: 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_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) //IL_0047: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = (byte)op; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, id ?? ""); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref value, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Client) { CoopMessages.SendToHost("GK2Coop.Progress", writer, (NetworkDelivery)3); } else if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Progress", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Progress", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } private static void RunTalentOp(TalentOp op, string id, int value) { TalentSystemData talentSystemData = MainGame.Instance.GameSave.talentSystemData; switch (op) { case TalentOp.AddToInspiration: talentSystemData.AddToInspiration(id, value); break; case TalentOp.AddTalentValue: talentSystemData.AddTalentValue(id, value); break; case TalentOp.PurchaseInspiration: talentSystemData.PurchaseInspiration(id); break; case TalentOp.PurchaseLevel: { TalentLevelUpDef val = ((CoopSession.Role == CoopRole.Client) ? ((GameBalanceBase)GameBalance.Me).GetDataOrNull(id) : null); List list = val?.expressionsOnBuy; try { if (val != null) { val.expressionsOnBuy = PartnerReplay.WithoutSharedWorld(list, "Talent '" + id + "'"); } talentSystemData.PurchaseLevel(id, value != 0); break; } finally { if (val != null) { val.expressionsOnBuy = list; } } } } } public static void BeginKnowledgeCall() { knowledgeDepth++; } public static void EndKnowledgeCall() { knowledgeDepth = Math.Max(0, knowledgeDepth - 1); } public unsafe static void OnKnowledgeCall(MethodBase method, object[] args) { //IL_0109: 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_0110: 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_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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_0143: 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_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) if (ApplyingRemote || knowledgeDepth != 1 || args == null || args.Length != 1 || KnowledgeSync.ApplyingRemote) { return; } bool flag = CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients; bool clientInWorld = ClientInWorld; if (!flag && !clientInWorld) { return; } object obj = args[0]; string text = ""; int num = 0; byte b; if (obj is string text2) { b = 0; text = text2; } else if (obj is Enum) { b = 1; num = Convert.ToInt32(obj); } else { AlchemyMixDef val = (AlchemyMixDef)((obj is AlchemyMixDef) ? obj : null); if (val == null) { return; } b = 2; text = ((BalanceBaseObject)val).id; } if (clientInWorld) { string key = method.Name + "|" + b + "|" + text + "|" + num; if (recentForwards.TryGetValue(key, out var value) && Time.unscaledTime < value) { return; } recentForwards[key] = Time.unscaledTime + 10f; } FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b2 = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b2, default(ForPrimitives)); CoopMessages.WriteString(ref writer, method.Name); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text ?? ""); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); if (flag) { CoopMessages.SendToReadyClients("GK2Coop.Progress", writer, (NetworkDelivery)3); } else { CoopMessages.SendToHost("GK2Coop.Progress", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Debug("Knowledge " + method.Name + "(" + ((b == 1) ? num.ToString() : text) + ") " + (flag ? "mirrored" : "sent to the host") + "."); } public static IEnumerable KnowledgeMethods() { string[] array = new string[16] { "UnlockVendorForOrders", "UnlockTalentBranch", "AddPhraseToBlackList", "RemovePhraseFromBlackList", "UnlockPhrase", "RemoveTech", "HideTech", "UnlockMapFightIcon", "LockMapFightIcon", "RemoveUnlockedCraft", "LockTownBuilding", "BlackListCraft", "RemoveCraftFromBlackList", "LockBuilding", "RevealTalentLevelUp", "RevealInspiration" }; foreach (string text in array) { yield return AccessTools.Method(typeof(KnowledgeSystem), text, new Type[1] { typeof(string) }, (Type[])null); } yield return AccessTools.Method(typeof(KnowledgeSystem), "UnlockTechTab", new Type[1] { typeof(TechTreeTab) }, (Type[])null); yield return AccessTools.Method(typeof(KnowledgeSystem), "LockTechTab", new Type[1] { typeof(TechTreeTab) }, (Type[])null); yield return AccessTools.Method(typeof(KnowledgeSystem), "UnlockCharTab", new Type[1] { typeof(CharPage) }, (Type[])null); yield return AccessTools.Method(typeof(KnowledgeSystem), "LockCharTab", new Type[1] { typeof(CharPage) }, (Type[])null); yield return AccessTools.Method(typeof(KnowledgeSystem), "UnlockOrgan", new Type[1] { typeof(ItemType) }, (Type[])null); yield return AccessTools.Method(typeof(KnowledgeSystem), "DiscoverAlchemyMix", new Type[1] { typeof(AlchemyMixDef) }, (Type[])null); } private static void RunKnowledgeCall(string name, byte kind, string text, int number) { MethodInfo methodInfo = KnowledgeMethods().FirstOrDefault((MethodBase m) => m != null && m.Name == name) as MethodInfo; if (methodInfo == null) { CoopLog.Warn("Unknown knowledge call '" + name + "'."); return; } Type parameterType = methodInfo.GetParameters()[0].ParameterType; object obj; switch (kind) { case 0: obj = text; break; case 1: obj = Enum.ToObject(parameterType, number); break; default: obj = ((GameBalanceBase)GameBalance.Me).GetData(text); if (obj == null) { CoopLog.Warn("Alchemy mix '" + text + "' isn't known here."); return; } break; } methodInfo.Invoke(MainGame.Instance.GameSave.knowledgeSystem, new object[1] { obj }); } public static void OnProgress(ulong sender, FastBufferReader reader) { //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_0109: 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_0124: 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_0214: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_00ad: 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_00db: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Client && !CoopSession.ClientInWorld) { return; } switch (b) { case 1: { if (CoopSession.Role != CoopRole.Client) { break; } int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); ApplyingRemote = true; try { byte packed = default(byte); for (int i = 0; i < num2; i++) { string id = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref packed, default(ForPrimitives)); ApplyQuest(id, packed); } break; } finally { ApplyingRemote = false; } } case 2: if (CoopSession.Role == CoopRole.Host) { int num3 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); string id2 = CoopMessages.ReadString(ref reader); pendingTriggers.Add(new PendingTrigger { Sender = sender, Type = (Type)num3, Id = id2, At = Time.unscaledTime + 0.3f }); } break; case 3: { byte b3 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b3, default(ForPrimitives)); string text3 = CoopMessages.ReadString(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); TalentOp talentOp = (TalentOp)b3; if (CoopSession.Role == CoopRole.Host) { CoopLog.Info($"{PlayerSync.Get(sender)?.Label ?? sender.ToString()}: talent {talentOp} '{text3}' {num}."); try { RunTalentOp(talentOp, text3, num); break; } catch (Exception e2) { CoopLog.Error($"Talent {talentOp} '{text3}' failed", e2); break; } } ApplyingRemote = true; try { RunTalentOp(talentOp, text3, num); Applied++; break; } catch (Exception e3) { CoopLog.Error($"Replaying talent {talentOp} '{text3}' failed", e3); break; } finally { ApplyingRemote = false; } } case 4: { string text = CoopMessages.ReadString(ref reader); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); string text2 = CoopMessages.ReadString(ref reader); int number = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref number, default(ForPrimitives)); bool flag = CoopSession.Role == CoopRole.Host; ApplyingRemote = !flag; try { RunKnowledgeCall(text, b2, text2, number); Applied++; CoopLog.Debug("Ran knowledge " + text + "(" + ((b2 == 1) ? number.ToString() : text2) + ") from " + ((!flag) ? "the host" : (PlayerSync.Get(sender)?.Label ?? sender.ToString())) + "."); break; } catch (Exception e) { CoopLog.Error("Knowledge " + text + "(" + text2 + ") failed", e); break; } finally { ApplyingRemote = false; } } } } public static string Describe() { return $"quests={sentQuests.Count} pendingTriggers={pendingTriggers.Count} sent={Sent} applied={Applied}"; } } [HarmonyPatch] internal static class HostRunsQuests { private static IEnumerable TargetMethods() { Type type = typeof(QuestSystemData); yield return AccessTools.Method(type, "StartQuest", (Type[])null, (Type[])null); yield return AccessTools.Method(type, "OnStartFailed", (Type[])null, (Type[])null); yield return AccessTools.Method(type, "AwaitQuest", (Type[])null, (Type[])null); yield return AccessTools.Method(type, "CompleteQuest", (Type[])null, (Type[])null); yield return AccessTools.Method(type, "CancelQuest", (Type[])null, (Type[])null); yield return AccessTools.Method(type, "ResetQuestToAvailable", (Type[])null, (Type[])null); Type data = typeof(QuestData); yield return AccessTools.Method(data, "Start", (Type[])null, (Type[])null); yield return AccessTools.Method(data, "FailedStart", (Type[])null, (Type[])null); yield return AccessTools.Method(data, "Await", (Type[])null, (Type[])null); yield return AccessTools.Method(data, "Complete", (Type[])null, (Type[])null); yield return AccessTools.Method(data, "Cancel", (Type[])null, (Type[])null); yield return AccessTools.Method(data, "ResetToAvailable", (Type[])null, (Type[])null); } private static bool Prefix(MethodBase __originalMethod) { if (!ProgressSync.BlockLocalQuestChange) { return true; } CoopLog.Debug("Skipped a local quest change (" + __originalMethod.DeclaringType?.Name + "." + __originalMethod.Name + "); the host's quest log is the real one."); return false; } } [HarmonyPatch(typeof(GlobalEventsSystem), "FireTrigger")] internal static class ForwardQuestTriggers { private static void Prefix(Type type, string id) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { ProgressSync.OnLocalTrigger(type, id); } catch (Exception e) { CoopLog.Error("Forwarding a quest trigger failed", e); } } } [HarmonyPatch(typeof(TalentSystemData), "AddToInspiration")] internal static class SyncAddToInspiration { private static bool Prefix(string inspirationId, int counterToAdd, out bool __state) { return ProgressSync.BeforeTalentOp(ProgressSync.TalentOp.AddToInspiration, inspirationId, counterToAdd, out __state); } private static void Postfix(string inspirationId, int counterToAdd, bool __state) { ProgressSync.AfterTalentOp(ProgressSync.TalentOp.AddToInspiration, inspirationId, counterToAdd, __state, happened: true); } } [HarmonyPatch(typeof(TalentSystemData), "AddTalentValue")] internal static class SyncAddTalentValue { private static bool Prefix(string talentId, int value, ref bool __result, out bool __state) { bool num = ProgressSync.BeforeTalentOp(ProgressSync.TalentOp.AddTalentValue, talentId, value, out __state); if (!num) { __result = true; } return num; } private static void Postfix(string talentId, int value, bool __result, bool __state) { ProgressSync.AfterTalentOp(ProgressSync.TalentOp.AddTalentValue, talentId, value, __state, __result); } } [HarmonyPatch(typeof(TalentSystemData), "PurchaseInspiration")] internal static class SyncPurchaseInspiration { private static bool Prefix(string inspirationId, out bool __state) { return ProgressSync.BeforeTalentOp(ProgressSync.TalentOp.PurchaseInspiration, inspirationId, 0, out __state); } private static void Postfix(string inspirationId, bool __state) { ProgressSync.AfterTalentOp(ProgressSync.TalentOp.PurchaseInspiration, inspirationId, 0, __state, happened: true); } } [HarmonyPatch(typeof(TalentSystemData), "PurchaseLevel")] internal static class SyncPurchaseLevel { private static bool Prefix(TalentSystemData __instance, string talentLevelId, bool free, out bool __state) { bool outermost; bool num = ProgressSync.BeforeTalentOp(ProgressSync.TalentOp.PurchaseLevel, talentLevelId, free ? 1 : 0, out outermost); TalentData val = default(TalentData); __state = outermost && __instance.CanPurchaseLevel(talentLevelId, ref val); if (!num) { __state = false; } return num; } private static void Postfix(string talentLevelId, bool free, bool __state) { ProgressSync.AfterTalentOp(ProgressSync.TalentOp.PurchaseLevel, talentLevelId, free ? 1 : 0, __state, __state); } } [HarmonyPatch] internal static class SyncOtherKnowledge { private static IEnumerable TargetMethods() { return from m in ProgressSync.KnowledgeMethods() where m != null select m; } private static void Prefix() { ProgressSync.BeginKnowledgeCall(); } private static void Postfix(MethodBase __originalMethod, object[] __args) { try { ProgressSync.OnKnowledgeCall(__originalMethod, __args); } catch (Exception e) { CoopLog.Error("Mirroring " + __originalMethod.Name + " failed", e); } } private static Exception Finalizer(Exception __exception) { ProgressSync.EndKnowledgeCall(); return __exception; } } internal static class SceneWait { private const byte OpTravel = 1; private const byte OpFetch = 2; private const byte OpComing = 3; private const byte OpDoor = 4; private const float MaxWait = 45f; private static string waitScene; private static float armedAt = -100f; private static List waitFor = new List(); private static Action heldArrival; private static float heldSince; private static float nextNotice; public static int Holds; private static string travelScene; private static Vector3 travelPosition; private static string travelPreset; private static float travelAt = -100f; private const float AskWait = 2f; private const float MaxLineWait = 45f; private static readonly Dictionary askedAt = new Dictionary(); private static readonly HashSet coming = new HashSet(); private static readonly HashSet notComing = new HashSet(); private static readonly List heldLines = new List(); private static float linesHeldSince; private static float nextLineNotice; private static float lastInSceneAt = -100f; private static bool replayingLines; public static int LineHolds; private static float fetchedUntil = -100f; private static readonly Dictionary watchingSince = new Dictionary(); private const float CameraWait = 1.2f; private static bool sceneWasOn; private static bool frozen; private static float frozenSince; private static float savedTimeScale = 1f; private static float nextFreezeNotice; public static int Freezes; public static bool IsTravelling => travelScene != null; public static bool Fetched => Time.unscaledTime < fetchedUntil; public static void Reset() { Unfreeze("session over"); sceneWasOn = false; ReleaseLines("session over"); askedAt.Clear(); coming.Clear(); notComing.Clear(); fetchedUntil = -100f; ReleaseArrival("session over"); waitScene = null; waitFor = new List(); travelScene = null; } public unsafe static void OnHostTeleport(TeleportDataBase data) { //IL_00d5: 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_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_00ff: 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_010c: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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) if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || data == null || data is CoopTeleportData) { return; } string text = MainGame.PlayerData?.currentGameSceneId; string text2 = data.GetDestinationSceneData()?.id; if (string.IsNullOrEmpty(text2)) { return; } if (text2 == text) { OnHostDoor(data, text2); return; } bool intro = TutorialSync.Active; List list = (from r in PlayerSync.Remotes where (r.StandingIn || intro) && CoopSession.ReadyClients.Contains(r.ClientId) select r.ClientId).ToList(); if (list.Count == 0) { return; } ReleaseArrival("another teleport"); waitScene = text2; armedAt = Time.unscaledTime; waitFor = list; Vector3 position = data.GetPosition(); string s = (string.IsNullOrEmpty(data.environmentPreset) ? "outdoor" : data.environmentPreset); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text2); CoopMessages.WriteVector3(ref writer, position); CoopMessages.WriteString(ref writer, s); foreach (ulong item in list) { CoopMessages.SendTo("GK2Coop.Travel", item, writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info($"Story teleport to '{text2}' at {position}: {list.Count} partner(s) go there now, and our arrival waits for them."); } private unsafe static void OnHostDoor(TeleportDataBase data, string scene) { //IL_005f: 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_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) //IL_0087: 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) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_010c: Unknown result type (might be due to invalid IL or missing references) if (!TutorialSync.GuidedIntro) { return; } List list = (from r in PlayerSync.Remotes where !r.StandingIn && r.Scene == scene && CoopSession.ReadyClients.Contains(r.ClientId) select r.ClientId).ToList(); if (list.Count == 0) { return; } Vector3 position = data.GetPosition(); string s = (string.IsNullOrEmpty(data.environmentPreset) ? "outdoor" : data.environmentPreset); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, scene); CoopMessages.WriteVector3(ref writer, position); CoopMessages.WriteString(ref writer, s); foreach (ulong item in list) { CoopMessages.SendTo("GK2Coop.Travel", item, writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info($"Through a door to {position} in '{scene}' (the intro): {list.Count} partner(s) come along."); } public static Action HoldArrival(string sceneId, Action arrival) { if (CoopSession.Role != CoopRole.Host || waitScene == null || sceneId != waitScene || Time.unscaledTime - armedAt > 60f) { return null; } return delegate { if (StillLoading().Count == 0) { CoopLog.Info("Arrived in '" + sceneId + "'; partners are already there."); waitScene = null; arrival?.Invoke(); } else { Holds++; heldArrival = arrival ?? ((Action)delegate { }); heldSince = Time.unscaledTime; nextNotice = 0f; CoopLog.Info("Loaded '" + sceneId + "'; holding our arrival until partners have loaded it too."); } }; } private static List StillLoading() { List list = new List(); foreach (ulong item in waitFor) { RemotePlayer remotePlayer = PlayerSync.Get(item); if (remotePlayer != null && CoopSession.ReadyClients.Contains(item) && (remotePlayer.StandingIn || TutorialSync.Active) && (remotePlayer.Scene != waitScene || remotePlayer.Transitioning)) { list.Add(item); } } return list; } public static void Tick() { if (CoopSession.Role == CoopRole.Client) { TickPartner(); } else { if (CoopSession.Role != CoopRole.Host) { return; } TickLines(); if (heldArrival == null) { return; } List list = StillLoading(); if (list.Count == 0) { ReleaseArrival("everyone has loaded"); } else if (Time.unscaledTime - heldSince > 45f) { ReleaseArrival($"waited {45f:0} s"); } else if (Time.unscaledTime >= nextNotice) { nextNotice = Time.unscaledTime + 3f; string text = string.Join(", ", list.Select((ulong id) => PartnerProfiles.NameOf(id) ?? $"Player {id}")); CoopRunner.Toast("Waiting for " + text + " to load the area...", 3.2f); } } } private static void ReleaseArrival(string why) { Action action = heldArrival; heldArrival = null; if (action == null) { return; } waitScene = null; CoopLog.Info($"Carrying on with our arrival ({why}, after {Time.unscaledTime - heldSince:0.0} s)."); try { action(); } catch (Exception e) { CoopLog.Error("Finishing our held arrival failed", e); } } public static void OnTravel(ulong sender, FastBufferReader reader) { //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_0025: 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_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) //IL_007c: 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_00a9: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (b == 3 && CoopSession.Role == CoopRole.Host) { byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); OnComing(sender, b2 != 0); } else if (b == 2 && CoopSession.Role == CoopRole.Client) { OnFetch(); } else if (b == 4 && CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { string scene = CoopMessages.ReadString(ref reader); Vector3 position = CoopMessages.ReadVector3(ref reader); string preset = CoopMessages.ReadString(ref reader); StandIn.FollowDoor(scene, position, preset); } else { if (b != 1 || CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld) { return; } string text = CoopMessages.ReadString(ref reader); Vector3 position2 = CoopMessages.ReadVector3(ref reader); string preset2 = CoopMessages.ReadString(ref reader); if (!StandIn.CanTravel) { if (TutorialSync.Active) { StandIn.TravelAlong(text, position2, preset2); } else { CoopLog.Info("The host's story is going to '" + text + "', but we're not watching it."); } return; } travelScene = text; travelPosition = position2; travelPreset = preset2; travelAt = Time.unscaledTime; StandIn.OnTravel(position2); CoopLog.Info("The host's story is going to '" + text + "': going there now."); } } public static bool Travelling(out string scene, out Vector3 position, out string preset) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) scene = travelScene; position = travelPosition; preset = travelPreset; return travelScene != null; } public static void CancelTravel() { travelScene = null; } private static void TickPartner() { if (travelScene != null) { RemotePlayer host = PlayerSync.Host; if (host != null && host.HasState && host.Scene == travelScene && !host.Transitioning) { CoopLog.Info("The host has arrived in '" + travelScene + "' too."); travelScene = null; } else if (Time.unscaledTime - travelAt > 75f || !StandIn.CanTravel) { CoopLog.Info("Stopped waiting for the host in '" + travelScene + "'."); travelScene = null; } } } private static bool HostInScene() { return StorySync.HostSceneNow(); } public static bool BeforeStoryLine(Action replay, string what) { if (replayingLines || CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || !HostInScene()) { return true; } lastInSceneAt = Time.unscaledTime; if (heldLines.Count > 0) { heldLines.Add(replay); return false; } AskNewPartners(); List list = LineWaitingFor(); if (list.Count == 0) { return true; } heldLines.Add(replay); linesHeldSince = Time.unscaledTime; nextLineNotice = Time.unscaledTime + 0.6f; LineHolds++; CoopLog.Info("Holding " + what + " until " + Names(list) + " " + ((list.Count == 1) ? "is" : "are") + " here to see it."); return false; } private unsafe static void AskNewPartners() { //IL_0037: 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_003d: 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: 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_005b: Unknown result type (might be due to invalid IL or missing references) foreach (ulong item in CoopSession.ReadyClients.ToList()) { if (!askedAt.ContainsKey(item)) { askedAt[item] = Time.unscaledTime; FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.SendTo("GK2Coop.Travel", item, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } } } private static List LineWaitingFor() { List list = new List(); string text = MainGame.PlayerData?.currentGameSceneId; float unscaledTime = Time.unscaledTime; foreach (ulong readyClient in CoopSession.ReadyClients) { RemotePlayer remotePlayer = PlayerSync.Get(readyClient); if (!notComing.Contains(readyClient) && remotePlayer != null && remotePlayer.HasState) { float value; bool num = !coming.Contains(readyClient) && askedAt.TryGetValue(readyClient, out value) && unscaledTime - value < 2f; bool flag = (coming.Contains(readyClient) || remotePlayer.StandingIn) && (!remotePlayer.StandingIn || remotePlayer.Scene != text || remotePlayer.Transitioning); if (num || flag) { list.Add(readyClient); } } } return list; } private static void OnComing(ulong sender, bool yes) { if (yes) { coming.Add(sender); notComing.Remove(sender); } else { notComing.Add(sender); coming.Remove(sender); } CoopLog.Debug((PartnerProfiles.NameOf(sender) ?? sender.ToString()) + " " + (yes ? "is coming to watch" : "isn't watching") + " this scene."); } public static void NoteStandingIn(ulong clientId) { watchingSince[clientId] = Time.unscaledTime; } private static bool CameraThere(RemotePlayer remote) { if (!remote.WatchReady) { if (watchingSince.TryGetValue(remote.ClientId, out var value)) { return Time.unscaledTime - value > 1.2f; } return false; } return true; } public static void OnPartnerStoppedStandingIn(ulong clientId) { coming.Remove(clientId); if (HostInScene()) { notComing.Add(clientId); } } private static void TickLines() { float unscaledTime = Time.unscaledTime; TickSceneStart(); if (HostInScene()) { lastInSceneAt = unscaledTime; } else if (heldLines.Count == 0 && unscaledTime - lastInSceneAt > 3f && (askedAt.Count > 0 || coming.Count > 0 || notComing.Count > 0)) { askedAt.Clear(); coming.Clear(); notComing.Clear(); } if (heldLines.Count == 0) { return; } AskNewPartners(); List list = LineWaitingFor(); if (list.Count == 0) { ReleaseLines("everyone is here"); } else if (unscaledTime - linesHeldSince > 45f) { foreach (ulong item in list) { notComing.Add(item); } ReleaseLines($"waited {45f:0} s"); } else if (unscaledTime >= nextLineNotice) { nextLineNotice = unscaledTime + 3f; List list2 = LoadingIn(list); if (list2.Count > 0) { CoopRunner.Toast("Waiting for " + Names(list2) + " to get here before the scene goes on...", 3.2f); } } } private static bool HostTeleporting() { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController != (Object)null) { return !playerController.IsControlEnabledByType((TakenControlType)4); } return false; } private static bool AnyoneFar() { //IL_003e: 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_0043: 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) PlayerController playerController = MainGame.PlayerController; string text = MainGame.PlayerData?.currentGameSceneId; Vector3 val = (((Object)(object)((playerController != null) ? playerController.PhysicalBody : null) != (Object)null) ? ((Component)playerController.PhysicalBody).transform.position : Vector3.zero); foreach (ulong readyClient in CoopSession.ReadyClients) { RemotePlayer remotePlayer = PlayerSync.Get(readyClient); if (remotePlayer != null && remotePlayer.HasState && !notComing.Contains(readyClient) && (remotePlayer.Scene != text || remotePlayer.Transitioning || Vector3.Distance(remotePlayer.Target, val) > 25f)) { return true; } } return false; } private static void TickSceneStart() { bool flag = HostInScene(); float unscaledTime = Time.unscaledTime; if (flag && !sceneWasOn && CoopSession.HasReadyClients) { AskNewPartners(); if (!frozen && AnyoneFar() && !HostTeleporting()) { frozen = true; frozenSince = unscaledTime; nextFreezeNotice = unscaledTime + 0.6f; savedTimeScale = ((Time.timeScale > 0f) ? Time.timeScale : 1f); Time.timeScale = 0f; Freezes++; CoopLog.Info("A scene is starting with a partner still on the way: holding it (time stopped) until they're here."); } } sceneWasOn = flag; if (!frozen) { return; } List list = LineWaitingFor(); if (!flag) { Unfreeze("the scene is over"); } else if (list.Count == 0) { Unfreeze("everyone is here"); } else if (HostTeleporting()) { Unfreeze("we're changing area"); } else if (unscaledTime - frozenSince > 45f) { foreach (ulong item in list) { notComing.Add(item); } Unfreeze($"waited {45f:0} s"); } else if (unscaledTime >= nextFreezeNotice) { nextFreezeNotice = unscaledTime + 3f; List list2 = LoadingIn(list); if (list2.Count > 0) { CoopRunner.Toast("Waiting for " + Names(list2) + " to get here before the scene starts...", 3.2f); } } } private static void Unfreeze(string why) { if (frozen) { frozen = false; Time.timeScale = ((savedTimeScale > 0f) ? savedTimeScale : 1f); CoopLog.Info($"Starting the scene ({why}, after {Time.unscaledTime - frozenSince:0.0} s)."); } } private static void ReleaseLines(string why) { if (heldLines.Count == 0) { return; } List list = heldLines.ToList(); heldLines.Clear(); CoopLog.Info($"Carrying on with the scene ({why}, after {Time.unscaledTime - linesHeldSince:0.0} s)."); replayingLines = true; try { foreach (Action item in list) { try { item(); } catch (Exception e) { CoopLog.Error("Showing a held line failed", e); } } } finally { replayingLines = false; } } private static List LoadingIn(List ids) { string hostScene = MainGame.PlayerData?.currentGameSceneId; return ids.Where(delegate(ulong id) { RemotePlayer remotePlayer = PlayerSync.Get(id); return remotePlayer != null && remotePlayer.HasState && (remotePlayer.Scene != hostScene || remotePlayer.Transitioning); }).ToList(); } private static string Names(List ids) { return string.Join(", ", ids.Select((ulong id) => PartnerProfiles.NameOf(id) ?? $"Player {id}")); } private unsafe static void OnFetch() { //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_0035: 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_003f: 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_0058: 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_006a: Unknown result type (might be due to invalid IL or missing references) bool flag = CoopSession.ClientInWorld && Plugin.WatchHostScenes.Value && StandIn.Enabled; if (flag) { fetchedUntil = Time.unscaledTime + 20f; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = (flag ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Travel", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Debug("The host's scene is starting; " + (flag ? "coming to watch" : "not watching") + "."); } public static string Describe() { if (CoopSession.Role != CoopRole.Host) { return $"travelling='{travelScene}' fetched={Fetched}"; } return string.Format("holds={0} holding={1} waitScene='{2}' lineHolds={3} heldLines={4} freezes={5} frozen={6} coming=[{7}] notComing=[{8}]", Holds, heldArrival != null, waitScene, LineHolds, heldLines.Count, Freezes, frozen, string.Join(",", coming), string.Join(",", notComing)); } } [HarmonyPatch(typeof(Bubble), "Talk")] internal static class HoldLineForPartners { private static bool Prefix(PhraseData data) { //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) try { return SceneWait.BeforeStoryLine(delegate { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Bubble.Talk(data); }, "the line '" + data.text + "'"); } catch (Exception e) { CoopLog.Error("Holding a line for partners failed", e); return true; } } } [HarmonyPatch(typeof(Bubble), "ShowMultiAnswer")] internal static class HoldAnswersForPartners { private static bool Prefix(List answers, Transform targetTransform, WgoData dialogParticipant, Action onChosen, Action onDisappeared, bool isOverBlackout) { try { return SceneWait.BeforeStoryLine(delegate { Bubble.ShowMultiAnswer(answers, targetTransform, dialogParticipant, onChosen, onDisappeared, isOverBlackout); }, "the answer menu"); } catch (Exception e) { CoopLog.Error("Holding an answer menu for partners failed", e); return true; } } } [HarmonyPatch(typeof(PlayerController), "Teleport")] internal static class TellPartnersWhereStoryGoes { private static void Prefix(TeleportDataBase teleportData) { try { SceneWait.OnHostTeleport(teleportData); } catch (Exception e) { CoopLog.Error("Telling partners about our teleport failed", e); } } } [HarmonyPatch(typeof(GameSceneManager), "LoadScene")] internal static class HoldArrivalForPartners { private static void Prefix(string sceneId, ref Action onSceneLoaded) { try { Action action = SceneWait.HoldArrival(sceneId, onSceneLoaded); if (action != null) { onSceneLoaded = action; } } catch (Exception e) { CoopLog.Error("Holding our arrival for partners failed", e); } } } internal static class SermonSync { private const byte OpFlags = 1; private const byte OpReward = 2; private const float MaxLength = 300f; private static readonly string[] Flags = new string[3] { "sermon_ready", "last_sermon_with_organ_activity", "last_sermon_with_choir_activity" }; private static readonly FieldRef MovableField = AccessTools.FieldRefAccess("assignedMovableObject"); private static bool active; private static float startedAt; private static readonly HashSet actors = new HashSet(); public static bool LocalCraft; public static int Sent; private static bool ClientActive { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { return Plugin.HostRunsWorld.Value; } return false; } } public static bool Active { get { if (active) { return Time.unscaledTime - startedAt < 300f; } return false; } } public static void Reset() { active = false; actors.Clear(); LocalCraft = false; Sent = 0; } public static void BeforeStart() { if (ClientActive) { active = true; startedAt = Time.unscaledTime; actors.Clear(); CoopLog.Info("Our sermon starts: it plays in our game; the week's flags and the collection go to the host."); } } public static void AfterStart() { if (active && ClientActive) { SendFlags("started"); } } public static void AfterFinish() { if (active) { if (ClientActive) { SendFlags("over"); } active = false; CoopLog.Info($"Our sermon is over ({actors.Count} parishioner(s) were ours)."); actors.Clear(); } } public static void NoteLocalAdd(string uid, string id) { if (Active && ClientActive && !string.IsNullOrEmpty(uid) && actors.Add(uid)) { CoopLog.Debug(id + " (" + uid + ") is one of our sermon's parishioners."); } } public static bool IsActor(string uid) { if (Active && uid != null) { return actors.Contains(uid); } return false; } public static bool IsActorMover(MovementComponent component) { if (Active && component != null) { IMovable obj = MovableField.Invoke(component); WgoData val = (WgoData)(object)((obj is WgoData) ? obj : null); if (val != null && val.UniqueId != (SGuid)null) { return actors.Contains(val.UniqueId.Id); } } return false; } private unsafe static void SendFlags(string when) { //IL_001a: 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_0021: 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_0030: 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_004c: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) PlayerData data = MainGame.PlayerData; if (data == null) { return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int num = Flags.Length; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); string[] flags = Flags; foreach (string text in flags) { CoopMessages.WriteString(ref writer, text); float res = data.GetRes(text, 0f); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref res, default(ForPrimitives)); } CoopMessages.SendToHost("GK2Coop.Sermons", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Info("Sermon " + when + ": sent the week's flags (" + string.Join(", ", Array.ConvertAll(Flags, (string f) => $"{f}={data.GetRes(f, 0f):0}")) + ") to the host."); } public unsafe static bool BeforeStoreResult(WgoData box, SermonResultData result) { //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_0026: 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_0030: 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_005c: 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_006d: Unknown result type (might be due to invalid IL or missing references) if (!ClientActive || ((box != null) ? box.UniqueId : null) == (SGuid)null || result == null) { return true; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, box.UniqueId.Id); int money = result.Money; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref money, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Sermons", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Info($"Our sermon's collection ({result.Money}) goes into the host's {((ObjectLinkedToDefinition)(object)box).id}."); return false; } public static void OnSermons(ulong sender, FastBufferReader reader) { //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_0054: 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_00fb: 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_007b: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role != CoopRole.Host) { return; } string text = PlayerSync.Get(sender)?.Label ?? $"Player {sender}"; switch (b) { case 1: { int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); PlayerData playerData = MainGame.PlayerData; float num3 = default(float); for (int i = 0; i < num2; i++) { string text3 = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); if (playerData != null && Array.IndexOf(Flags, text3) >= 0) { playerData.SetRes(text3, num3); } } CoopLog.Info(string.Format("{0}'s sermon: the week's flags set here (sermon_ready={1:0}).", text, (playerData != null) ? new float?(playerData.GetRes("sermon_ready", 0f)) : ((float?)null))); break; } case 2: { string text2 = CoopMessages.ReadString(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text2)) : null); if (val == null) { CoopLog.Warn(text + "'s sermon collection: reward box " + text2 + " isn't here."); break; } val.AddGameRes("money", num); bool flag = false; foreach (InteractionEvent @event in val.Events) { if (@event != null && @event.str == "sermon_reward") { flag = true; break; } } if (!flag) { val.AddInteractionEvent("sermon_reward", false); } CoopRunner.Toast($"{text}'s sermon collected {num}: take it from the box in the church."); CoopLog.Info($"{text}'s sermon collection ({num}) put in {((ObjectLinkedToDefinition)(object)val).id}."); break; } } } public static string Describe() { return $"active={Active} actors={actors.Count} sent={Sent}"; } } [HarmonyPatch(typeof(UIPrayWindowData), "StartCraft")] internal static class PartnerSermonStart { private static void Prefix() { SermonSync.BeforeStart(); SermonSync.LocalCraft = SermonSync.Active; ItemScripts.Begin("A sermon's start"); } private static void Postfix() { SermonSync.AfterStart(); } private static void Finalizer() { SermonSync.LocalCraft = false; ItemScripts.End(); } } [HarmonyPatch(typeof(UIPrayWindowData), "OnPrayFinished")] internal static class PartnerSermonEnd { private static void Prefix() { ItemScripts.Begin("A sermon's end"); } private static void Finalizer() { ItemScripts.End(); SermonSync.AfterFinish(); } } [HarmonyPatch] internal static class PartnerSermonCraftStaysHere { private static IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(UIPrayWindowData), "CreateCraftElementForChosenSermon", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(UIPrayWindowData), "EraseNonStartedCraft", (Type[])null, (Type[])null); } private static void Prefix() { if (CoopSession.Role == CoopRole.Client) { SermonSync.LocalCraft = true; } } private static void Finalizer() { SermonSync.LocalCraft = false; } } [HarmonyPatch(typeof(WgoDataExtensions), "StoreSermonResult")] internal static class PartnerSermonCollection { private static bool Prefix(WgoData data, SermonResultData sermonResultData) { try { return SermonSync.BeforeStoreResult(data, sermonResultData); } catch (Exception e) { CoopLog.Error("Sending the sermon's collection to the host failed", e); return true; } } } internal static class SleepSync { private class StoredSleep { public Action OnEnded; public Action OnDidNotStart; public bool WithoutSaving; public SleepAnimType AnimType; public float Duration; } private class Sleeper { public string Name; public float Energy; public float Max = 1f; } private delegate void WriteBody(FastBufferWriter writer); private const byte OpRequest = 1; private const byte OpStatus = 2; private const byte OpStart = 3; private const byte OpRelease = 4; private const byte OpEnergy = 5; private const byte OpNotice = 6; private const float ReportInterval = 0.25f; private const float RequestCooldown = 20f; private const float GetUpAfter = 1.2f; private static readonly FieldRef RemainingSleepTime = AccessTools.FieldRefAccess("remainingSleepTime"); private static StoredSleep stored; private static bool starting; private static bool holdAsleep; private static bool inSharedSleep; private static float nextReport; private static StoredSleep requestBed; private static float getUpAt; private static float lastRequestAt = -100f; private static readonly Dictionary askedAt = new Dictionary(); private static readonly Dictionary sleepers = new Dictionary(); public static bool ScriptedSleep; public static bool InSharedSleep => inSharedSleep; public static bool HoldAsleep => holdAsleep; private static bool Active => CoopSession.KeepWorldRunning; public static void Reset() { holdAsleep = false; stored = null; starting = false; inSharedSleep = false; requestBed = null; askedAt.Clear(); sleepers.Clear(); SleepBars.Clear(); } public static bool BeforeStartSleeping(EnergySystem system, Action onEnded, Action onDidNotStart, bool withoutSaving, SleepAnimType animType, float duration) { //IL_0007: 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_0062: 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_009b: Unknown result type (might be due to invalid IL or missing references) if (!starting && Active) { PlayerController playerController = MainGame.PlayerController; if (system == ((playerController == null) ? null : playerController.PlayerData?.energySystem)) { StoredSleep bed = new StoredSleep { OnEnded = onEnded, OnDidNotStart = onDidNotStart, WithoutSaving = withoutSaving, AnimType = animType, Duration = duration }; if (CoopSession.Role == CoopRole.Host) { stored = bed; CoopLog.Info(ScriptedSleep ? $"A scripted sleep ({animType}, {duration:0.0} s): everyone sleeps now." : "In bed: everyone sleeps now."); SendToAll(delegate(FastBufferWriter w) { //IL_0008: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown //IL_0021: 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_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) byte b = 3; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); int num = (int)animType; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref duration, default(ForPrimitives)); }); BeginSleep(); return false; } if (ScriptedSleep) { stored = bed; CoopLog.Info("The story put us to bed: sleeping when the host does."); return false; } RequestSleep(bed); return false; } } return true; } private unsafe static void RequestSleep(StoredSleep bed) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) string text = PlayerSync.Host?.Label ?? "the host"; if (Time.unscaledTime - lastRequestAt < 20f) { CoopRunner.Think("I already asked " + text + " to sleep."); CoopLog.Info("In bed again: already asked the host to sleep a moment ago."); } else { lastRequestAt = Time.unscaledTime; FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Sleep", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopRunner.Toast("Sleep request sent to " + text + "."); CoopLog.Info("In bed: asked the host to sleep."); } requestBed = bed; getUpAt = Time.unscaledTime + 1.2f; } public static void BeforeRestoreEnergy(EnergySystem system) { if (!holdAsleep) { return; } PlayerController playerController = MainGame.PlayerController; if (system == ((playerController == null) ? null : playerController.PlayerData?.energySystem)) { ref float reference = ref RemainingSleepTime.Invoke(system); if (reference < 0.25f) { reference = 0.25f; } } } private unsafe static void OnRequest(ulong sender) { //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_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) //IL_0095: 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_00af: Unknown result type (might be due to invalid IL or missing references) if (askedAt.TryGetValue(sender, out var value) && Time.unscaledTime - value < 20f) { return; } askedAt[sender] = Time.unscaledTime; string text = PartnerProfiles.NameOf(sender) ?? PlayerSync.Get(sender)?.Label ?? $"Player {sender}"; CoopLog.Info(text + " is trying to sleep."); CoopRunner.Toast(text + " is trying to sleep."); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 6; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text); CoopMessages.SendToReadyClients("GK2Coop.Sleep", writer, (NetworkDelivery)3, sender); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static IEnumerable Participants() { if (CoopSession.Role == CoopRole.Client) { foreach (ulong item in sleepers.Keys.ToList()) { yield return item; } yield break; } yield return 0uL; foreach (ulong readyClient in CoopSession.ReadyClients) { yield return readyClient; } } public static void OnPlayersChanged() { if (CoopSession.Role != CoopRole.Host) { return; } foreach (ulong item in sleepers.Keys.ToList()) { if (!Participants().Contains(item)) { sleepers.Remove(item); } } } private static void BeginSleep() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; EnergySystem val = ((playerController == null) ? null : playerController.PlayerData?.energySystem); if (val != null) { inSharedSleep = true; holdAsleep = true; StoredSleep storedSleep = stored ?? new StoredSleep { Duration = 1.5f }; starting = true; try { val.StartSleeping(storedSleep.OnEnded, storedSleep.OnDidNotStart, storedSleep.WithoutSaving, true, storedSleep.AnimType, storedSleep.Duration); } finally { starting = false; } CoopLog.Info("Shared sleep started."); } } public unsafe static void Tick() { //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0285: 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_0292: 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_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; EnergySystem val = ((playerController == null) ? null : playerController.PlayerData?.energySystem); if (val == null) { SleepBars.Clear(); return; } SleepBars.Update(inSharedSleep, (from id in Participants() where sleepers.ContainsKey(id) select new KeyValuePair(sleepers[id].Name, sleepers[id].Energy / Mathf.Max(1f, sleepers[id].Max))).ToList()); if (requestBed != null && !inSharedSleep && Time.unscaledTime >= getUpAt) { Action action = requestBed.OnDidNotStart ?? requestBed.OnEnded; requestBed = null; try { action?.Invoke(); } catch (Exception e) { CoopLog.Error("Getting out of bed failed", e); } } if (inSharedSleep && !holdAsleep && !val.IsSleeping && !val.IsInTransitionBetweenSleep) { inSharedSleep = false; stored = null; sleepers.Clear(); CoopLog.Info("Shared sleep finished."); } else { if (!inSharedSleep || Time.unscaledTime < nextReport) { return; } nextReport = Time.unscaledTime + 0.25f; float energy = 0f; float max = 1f; try { PlayerEnergyGameResSystem system = PlayerEnergyGameResSystem.GetSystem(); max = Mathf.Max(1f, ((GK2GameResSystem)system).Max); energy = MainGame.PlayerController.PlayerData.GetRes("energy", 0f); } catch { } if (CoopSession.Role != CoopRole.Host) { FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 5; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref energy, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref max, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Sleep", writer, (NetworkDelivery)3); return; } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } if (!sleepers.TryGetValue(0uL, out var value)) { value = new Sleeper { Name = Plugin.PlayerName.Value }; sleepers[0uL] = value; } value.Energy = energy; value.Max = max; if (holdAsleep && Participants().All((ulong id) => sleepers.TryGetValue(id, out var value2) && value2.Energy >= value2.Max - 0.5f)) { CoopLog.Info("Everyone is rested: waking everyone up."); SendToAll(delegate(FastBufferWriter w) { //IL_0008: 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) byte b2 = 4; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b2, default(ForPrimitives)); }); holdAsleep = false; } List list = (from id in Participants() where sleepers.ContainsKey(id) select id).ToList(); SendToAll(delegate(FastBufferWriter w) { //IL_0008: 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_0026: 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_005d: 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_008b: 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_00a8: Unknown result type (might be due to invalid IL or missing references) byte b2 = 2; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b2, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref w)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (ulong item in list) { ulong current = item; Sleeper sleeper = sleepers[current]; ((FastBufferWriter)(ref w)).WriteValueSafe(ref current, default(ForPrimitives)); CoopMessages.WriteString(ref w, sleeper.Name ?? ""); ((FastBufferWriter)(ref w)).WriteValueSafe(ref sleeper.Energy, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref sleeper.Max, default(ForPrimitives)); } }); } } private unsafe static void SendToAll(WriteBody body) { //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_0012: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter writer = CoopMessages.NewWriter(); try { body(writer); CoopMessages.SendToReadyClients("GK2Coop.Sleep", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } } public static void OnSleep(ulong sender, FastBufferReader reader) { //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_0046: 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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: 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_0076: 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_0088: 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_011c: 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_0137: 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_0149: Unknown result type (might be due to invalid IL or missing references) //IL_023e: 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_024c: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 1: if (CoopSession.Role == CoopRole.Host) { byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); if (b2 != 0) { OnRequest(sender); } } break; case 5: if (CoopSession.Role == CoopRole.Host) { float energy = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref energy, default(ForPrimitives)); float max = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref max, default(ForPrimitives)); if (!sleepers.TryGetValue(sender, out var value)) { value = new Sleeper { Name = (PartnerProfiles.NameOf(sender) ?? $"Player {sender}") }; sleepers[sender] = value; } value.Energy = energy; value.Max = max; } break; case 2: if (CoopSession.Role == CoopRole.Client) { int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); sleepers.Clear(); ulong key = default(ulong); float energy2 = default(float); float max2 = default(float); for (int i = 0; i < num2; i++) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref key, default(ForPrimitives)); string name = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref energy2, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref max2, default(ForPrimitives)); sleepers[key] = new Sleeper { Name = name, Energy = energy2, Max = max2 }; } } break; case 6: if (CoopSession.Role == CoopRole.Client) { string text = CoopMessages.ReadString(ref reader); if (CoopSession.ClientInWorld) { CoopRunner.Toast(text + " is trying to sleep."); } } break; case 3: if (CoopSession.Role == CoopRole.Client) { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); float duration = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref duration, default(ForPrimitives)); if (requestBed != null) { stored = requestBed; requestBed = null; } if (stored == null) { CoopLog.Info($"Host started the shared sleep ({(object)(SleepAnimType)num}): sleeping along."); stored = new StoredSleep { Duration = duration, AnimType = (SleepAnimType)num }; } else if ((int)stored.AnimType == 0) { stored.AnimType = (SleepAnimType)num; } BeginSleep(); } break; case 4: if (CoopSession.Role == CoopRole.Client) { holdAsleep = false; CoopLog.Info("Host says everyone is rested: waking up."); } break; } } public static string Describe() { return $"shared={inSharedSleep} hold={holdAsleep} requesting={requestBed != null} sleepers={sleepers.Count}"; } } internal static class SleepBars { private class Row { public Slider Energy; public TextMeshProUGUI Name; } private static readonly FieldRef HudBarField = AccessTools.FieldRefAccess("energySanityBar"); private static readonly FieldRef EnergySliderField = AccessTools.FieldRefAccess("currentEnergySlider"); private static readonly FieldRef InsanitySliderField = AccessTools.FieldRefAccess("currentInsanitySlider"); private static readonly FieldRef InsanityIconField = AccessTools.FieldRefAccess("insanityIcon"); private static readonly FieldRef InsanityBlickField = AccessTools.FieldRefAccess("insanityBlick"); private const float MinShown = 0.058f; private const float RowGap = 10f; private static GameObject root; private static readonly List rows = new List(); private static bool failed; public static void Clear() { if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } root = null; rows.Clear(); } public static void Update(bool sleeping, List> players) { UISleepFade val = null; try { val = LazyUI.Get(); } catch { } if (!sleeping || players.Count == 0 || (Object)(object)val == (Object)null || !((UIBasicFade)val).IsFadeShowing) { if ((Object)(object)root != (Object)null) { Clear(); } if (!sleeping) { failed = false; } } else { if (failed) { return; } try { if ((Object)(object)root == (Object)null || rows.Count != players.Count) { Build(((Component)val).transform, players.Count); } for (int i = 0; i < rows.Count; i++) { Row row = rows[i]; float num = Mathf.Clamp01(players[i].Value); if ((Object)(object)row.Energy != (Object)null) { row.Energy.value = ((num > 0f && num < 0.058f) ? 0.058f : num); } if ((Object)(object)row.Name != (Object)null && ((TMP_Text)row.Name).text != players[i].Key) { ((TMP_Text)row.Name).text = players[i].Key; } } } catch (Exception ex) { CoopLog.Warn("Showing everyone's energy on the sleep screen failed: " + ex.Message); Clear(); failed = true; } } } private static void Build(Transform parent, int count) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_009c: 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_00b5: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_024b: 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_027c: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: 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_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: 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_0306: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) Clear(); HUD val = LazyUI.Get(); UIEnergySanityBar val2 = (((Object)(object)val != (Object)null) ? HudBarField.Invoke(val) : null); if ((Object)(object)val2 == (Object)null) { throw new InvalidOperationException("the HUD has no energy bar"); } TextMeshProUGUI hudLabel = val.GetHudLabel("tech_red"); root = new GameObject("GK2Coop sleep energy", new Type[1] { typeof(RectTransform) }); root.SetActive(false); RectTransform val3 = (RectTransform)root.transform; ((Transform)val3).SetParent(parent, false); Vector2 val4 = default(Vector2); ((Vector2)(ref val4))..ctor(0.5f, 0f); val3.anchorMax = val4; val3.anchorMin = val4; val3.pivot = new Vector2(0.5f, 0f); val3.anchoredPosition = new Vector2(0f, 90f); RectTransform val5 = (RectTransform)((Component)val2).transform; Rect rect = val5.rect; float num = Mathf.Max(120f, ((Rect)(ref rect)).width); rect = val5.rect; float num2 = Mathf.Max(16f, ((Rect)(ref rect)).height); val3.sizeDelta = new Vector2(num, (float)count * (num2 + 10f)); for (int i = 0; i < count; i++) { GameObject val6 = Object.Instantiate(((Component)val2).gameObject, (Transform)(object)val3, false); UIEnergySanityBar component = val6.GetComponent(); Slider val7 = EnergySliderField.Invoke(component); Slider val8 = InsanitySliderField.Invoke(component); Transform val9 = InsanityIconField.Invoke(component); GameObject val10 = InsanityBlickField.Invoke(component); UIMouseTooltip[] componentsInChildren = val6.GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { Object.DestroyImmediate((Object)(object)componentsInChildren[j]); } Object.DestroyImmediate((Object)(object)component); if ((Object)(object)val8 != (Object)null && (Object)(object)val8 != (Object)(object)val7) { ((Component)val8).gameObject.SetActive(false); } if ((Object)(object)val9 != (Object)null) { ((Component)val9).gameObject.SetActive(false); } if ((Object)(object)val10 != (Object)null) { val10.SetActive(false); } val6.SetActive(true); RectTransform val11 = (RectTransform)val6.transform; ((Vector2)(ref val4))..ctor(0.5f, 0f); val11.anchorMax = val4; val11.anchorMin = val4; val11.pivot = new Vector2(0.5f, 0f); val11.sizeDelta = new Vector2(num, num2); val11.anchoredPosition = new Vector2(0f, (float)(count - 1 - i) * (num2 + 10f)); TextMeshProUGUI val12 = null; if ((Object)(object)hudLabel != (Object)null) { GameObject obj = Object.Instantiate(((Component)hudLabel).gameObject, (Transform)(object)val11, false); obj.SetActive(true); val12 = obj.GetComponent(); ((TMP_Text)val12).alignment = (TextAlignmentOptions)4100; ((TMP_Text)val12).textWrappingMode = (TextWrappingModes)0; RectTransform val13 = (RectTransform)obj.transform; ((Vector2)(ref val4))..ctor(0f, 0.5f); val13.anchorMax = val4; val13.anchorMin = val4; val13.pivot = new Vector2(1f, 0.5f); val13.sizeDelta = new Vector2(200f, num2); val13.anchoredPosition = new Vector2(-12f, 0f); } rows.Add(new Row { Energy = val7, Name = val12 }); } root.SetActive(true); CoopLog.Debug($"Sleep screen: energy bars for {count} player(s)."); } } [HarmonyPatch(typeof(EnergySystem), "StartSleeping")] internal static class SharedSleepVote { private static bool Prefix(EnergySystem __instance, Action onSleepEndedByEnergyCallback, Action onSleepDidNotStartedCallback, bool sleepWithoutSavingGame, SleepAnimType sleepAnimType, float sleepDuration) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return SleepSync.BeforeStartSleeping(__instance, onSleepEndedByEnergyCallback, onSleepDidNotStartedCallback, sleepWithoutSavingGame, sleepAnimType, sleepDuration); } } [HarmonyPatch(typeof(EnergySystem), "RestoreEnergyWhileSleeping")] internal static class SharedSleepHold { private static void Prefix(EnergySystem __instance) { SleepSync.BeforeRestoreEnergy(__instance); } } [HarmonyPatch(typeof(Flow_GoToSleep), "GoToSleep")] internal static class ScriptedSleepFlag { private static void Prefix() { SleepSync.ScriptedSleep = true; } private static void Finalizer() { SleepSync.ScriptedSleep = false; } } internal sealed class CoopTeleportData : TeleportDataBase { private readonly string scene; private readonly Vector3 position; public CoopTeleportData(string scene, Vector3 position, string environmentPreset) : base(string.IsNullOrEmpty(environmentPreset) ? "outdoor" : environmentPreset, "", (Action)null, false, 0.3f) { //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) this.scene = scene; this.position = position; } public override string GetDestinationId() { return "gk2coop_" + scene; } public override GameSceneData GetDestinationSceneData() { return MainGame.Instance.GameSave.worldData.GetGameSceneDataById(scene); } public override Vector3 GetPosition() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return position; } } internal static class StandIn { private enum Phase { Off, Arriving, Active, Returning } private struct HeldEffect { public byte Op; public string Text; public float A; public float B; public float C; public int Kind; } private const byte OpDirector = 1; private const byte OpFade = 2; private const byte OpCameraAnim = 3; private const byte OpPostProcess = 4; private const byte OpLightOverride = 5; private const byte OpShake = 6; private const float EndGrace = 1.5f; private static readonly FieldRef WispTypeField = AccessTools.FieldRefAccess("wispType"); private static readonly MethodInfo RefreshOverheadMethod = AccessTools.Method(typeof(PlayerData), "RefreshOverheadVisuals", (Type[])null, (Type[])null); public static bool ApplyingRemote; private static float nextDirector; private static readonly FieldInfo CameraStateField = AccessTools.Field(typeof(CameraController), "currentState"); private static readonly FieldRef DollyField = AccessTools.FieldRefAccess("dollyTarget"); private static bool screenFaded; private static bool screenHudHidden; private static bool screenCinematic; private static string screenPostProcess = ""; private static string screenLight = ""; private static float screenLightIntensity; private static int screenSleepFade = -1; private static Phase phase; private static float phaseSince; private static string arrivingScene; private static float endAt = -1f; private static string savedScene; private static Vector3 savedPosition; private static Vector2 savedDirection; private static string savedPreset; private static bool fetched; private const float FetchedDistance = 25f; private static readonly Dictionary hostPresetIn = new Dictionary(); private static bool savedKinematic; private static bool savedWispActive; private static WispType savedWispType; private static bool savedWispKinematic; private static bool bodyFrozen; private static bool wispTaken; private static bool lookApplied; private static int appliedAnim = -1; private static float animAppliedAt; private static Vector2 appliedDirection; private static string appliedCarry; private static Transform cameraProxy; private static bool cameraTaken; private static bool effectsUsed; private static bool haveDirector; private static Vector3 hostFocus; private static bool hostWispActive; private static Vector3 hostWispPosition; private static float hostWispScale = 1f; private static WispType hostWispType; private static string hostPreset = ""; private static string joinScene; private static Vector3 joinPosition; private static bool joinFixPending; private static bool joinArriving; private static float joinDeadline; public static int Teleports; private static bool inPlace; private const float InPlaceRange = 25f; private const float LeavePlaceRange = 30f; private static bool joinOwnSpot; private static bool joinToHost; private static bool blackedOut; public static bool WantsBlackOut; private static Vector3 offSpot; private static float offSpotSince = -1f; private static string guardScene; private static Vector3 guardSpot; private static float guardUntil = -1f; private const float GuardSeconds = 6f; private const float GuardDrop = 3f; private const float PanSeconds = 0.5f; private static Transform savedCameraTarget; private static bool panDone; private static float panStartedAt; private static bool cameraReturning; private static float cameraReturnSince; private const float GlideBackDistance = 15f; private const float FollowAfterScene = 12f; private static Transform snapTo; private static int snapFrames; private const float FreeAfter = 5f; private const float FreeAfterFetched = 10f; private const float LockedHoldMax = 600f; private static float endRequestedAt; private static string ownPose; private const string ChainsBreak = "mc_chained_transit_02"; private static Vector3 hostSpotAtEnd; private static readonly Dictionary heldScreen = new Dictionary(); public const byte FadeEffect = 2; public const byte CameraAnimEffect = 3; public const byte PostProcessEffect = 4; public const byte LightOverrideEffect = 5; public const byte ShakeEffect = 6; public const byte SoundEffect = 7; public const byte PlaylistEffect = 8; public const byte FxEffect = 9; public const byte HudEffect = 10; public const byte GdPointEffect = 11; public const byte NoticeEffect = 12; public const byte TutorialEffect = 13; public const byte ArrowEffect = 14; public const byte CinematicEffect = 15; public const byte SleepFadeEffect = 16; public const byte TextFadeEffect = 17; public const byte EndGameEffect = 18; public const char TextSeparator = '\u001f'; private static bool cinematicShown; private static bool hudHidden; private static bool musicChanged; private static readonly FieldRef SleepHairField = AccessTools.FieldRefAccess("hrs"); private static readonly FieldRef SleepBeardField = AccessTools.FieldRefAccess("brd"); private static readonly MethodInfo SleepSkinPartMethod = AccessTools.Method(typeof(UISleepFade), "ApplySkinPart", (Type[])null, (Type[])null); private static bool sleepFadeShown; private static bool HostSends { get { if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && CoopSession.IsInGame) { return !ApplyingRemote; } return false; } } public static bool Enabled { get { if (Plugin.WatchHostScenes.Value) { return Plugin.StandInForHost.Value; } return false; } } public static bool Active { get { if (phase != Phase.Arriving) { return phase == Phase.Active; } return true; } } public static bool ActsAsHost { get { if (phase == Phase.Active) { return !inPlace; } return false; } } public static bool HidesOwnCharacter { get { if (Active) { return !inPlace; } return false; } } public static bool Showing => phase == Phase.Active; public static bool Engaged => phase != Phase.Off; public static bool CanTravel { get { if (phase != Phase.Active) { return phase == Phase.Arriving; } return true; } } public static string HostPreset => hostPreset; public static bool HostWispVisible { get { if (haveDirector && hostWispActive && PlayerSync.Host != null) { return PlayerSync.Host.Scene == MainGame.PlayerData?.currentGameSceneId; } return false; } } public static Vector3 HostWispPosition => hostWispPosition; public static bool CameraReady { get { if (phase != Phase.Active) { return false; } if (!inPlace || (Object)(object)cameraProxy == (Object)null || !haveDirector) { return true; } if (!panDone) { return Time.unscaledTime - panStartedAt > 1f; } return true; } } private static bool Gliding(CameraController camera, out Vector3 point) { //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_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) point = Vector3.zero; try { if (CameraStateField == null || CameraStateField.GetValue(camera)?.ToString() != "TrackingDolly") { return false; } Transform val = DollyField.Invoke(camera); if ((Object)(object)val == (Object)null) { return false; } point = val.position; return true; } catch { return false; } } private unsafe static void TickDirector() { //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: 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_00f4: 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_0103: 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_011c: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: 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_00bb: Unknown result type (might be due to invalid IL or missing references) if (!HostSends || Time.unscaledTime < nextDirector) { return; } nextDirector = Time.unscaledTime + (StorySync.HostStoryActive ? 0.05f : 0.5f); PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController == (Object)null) { return; } Vector3 v = (((Object)(object)playerController.View != (Object)null) ? ((Component)playerController.View).transform.position : ((Component)playerController).transform.position); try { CameraSystem instance = CameraSystem.Instance; CameraController val = ((instance != null) ? instance.GetCameraController((CameraType)0) : null); Transform val2 = ((val != null) ? val.Target : null); if ((Object)(object)val2 != (Object)null) { v = val2.position; } if ((Object)(object)val != (Object)null && Gliding(val, out var point)) { v = point; } } catch { } WispController wispController = playerController.WispController; bool flag = (Object)(object)wispController != (Object)null && ((Component)wispController).gameObject.activeInHierarchy; FastBufferWriter val3 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val3; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteVector3(ref writer, v); b = (flag ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteVector3(ref writer, ((Object)(object)wispController != (Object)null) ? ((Component)wispController).transform.position : Vector3.zero); float num = (((Object)(object)wispController != (Object)null) ? ((Component)wispController).transform.localScale.x : 1f); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); int num2 = (((Object)(object)wispController != (Object)null) ? ((int)WispTypeField.Invoke(wispController)) : 0); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num2, default(ForPrimitives)); EnvironmentEngine instance2 = EnvironmentEngine.Instance; object obj2; if (instance2 == null) { obj2 = null; } else { TimeOfDayPresets timesOfDay = instance2.TimesOfDay; obj2 = ((timesOfDay != null) ? ((Object)timesOfDay).name : null); } if (obj2 == null) { obj2 = ""; } CoopMessages.WriteString(ref writer, (string)obj2); CoopMessages.SendToReadyClients("GK2Coop.Director", writer, (NetworkDelivery)1); } finally { ((IDisposable)(*(FastBufferWriter*)(&val3))/*cast due to .constrained prefix*/).Dispose(); } } public static void HostEffect(byte op, string text, float a = 0f, float b = 0f, float c = 0f, int kind = 0) { if (CoopSession.Role == CoopRole.Host && !ApplyingRemote) { RememberScreen(op, text, a, kind); } if (HostSends) { SendEffect(null, op, text, a, b, c, kind); } } private unsafe static void SendEffect(ulong? onlyTo, byte op, string text, float a = 0f, float b = 0f, float c = 0f, int kind = 0) { //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_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_000e: 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_0031: 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_0043: 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_0055: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref op, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text ?? ""); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref a, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref c, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref kind, default(ForPrimitives)); if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Director", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Director", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void RememberScreen(byte op, string text, float a, int kind) { switch (op) { case 2: screenFaded = kind == 0 || kind == 2; break; case 10: screenHudHidden = kind != 1; break; case 15: screenCinematic = kind == 1; break; case 4: screenPostProcess = text ?? ""; break; case 5: screenLight = text ?? ""; screenLightIntensity = a; break; case 16: screenSleepFade = ((kind == 0 || kind == 2) ? ((int)a) : (-1)); break; } } public static void OnPartnerStandingIn(ulong clientId) { if (CoopSession.Role != CoopRole.Host) { return; } try { UIFade val = LazyUI.Get(); if (screenFaded && ((Object)(object)val == (Object)null || !((UIBasicFade)val).IsFadeShowing)) { screenFaded = false; } UISleepFade val2 = LazyUI.Get(); if (screenSleepFade >= 0 && ((Object)(object)val2 == (Object)null || !((UIBasicFade)val2).IsFadeShowing)) { screenSleepFade = -1; } UICinematic val3 = LazyUI.Get(); if (screenCinematic && ((Object)(object)val3 == (Object)null || !((Component)val3).gameObject.activeSelf)) { screenCinematic = false; } } catch (Exception ex) { CoopLog.Debug("Checking our screen failed: " + ex.Message); } SendEffect(clientId, 2, "", 0f, 0f, 0f, screenFaded ? 2 : 3); SendEffect(clientId, 10, "", 0f, 0f, 0f, (!screenHudHidden) ? 1 : 0); if (screenCinematic) { SendEffect(clientId, 15, "", 1f, 0f, 0f, 1); } if (!string.IsNullOrEmpty(screenPostProcess)) { SendEffect(clientId, 4, screenPostProcess); } if (!string.IsNullOrEmpty(screenLight)) { SendEffect(clientId, 5, screenLight, screenLightIntensity); } if (screenSleepFade >= 0) { SendEffect(clientId, 16, "", screenSleepFade, 0f, 0f, 2); } CoopLog.Debug($"Sent our screen to client {clientId}: faded={screenFaded} hudHidden={screenHudHidden} cinematic={screenCinematic} post='{screenPostProcess}' light='{screenLight}' sleepScreen={screenSleepFade}."); } public static void ResetHostScreen() { screenFaded = false; screenHudHidden = false; screenCinematic = false; screenPostProcess = ""; screenLight = ""; screenSleepFade = -1; } private static string PresetFor(string scene, string fallback) { RemotePlayer host = PlayerSync.Host; if (host != null && host.Scene == scene && !string.IsNullOrEmpty(hostPreset)) { return hostPreset; } if (string.IsNullOrEmpty(scene) || !hostPresetIn.TryGetValue(scene, out var value)) { return fallback; } return value; } public static void OnTravel(Vector3 position) { //IL_0041: 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) fetched = false; inPlace = false; ReleaseCamera(); if (endAt > 0f) { endAt = Time.unscaledTime + 1.5f; endRequestedAt = Time.unscaledTime; hostSpotAtEnd = position; } } public static bool HidesAvatarOf(RemotePlayer remote) { if (CoopSession.Role == CoopRole.Client && Active && !inPlace && remote != null) { return remote.ClientId == 0; } return false; } public static void Reset() { if (Engaged) { try { Restore(returnToSpot: false); } catch (Exception e) { CoopLog.Error("Restoring our character after standing in failed", e); } } phase = Phase.Off; endAt = -1f; haveDirector = false; hostPreset = ""; joinScene = null; joinFixPending = false; joinArriving = false; ApplyingRemote = false; Teleports = 0; if ((Object)(object)cameraProxy != (Object)null) { Object.Destroy((Object)(object)((Component)cameraProxy).gameObject); cameraProxy = null; } } public static void Tick() { if (CoopSession.Role == CoopRole.Host) { TickDirector(); } else { if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld) { return; } TickCameraReturn(); TickCameraSnap(); TickJoinFix(); TickGuard(); if (blackedOut && !joinFixPending && !joinArriving && phase == Phase.Off && !StorySync.Watching) { EndBlackOut(); } if (joinFixPending || joinArriving) { return; } switch (phase) { case Phase.Off: NoteOwnSpot(); if (Enabled && StorySync.Watching && !StorySync.EndingWatch && WorthPullingIn()) { Begin(); } break; case Phase.Arriving: TickArriving(); break; case Phase.Active: TickActive(); break; case Phase.Returning: if (!IsTeleporting() && (MainGame.PlayerData.currentGameSceneId == savedScene || Time.unscaledTime - phaseSince > 30f)) { SetPhase(Phase.Off); StorySync.FinishWatch("returned after the scene"); } break; } if (endAt > 0f && (Time.unscaledTime >= endAt || HostControlsBack()) && (phase == Phase.Active || phase == Phase.Arriving) && !IsTeleporting() && HostIsReallyFree()) { endAt = -1f; Restore(ShouldReturn()); } } } private static void SetPhase(Phase next) { if (phase != next) { CoopLog.Info($"Stand-in: {phase} -> {next}."); } phase = next; phaseSince = Time.unscaledTime; if (next == Phase.Off) { offSpotSince = -1f; } } private static bool IsTeleporting() { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController != (Object)null) { return !playerController.IsControlEnabledByType((TakenControlType)4); } return false; } public static void NoteJoinSpot(string scene, Vector3 position, bool ownSpot) { //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) joinScene = scene; joinPosition = position; joinOwnSpot = ownSpot; offSpotSince = -1f; } public static void BlackOut() { try { UIFade obj = LazyUI.Get(); if (obj != null) { ((UIBasicFade)obj).FadeInInstant((FadeFlag)1); } blackedOut = true; } catch (Exception ex) { CoopLog.Debug("Couldn't black out the screen: " + ex.Message); } } private static void EndBlackOut() { if (!blackedOut) { return; } blackedOut = false; try { UIFade obj = LazyUI.Get(); if (obj != null) { ((UIBasicFade)obj).FadeOut(0.35f, (Action)null, (FadeFlag)1); } } catch (Exception ex) { CoopLog.Debug("Couldn't end the black screen: " + ex.Message); } } private static void NoteOwnSpot() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if (joinOwnSpot) { return; } PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); if ((Object)(object)val == (Object)null || IsTeleporting()) { offSpotSince = -1f; return; } if (offSpotSince < 0f) { offSpotSince = Time.unscaledTime; offSpot = ((Component)val).transform.position; return; } if (!(Time.unscaledTime - offSpotSince > 10f)) { Vector3 val2 = ((Component)val).transform.position - offSpot; if (!(((Vector3)(ref val2)).sqrMagnitude > 16f)) { return; } } joinOwnSpot = true; CoopLog.Debug("We've been about on our own: after a scene that fetches us, we go back to where we were."); } public static void AfterJoinLoad() { PlayerController playerController = MainGame.PlayerController; PlayerData val = ((playerController != null) ? playerController.PlayerData : null); joinToHost = false; if (val != null && !joinOwnSpot) { FreezeBody(freeze: true); joinFixPending = true; joinToHost = true; joinDeadline = Time.unscaledTime + 6f; BlackOut(); CoopLog.Info("New to this save: joining next to the host."); } else if (val != null && !string.IsNullOrEmpty(joinScene) && !(val.currentGameSceneId == joinScene)) { WorldData worldData = MainGame.WorldData; if (((worldData != null) ? worldData.GetGameSceneDataById(joinScene) : null) == null) { CoopLog.Warn("The host's world had us in '" + joinScene + "', which isn't in this world; staying in '" + val.currentGameSceneId + "'."); } else { FreezeBody(freeze: true); joinFixPending = true; joinDeadline = Time.unscaledTime + 1.5f; CoopLog.Info("Loading put us in '" + val.currentGameSceneId + "', but the host's world has us in '" + joinScene + "'; going there."); } } } private static void TickJoinFix() { //IL_0135: 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_00aa: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) RemotePlayer host = PlayerSync.Host; bool flag = host != null && host.HasState && !host.Transitioning && !string.IsNullOrEmpty(host.Scene); if (joinFixPending) { bool num; if (!joinToHost) { num = haveDirector; } else { if (!flag) { goto IL_0050; } num = haveDirector; } if (!num) { goto IL_0050; } goto IL_005c; } goto IL_00c9; IL_0050: if (Time.unscaledTime >= joinDeadline) { goto IL_005c; } goto IL_00c9; IL_00c9: if (joinArriving && !IsTeleporting() && (MainGame.PlayerData.currentGameSceneId == joinScene || Time.unscaledTime - phaseSince > 30f)) { joinArriving = false; FreezeBody(freeze: false); if (!StorySync.Watching) { EndBlackOut(); } CoopLog.Info($"Join fix done: in '{MainGame.PlayerData.currentGameSceneId}' at {((Component)MainGame.PlayerController.PhysicalBody).transform.position}."); } return; IL_005c: joinFixPending = false; joinArriving = true; phaseSince = Time.unscaledTime; if (joinToHost && flag) { joinScene = host.Scene; joinPosition = host.Target; } Teleport(joinScene, joinPosition + new Vector3(1.2f, 0f, -0.4f), PresetFor(joinScene, hostPreset)); } private static void Teleport(string scene, Vector3 position, string preset) { //IL_0012: 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_004e: 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_0072: Unknown result type (might be due to invalid IL or missing references) Teleports++; CoopLog.Info($"Teleporting to '{scene}' at {position} (light '{preset}')."); GuardPlacement(scene, position); PlayerController playerController = MainGame.PlayerController; if (playerController.PlayerData.currentGameSceneId == scene) { playerController.SetPosition(position, true, true); } else if (!PlayerController.Teleport((TeleportDataBase)(object)new CoopTeleportData(scene, position, preset))) { CoopLog.Warn("The game refused to teleport us to '" + scene + "'."); playerController.SetPosition(position, true, true); } } private static void FreezeBody(bool freeze) { //IL_0076: 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) PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); if (!((Object)(object)((val != null) ? val.Rb : null) == (Object)null) && freeze != bodyFrozen) { if (freeze) { savedKinematic = val.Rb.isKinematic; val.Rb.linearVelocity = Vector3.zero; val.Rb.isKinematic = true; } else { val.Rb.isKinematic = savedKinematic; val.Rb.linearVelocity = Vector3.zero; } bodyFrozen = freeze; } } private static bool WorthPullingIn() { //IL_0046: 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) RemotePlayer host = PlayerSync.Host; PlayerController playerController = MainGame.PlayerController; if (host == null || !host.HasState || (Object)(object)((playerController != null) ? playerController.PhysicalBody : null) == (Object)null) { return false; } if ((!(host.Scene == playerController.PlayerData.currentGameSceneId) || !(Vector3.Distance(host.Target, ((Component)playerController.PhysicalBody).transform.position) <= 25f)) && !SceneWait.Fetched) { return StorySync.StoryIsSubstantial; } return true; } private static void Begin() { //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_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) //IL_00c2: 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_00a0: 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_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_0156: 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) RemotePlayer host = PlayerSync.Host; PlayerController playerController = MainGame.PlayerController; if (host != null && host.HasState && !((Object)(object)((playerController != null) ? playerController.PhysicalBody : null) == (Object)null)) { PlayerData playerData = playerController.PlayerData; savedScene = playerData.currentGameSceneId; savedPosition = ((Component)playerController.PhysicalBody).transform.position; savedDirection = playerData.Direction; EnvironmentEngine instance = EnvironmentEngine.Instance; object obj; if (instance == null) { obj = null; } else { TimeOfDayPresets timesOfDay = instance.TimesOfDay; obj = ((timesOfDay != null) ? ((Object)timesOfDay).name : null); } if (obj == null) { obj = ""; } savedPreset = (string)obj; fetched = joinOwnSpot && (savedScene != host.Scene || Vector3.Distance(savedPosition, host.Target) > 25f); float num = Vector3.Distance(savedPosition, host.Target); inPlace = Plugin.WatchNearbyInPlace.Value && joinOwnSpot && savedScene == host.Scene && num <= 25f && !SceneWait.IsTravelling; if (inPlace) { CoopLog.Info("The host is close by: watching from here."); } appliedAnim = -1; appliedDirection = Vector2.zero; appliedCarry = null; endAt = -1f; CoopLog.Info($"Standing in for the host: we were in '{savedScene}' at {savedPosition}; the host is in '{host.Scene}' at {host.Target}."); GoToHost(host); } } private static void GoToHost(RemotePlayer host) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) PlayerData playerData = MainGame.PlayerData; if (!string.IsNullOrEmpty(host.Scene) && host.Scene != playerData.currentGameSceneId) { WorldData worldData = MainGame.WorldData; if (((worldData != null) ? worldData.GetGameSceneDataById(host.Scene) : null) == null) { CoopLog.Warn("The host's area '" + host.Scene + "' isn't in this world; watching from here."); EnterActive(); return; } inPlace = false; arrivingScene = host.Scene; SetPhase(Phase.Arriving); Teleport(host.Scene, host.Target, hostPreset); } else { EnterActive(); } } private static void TickArriving() { if (!IsTeleporting()) { if (MainGame.PlayerData.currentGameSceneId == arrivingScene) { EnterActive(); } else if (Time.unscaledTime - phaseSince > 30f) { CoopLog.Warn("Didn't arrive in '" + arrivingScene + "' within 30 s; watching from here."); EnterActive(); } } } private static void EnterActive() { SetPhase(Phase.Active); FreezeBody(freeze: true); TakeWisp(); TakeCamera(); ApplyHeldScreen(); if (inPlace) { return; } ApplyHostLook(); Snap(); if (string.IsNullOrEmpty(PlayerSync.LastHostTrigger) || !(Time.unscaledTime - PlayerSync.LastHostTriggerAt < 120f)) { return; } try { ((AnimationComponentBase)MainGame.PlayerController.View.PlayerAnimation).SetTrigger(PlayerSync.LastHostTrigger); NoteOwnPose(PlayerSync.LastHostTrigger); } catch { } } private static void ApplyHostLook() { PlayerCustomizationData val = PlayerSync.CustomizationOf(PlayerSync.Host); if (val == null) { return; } try { PlayerSync.ApplyLook(MainGame.PlayerController.View, val); lookApplied = true; } catch (Exception ex) { CoopLog.Warn("Couldn't dress our character as the host: " + ex.Message); } } private static void Snap() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) RemotePlayer host = PlayerSync.Host; if (host != null && !(host.Scene != MainGame.PlayerData.currentGameSceneId)) { MoveBody(host.Target); } } private static Vector3 SpotBeside(Vector3 hostSpot, Vector2 facing) { //IL_0017: 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_001c: 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_002a: 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_0043: 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_005c: 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_0068: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_00a1: 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_00b3: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //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_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) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: 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_00e9: 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_0112: 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) Vector2 val = ((((Vector2)(ref facing)).sqrMagnitude > 0.0001f) ? ((Vector2)(ref facing)).normalized : Vector2.up); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(val.y, 0f, 0f - val.x); Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(val.x, 0f, val.y); Vector3[] array = (Vector3[])(object)new Vector3[6] { val2 * 0.9f, -val2 * 0.9f, -val3 * 0.9f, val3 * 0.9f, val2 * 0.5f, -val2 * 0.5f }; foreach (Vector3 val4 in array) { Vector3 val5 = hostSpot + val4; if (HasFloor(val5, hostSpot.y)) { return val5; } } CoopLog.Info("No floor beside the host: staying on their spot."); return hostSpot; } private static bool HasFloor(Vector3 position, float height) { //IL_0000: 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) try { RaycastHit val = default(RaycastHit); return SpecialPhysicsCastUtils.TryGetTopmostGroundHit(position, ref val) && Mathf.Abs(((RaycastHit)(ref val)).point.y - height) < 0.6f; } catch { return false; } } private static void GuardPlacement(string scene, Vector3 spot) { //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) guardScene = scene; guardSpot = spot; guardUntil = Time.unscaledTime + 6f; } private static void TickGuard() { //IL_0084: 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_00d3: 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_0107: 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_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (guardUntil < 0f) { return; } PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = (((Object)(object)playerController != (Object)null) ? playerController.PhysicalBody : null); if ((Object)(object)val == (Object)null || playerController.PlayerData == null || IsTeleporting()) { return; } if (playerController.PlayerData.currentGameSceneId != guardScene) { guardUntil = Time.unscaledTime + 6f; } else if (Time.unscaledTime > guardUntil) { guardUntil = -1f; } else if (((Component)val).transform.position.y < guardSpot.y - 3f) { RemotePlayer host = PlayerSync.Host; Vector3 val2 = ((host != null && host.HasState && host.Scene == guardScene) ? host.Target : guardSpot); if ((Object)(object)val.Rb != (Object)null) { val.Rb.linearVelocity = Vector3.zero; } MoveBody(val2); guardUntil = -1f; CoopLog.Warn($"Fell out of the world after being placed at {guardSpot} in '{guardScene}': put back at {val2}."); } } private static void MoveBody(Vector3 position) { //IL_0012: 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_002c: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody physicalBody = playerController.PhysicalBody; ((Component)physicalBody).transform.position = position; if ((Object)(object)physicalBody.Rb != (Object)null) { physicalBody.Rb.position = position; } playerController.PlayerData.position.Value = position; } private static void TickActive() { //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_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: 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_0209: 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_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0201: 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_028c: Invalid comparison between Unknown and I4 //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0268: 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_012b: 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) RemotePlayer host = PlayerSync.Host; PlayerController playerController = MainGame.PlayerController; if (host == null || !host.HasState || (Object)(object)playerController == (Object)null) { Restore(ShouldReturn()); return; } if (SceneWait.Travelling(out var scene, out var position, out var preset)) { if (playerController.PlayerData.currentGameSceneId != scene) { if (!IsTeleporting()) { WorldData worldData = MainGame.WorldData; if (((worldData != null) ? worldData.GetGameSceneDataById(scene) : null) == null) { CoopLog.Warn("The host's story is going to '" + scene + "', which isn't in this world; staying here."); SceneWait.CancelTravel(); } else { arrivingScene = scene; SetPhase(Phase.Arriving); Teleport(scene, position, preset); } } return; } if (host.Scene != scene) { return; } if (!cameraTaken) { TakeCamera(); } } if (!host.Transitioning && !string.IsNullOrEmpty(host.Scene) && host.Scene != playerController.PlayerData.currentGameSceneId) { GoToHost(host); return; } if (inPlace && (Object)(object)playerController.PhysicalBody != (Object)null && (host.Scene != playerController.PlayerData.currentGameSceneId || Vector3.Distance(host.Target, ((Component)playerController.PhysicalBody).transform.position) > 30f)) { CoopLog.Info("The host's scene moved away from us: standing in for them from here."); inPlace = false; ApplyHostLook(); Snap(); if (!string.IsNullOrEmpty(PlayerSync.LastHostTrigger) && Time.unscaledTime - PlayerSync.LastHostTriggerAt < 120f) { try { ((AnimationComponentBase)playerController.View.PlayerAnimation).SetTrigger(PlayerSync.LastHostTrigger); NoteOwnPose(PlayerSync.LastHostTrigger); } catch { } } } bool cinematicPlaying = StorySync.CinematicPlaying; if (!inPlace && !cinematicPlaying) { Vector3 position2 = ((Component)playerController.PhysicalBody).transform.position; float num = 1f - Mathf.Exp(-15f * Time.deltaTime); Vector3 val = host.Target - position2; MoveBody((((Vector3)(ref val)).sqrMagnitude > 36f) ? host.Target : Vector3.Lerp(position2, host.Target, num)); if (((Vector2)(ref host.Direction)).sqrMagnitude > 0.0001f) { Vector2 val2 = host.Direction - appliedDirection; if (((Vector2)(ref val2)).sqrMagnitude > 0.0001f) { appliedDirection = host.Direction; playerController.PlayerData.Direction = host.Direction; playerController.View.UpdateAnimationDirection(host.Direction); } } PlayerAnimation playerAnimation = playerController.View.PlayerAnimation; bool flag = (int)((AnimationComponentBase)playerAnimation).GetState() != host.Anim; if (host.Anim != appliedAnim || (flag && Time.unscaledTime - animAppliedAt > 0.5f)) { appliedAnim = host.Anim; animAppliedAt = Time.unscaledTime; ((AnimationComponentBase)playerAnimation).SetState((AnimationState)host.Anim); } ShowHostCarry(host); } DriveWisp(); if (!cinematicPlaying) { DriveCamera(); } if (!string.IsNullOrEmpty(hostPreset) && (Object)(object)EnvironmentEngine.Instance != (Object)null && (Object)(object)EnvironmentEngine.Instance.TimesOfDay != (Object)null && ((Object)EnvironmentEngine.Instance.TimesOfDay).name != hostPreset) { EnvironmentEngine.Instance.SetTimeOfDayPreset(hostPreset); } } private static void ShowHostCarry(RemotePlayer host) { string text = ((host.CarryBytes == null) ? "" : Convert.ToBase64String(host.CarryBytes)); if (text == appliedCarry) { return; } appliedCarry = text; PlayerController playerController = MainGame.PlayerController; try { List list = ((host.CarryBytes == null) ? new List() : (SerializationUtility.DeserializeValue>(host.CarryBytes, (DataFormat)0, (DeserializationContext)null) ?? new List())); if (list.Count == 0) { playerController.RemoveOverheadItem(); } else { playerController.SetOverheadItems((IReadOnlyList)list); } } catch (Exception ex) { CoopLog.Warn("Couldn't show what the host carries: " + ex.Message); } } private static void TakeWisp() { //IL_003f: 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) PlayerController playerController = MainGame.PlayerController; WispController val = ((playerController != null) ? playerController.WispController : null); if (!((Object)(object)val == (Object)null) && !wispTaken) { savedWispActive = ((Component)val).gameObject.activeSelf; savedWispType = WispTypeField.Invoke(val); Rigidbody component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { savedWispKinematic = component.isKinematic; component.linearVelocity = Vector3.zero; component.isKinematic = true; } ((Behaviour)val).enabled = false; wispTaken = true; } } private static void DriveWisp() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between I4 and Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //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_009c: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00f7: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; WispController val = ((playerController != null) ? playerController.WispController : null); if ((Object)(object)val == (Object)null || !wispTaken || !haveDirector) { return; } if (((Component)val).gameObject.activeSelf != hostWispActive) { val.ChangeActiveState(hostWispActive); } if ((int)WispTypeField.Invoke(val) != (int)hostWispType) { val.ChangeWispType(hostWispType); } if (hostWispActive) { Vector3 position = ((Component)val).transform.position; float num = 1f - Mathf.Exp(-12f * Time.deltaTime); Transform transform = ((Component)val).transform; Vector3 val2 = hostWispPosition - position; transform.position = ((((Vector3)(ref val2)).sqrMagnitude > 25f) ? hostWispPosition : Vector3.Lerp(position, hostWispPosition, num)); Vector3 localScale = ((Component)val).transform.localScale; if (!Mathf.Approximately(Mathf.Sign(localScale.x), Mathf.Sign(hostWispScale))) { ((Component)val).transform.localScale = new Vector3(Mathf.Abs(localScale.x) * Mathf.Sign(hostWispScale), localScale.y, localScale.z); } } } private static void ReleaseWisp() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Invalid comparison between I4 and Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; WispController val = ((playerController != null) ? playerController.WispController : null); if (!((Object)(object)val == (Object)null) && wispTaken) { wispTaken = false; ((Behaviour)val).enabled = true; Rigidbody component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { component.isKinematic = savedWispKinematic; } if ((int)WispTypeField.Invoke(val) != (int)savedWispType) { val.ChangeWispType(savedWispType); } if (((Component)val).gameObject.activeSelf != savedWispActive) { val.ChangeActiveState(savedWispActive); } if (savedWispActive) { val.TeleportToTarget(true); } } } private static void TakeCamera() { //IL_0056: 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_0061: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_012f: 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_0132: Unknown result type (might be due to invalid IL or missing references) CameraSystem instance = CameraSystem.Instance; CameraController val = ((instance != null) ? instance.GetCameraController((CameraType)0) : null); if ((Object)(object)val == (Object)null || (SceneWait.Travelling(out var scene, out var _, out var _) && PlayerSync.Host?.Scene != scene)) { return; } if ((Object)(object)cameraProxy == (Object)null) { GameObject val2 = new GameObject("GK2Coop camera focus"); Object.DontDestroyOnLoad((Object)val2); cameraProxy = val2.transform; } if (cameraTaken) { val.SetTargetInstant(cameraProxy); return; } Transform target = val.Target; savedCameraTarget = (((Object)(object)target != (Object)null && (Object)(object)target != (Object)(object)cameraProxy) ? target : null); Vector3 position2 = ((Component)MainGame.PlayerController.View).transform.position; cameraTaken = true; if (inPlace && haveDirector) { cameraProxy.position = hostFocus; panDone = false; panStartedAt = Time.unscaledTime; val.SetTarget(cameraProxy, 0.5f, (Action)delegate { panDone = true; }); } else { cameraProxy.position = ((haveDirector && !SceneWait.IsTravelling) ? hostFocus : position2); val.SetTargetInstant(cameraProxy); panDone = true; } } private static void DriveCamera() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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_00ee: 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_00e4: 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_0076: Unknown result type (might be due to invalid IL or missing references) if (cameraTaken && !((Object)(object)cameraProxy == (Object)null) && haveDirector) { Vector3 position = cameraProxy.position; RemotePlayer host = PlayerSync.Host; PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); Vector3 val2 = ((host != null && host.Scene != MainGame.PlayerData?.currentGameSceneId && (Object)(object)val != (Object)null) ? ((Component)val).transform.position : hostFocus); if (inPlace) { float num = 1f - Mathf.Exp(-8f * Time.deltaTime); cameraProxy.position = Vector3.Lerp(position, val2, num); return; } float num2 = 1f - Mathf.Exp(-14f * Time.deltaTime); Transform obj = cameraProxy; Vector3 val3 = val2 - position; obj.position = ((((Vector3)(ref val3)).sqrMagnitude > 64f) ? val2 : Vector3.Lerp(position, val2, num2)); } } private static void TickCameraReturn() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_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_0087: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_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_00de: 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_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) if (!cameraReturning) { return; } CameraSystem instance = CameraSystem.Instance; CameraController val = ((instance != null) ? instance.GetCameraController((CameraType)0) : null); PlayerController playerController = MainGame.PlayerController; PlayerView val2 = ((playerController != null) ? playerController.View : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)cameraProxy == (Object)null) { cameraReturning = false; return; } Vector3 position = cameraProxy.position; Vector3 position2 = ((Component)val2).transform.position; float num = 1f - Mathf.Exp(-6f * Time.deltaTime); cameraProxy.position = Vector3.MoveTowards(position, Vector3.Lerp(position, position2, num), 30f * Time.deltaTime + 0.02f); Vector3 val3 = cameraProxy.position - position2; if (!(((Vector3)(ref val3)).sqrMagnitude < 0.09f) && !(Time.unscaledTime - cameraReturnSince > 2f)) { val3 = position - position2; if (!(((Vector3)(ref val3)).sqrMagnitude > 3600f)) { return; } } cameraReturning = false; val.SetTargetInstant(((Component)val2).transform); } private static void TickCameraSnap() { if (snapFrames > 0) { snapFrames--; CameraSystem instance = CameraSystem.Instance; CameraController val = ((instance != null) ? instance.GetCameraController((CameraType)0) : null); if ((Object)(object)val == (Object)null || (Object)(object)snapTo == (Object)null || cameraTaken) { snapFrames = 0; } else { val.SetTargetInstant(snapTo); } } } private static void ReleaseCamera(bool pan = false, Vector3? landing = null) { //IL_0089: 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_008e: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if (!cameraTaken) { return; } cameraTaken = false; CameraSystem instance = CameraSystem.Instance; CameraController val = ((instance != null) ? instance.GetCameraController((CameraType)0) : null); PlayerController playerController = MainGame.PlayerController; PlayerView val2 = ((playerController != null) ? playerController.View : null); Transform val3 = (((Object)(object)savedCameraTarget != (Object)null) ? savedCameraTarget : (((Object)(object)val2 != (Object)null) ? ((Component)val2).transform : null)); savedCameraTarget = null; if ((Object)(object)val == (Object)null || (Object)(object)val3 == (Object)null) { return; } Vector3 val4 = (Vector3)(((??)landing) ?? val3.position); if (pan && (Object)(object)cameraProxy != (Object)null) { Vector3 val5 = cameraProxy.position - val4; if (((Vector3)(ref val5)).sqrMagnitude < 225f) { val.SetTarget(val3, 0.9f, (Action)null); return; } } val.SetTargetInstant(val3); snapTo = val3; snapFrames = 6; } private static void Restore(bool returnToSpot) { //IL_01d2: 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_023d: 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_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: 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_0301: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: 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) PlayerController playerController = MainGame.PlayerController; bool flag = !inPlace && phase == Phase.Active; bool flag2 = !returnToSpot || savedScene == ((playerController == null) ? null : playerController.PlayerData?.currentGameSceneId); Vector3? landing = null; ReleaseWisp(); if (lookApplied && ((playerController == null) ? null : playerController.PlayerData?.customization) != null) { try { PlayerSync.ApplyLook(playerController.View, playerController.PlayerData.customization); } catch (Exception ex) { CoopLog.Warn("Couldn't put our own outfit back: " + ex.Message); } lookApplied = false; } if (appliedCarry != null && ((playerController != null) ? playerController.PlayerData : null) != null) { RefreshOverheadMethod?.Invoke(playerController.PlayerData, null); appliedCarry = null; } ClearEffects(); try { if (playerController != null) { PlayerView view = playerController.View; if (view != null) { PlayerAnimation playerAnimation = view.PlayerAnimation; if (playerAnimation != null) { ((AnimationComponentBase)playerAnimation).SetState((AnimationState)0); } } } if (IsChainedPose(ownPose)) { if (playerController != null) { PlayerView view2 = playerController.View; if (view2 != null) { PlayerAnimation playerAnimation2 = view2.PlayerAnimation; if (playerAnimation2 != null) { ((AnimationComponentBase)playerAnimation2).SetTrigger("mc_chained_transit_02"); } } } CoopLog.Info("Our character was still in the host's pose '" + ownPose + "': out of the chains."); } } catch { } ownPose = null; FreezeBody(freeze: false); int num; if (returnToSpot && !string.IsNullOrEmpty(savedScene) && (Object)(object)playerController != (Object)null && savedScene != playerController.PlayerData.currentGameSceneId) { WorldData worldData = MainGame.WorldData; num = ((((worldData != null) ? worldData.GetGameSceneDataById(savedScene) : null) != null) ? 1 : 0); } else { num = 0; } bool flag3 = (byte)num != 0; if (returnToSpot && (Object)(object)playerController != (Object)null && !string.IsNullOrEmpty(savedScene)) { CoopLog.Info($"Scene over: back to '{savedScene}' at {savedPosition}."); if (flag3) { ReleaseCamera(); Teleport(savedScene, savedPosition, PresetFor(savedScene, savedPreset)); } else { playerController.SetPosition(savedPosition, true, true); SetPreset(PresetFor(savedScene, savedPreset)); landing = savedPosition; } if (((Vector2)(ref savedDirection)).sqrMagnitude > 0.0001f) { playerController.PlayerData.Direction = savedDirection; playerController.View.UpdateAnimationDirection(savedDirection); } } if (!returnToSpot && (Object)(object)playerController != (Object)null) { CoopLog.Info("Scene over: staying with the host in '" + playerController.PlayerData.currentGameSceneId + "'."); SetPreset(PresetFor(playerController.PlayerData.currentGameSceneId, "")); RemotePlayer host = PlayerSync.Host; int num2; if (host != null && host.HasState && (Object)(object)playerController.PhysicalBody != (Object)null) { Vector3 val = ((Component)playerController.PhysicalBody).transform.position - host.Target; num2 = ((((Vector3)(ref val)).sqrMagnitude > 144f) ? 1 : 0); } else { num2 = 0; } bool flag4 = (byte)num2 != 0; if ((flag || flag4) && host != null && host.HasState && host.Scene == playerController.PlayerData.currentGameSceneId && (Object)(object)playerController.PhysicalBody != (Object)null) { if (!flag) { CoopLog.Info($"The scene took the host {Vector3.Distance(((Component)playerController.PhysicalBody).transform.position, host.Target):0} m away: going over to them."); } Vector3 val2 = SpotBeside(host.Target, host.Direction); MoveBody(val2); GuardPlacement(playerController.PlayerData.currentGameSceneId, val2); landing = val2; } } ReleaseCamera(flag2 && !flag3, landing); PlayerSync.RefreshOtherLooks(); heldScreen.Clear(); inPlace = false; if (flag3) { SetPhase(Phase.Returning); return; } SetPhase(Phase.Off); StorySync.FinishWatch("the host's scene is over"); } private static void SetPreset(string preset) { try { if (!string.IsNullOrEmpty(preset) && (Object)(object)EnvironmentEngine.Instance != (Object)null) { TimeOfDayPresets timesOfDay = EnvironmentEngine.Instance.TimesOfDay; if (((timesOfDay != null) ? ((Object)timesOfDay).name : null) != preset) { EnvironmentEngine.Instance.SetTimeOfDayPreset(preset); CoopLog.Debug("Light preset -> '" + preset + "'."); } } } catch (Exception ex) { CoopLog.Warn("Setting the light preset '" + preset + "' failed: " + ex.Message); } } private static bool ShouldReturn() { if (!fetched) { return false; } if (CombatSync.FightAroundHost) { CoopLog.Info("The scene led into a fight; staying with the host."); return false; } return Plugin.ReturnAfterScenes.Value; } public static void RequestEnd() { //IL_004a: 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_0043: 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) if (endAt < 0f) { endAt = Time.unscaledTime + 1.5f; endRequestedAt = Time.unscaledTime; hostSpotAtEnd = (SceneWait.Travelling(out var _, out var position, out var _) ? position : (PlayerSync.Host?.Target ?? Vector3.zero)); } } private static bool HostIsReallyFree() { //IL_00e2: 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_00a1: 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) RemotePlayer host = PlayerSync.Host; if (host == null || !host.HasState) { return true; } if (SceneWait.IsTravelling) { return false; } if (host.Scene != MainGame.PlayerData?.currentGameSceneId && !string.IsNullOrEmpty(host.Scene)) { return true; } if (host.MovementLocked && Time.unscaledTime - endRequestedAt < 600f) { return false; } PlayerData playerData = MainGame.PlayerData; bool flag = fetched && playerData != null && !string.IsNullOrEmpty(savedScene) && savedScene != playerData.currentGameSceneId; if (!flag && host.ControlsFree && (Vector3.Distance(host.Target, hostSpotAtEnd) > 0.75f || Time.unscaledTime - endRequestedAt > 3f)) { return true; } if (flag) { return Time.unscaledTime - endRequestedAt > 10f; } if (!(Vector3.Distance(host.Target, hostSpotAtEnd) > 1.5f)) { return Time.unscaledTime - endRequestedAt > 5f; } return true; } public static void NoteOwnPose(string trigger) { ownPose = trigger; } private static bool IsChainedPose(string trigger) { if (!string.IsNullOrEmpty(trigger) && trigger.StartsWith("mc_chained_")) { return trigger != "mc_chained_transit_02"; } return false; } private static bool HostControlsBack() { RemotePlayer host = PlayerSync.Host; if (host != null && host.HasState && host.ControlsFree && !host.MovementLocked) { return !SceneWait.IsTravelling; } return false; } public static void TravelAlong(string scene, Vector3 position, string preset) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!IsTeleporting()) { CoopLog.Info("The host is going to '" + scene + "' (the intro): going along."); Teleport(scene, position, preset); } } public static void FollowDoor(string scene, Vector3 position, string preset) { //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_0040: 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_0052: 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_0076: 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_0096: Unknown result type (might be due to invalid IL or missing references) PlayerController playerController = MainGame.PlayerController; if (((playerController != null) ? playerController.PlayerData : null) != null && !Engaged && !IsTeleporting() && !(playerController.PlayerData.currentGameSceneId != scene)) { Vector3 val = SpotBeside(position, PlayerSync.Host?.Direction ?? Vector2.zero); Teleports++; CoopLog.Info($"The host went through a door to {position} (light '{preset}'): following."); GuardPlacement(scene, val); if (!PlayerController.Teleport((TeleportDataBase)(object)new CoopTeleportData(scene, val, preset))) { CoopLog.Warn("The game refused to take us through the door; moving us there."); playerController.SetPosition(val, true, true); SetPreset(PresetFor(scene, preset)); } } } public static void CancelEnd() { endAt = -1f; } public static void ForceStop() { if (Engaged) { try { Restore(returnToSpot: false); } catch (Exception e) { CoopLog.Error("Restoring our character after standing in failed", e); } } phase = Phase.Off; endAt = -1f; } private static void ClearEffects() { if (!effectsUsed) { return; } effectsUsed = false; ApplyingRemote = true; try { ClearStoryEffects(); UIFade val = LazyUI.Get(); if ((Object)(object)val != (Object)null && ((UIBasicFade)val).IsFadeShowing) { ((UIBasicFade)val).FadeOutInstant((FadeFlag)1); } CameraSystem instance = CameraSystem.Instance; if (instance != null) { MainCamera mainCamera = instance.MainCamera; if (mainCamera != null) { mainCamera.ResetPostProcessProfile(); } } EnvironmentEngine instance2 = EnvironmentEngine.Instance; if (instance2 != null) { instance2.ApplyOverridePreset((LightEnvironmentPreset)null, 0f); } } catch (Exception ex) { CoopLog.Warn("Clearing the host's scene effects failed: " + ex.Message); } finally { ApplyingRemote = false; } } private static bool IsScreenEffect(byte op) { if (op != 2 && op != 4 && op != 5 && op != 10) { return op == 15; } return true; } private static void ApplyHeldScreen() { blackedOut = false; if (heldScreen.Count == 0) { return; } List list = new List(heldScreen.Values); heldScreen.Clear(); foreach (HeldEffect item in list) { int kind = (((item.Op == 2 || item.Op == 16) && item.Kind <= 1) ? (item.Kind + 2) : item.Kind); if (item.Op == 10 || item.Op == 15 || item.Op == 16) { ApplyingRemote = true; try { ApplyStoryEffect(item.Op, item.Text, (item.Op == 15) ? 1f : item.A, item.B, item.C, kind); } catch (Exception ex) { CoopLog.Warn("Applying the host's HUD state failed: " + ex.Message); } finally { ApplyingRemote = false; } } else { ApplyEffect(item.Op, item.Text, item.A, item.B, item.C, kind); } } CoopLog.Debug($"Applied {list.Count} screen effect(s) from before we were in place."); } private static void ApplyEffect(byte op, string text, float a, float b, float c, int kind) { if (!Showing) { if (IsScreenEffect(op) && Active) { heldScreen[op] = new HeldEffect { Op = op, Text = text, A = a, B = b, C = c, Kind = kind }; } return; } effectsUsed = true; ApplyingRemote = true; try { switch (op) { case 2: { UIFade val = LazyUI.Get(); if (!((Object)(object)val == (Object)null)) { switch (kind) { case 0: ((UIBasicFade)val).FadeIn(a, (Action)null, (FadeFlag)1, false); break; case 1: ((UIBasicFade)val).FadeOut(a, (Action)null, (FadeFlag)1); break; case 2: ((UIBasicFade)val).FadeInInstant((FadeFlag)1); break; case 3: ((UIBasicFade)val).FadeOutInstant((FadeFlag)1); break; } } break; } case 3: { CameraSystem instance5 = CameraSystem.Instance; if (instance5 != null) { MainCamera mainCamera = instance5.MainCamera; if (mainCamera != null) { mainCamera.PlayAnimation(text, (Action)delegate { }); } } break; } case 4: { if (string.IsNullOrEmpty(text)) { CameraSystem instance6 = CameraSystem.Instance; if (instance6 != null) { MainCamera mainCamera2 = instance6.MainCamera; if (mainCamera2 != null) { mainCamera2.ResetPostProcessProfile(); } } break; } CameraSystem instance7 = CameraSystem.Instance; if (instance7 != null) { MainCamera mainCamera3 = instance7.MainCamera; if (mainCamera3 != null) { mainCamera3.SetPostProcessProfile(text); } } break; } case 5: if (string.IsNullOrEmpty(text)) { EnvironmentEngine instance3 = EnvironmentEngine.Instance; if (instance3 != null) { instance3.ApplyOverridePreset((LightEnvironmentPreset)null, 0f); } } else { EnvironmentEngine instance4 = EnvironmentEngine.Instance; if (instance4 != null) { instance4.ApplyOverridePreset(text, a); } } break; case 6: { if (kind < 0) { CameraSystem instance = CameraSystem.Instance; if (instance != null) { CameraController cameraController = instance.GetCameraController((CameraType)0); if (cameraController != null) { cameraController.ShakeCamera(a, b, c); } } break; } CameraSystem instance2 = CameraSystem.Instance; if (instance2 != null) { CameraController cameraController2 = instance2.GetCameraController((CameraType)0); if (cameraController2 != null) { cameraController2.ShakeCamera((CameraNoiseType)kind, a, b, c, (Action)null); } } break; } } } catch (Exception ex) { CoopLog.Warn($"Replaying the host's scene effect {op} '{text}' failed: {ex.Message}"); } finally { ApplyingRemote = false; } } public static void OnDirector(ulong sender, FastBufferReader reader) { //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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0100: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0119: 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) //IL_012c: 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_002a: 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_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_0057: 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_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_00d3: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role != CoopRole.Client) { return; } if (b == 1) { hostFocus = CoopMessages.ReadVector3(ref reader); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); hostWispPosition = CoopMessages.ReadVector3(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref hostWispScale, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); hostPreset = CoopMessages.ReadString(ref reader); RemotePlayer host = PlayerSync.Host; if (host != null && !host.Transitioning && !string.IsNullOrEmpty(host.Scene) && !string.IsNullOrEmpty(hostPreset)) { hostPresetIn[host.Scene] = hostPreset; } hostWispActive = b2 != 0; hostWispType = (WispType)num; haveDirector = true; return; } string text = CoopMessages.ReadString(ref reader); float a = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref a, default(ForPrimitives)); float b3 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b3, default(ForPrimitives)); float c = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref c, default(ForPrimitives)); int kind = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref kind, default(ForPrimitives)); if (!CoopSession.ClientInWorld) { return; } if (b >= 7) { if (!AppliesOutsideStandIn(b, out var apply)) { apply = Showing; } if (apply) { ApplyingRemote = true; try { if (b != 11 && b != 9) { effectsUsed = true; } ApplyStoryEffect(b, text, a, b3, c, kind); return; } catch (Exception ex) { CoopLog.Warn($"Replaying the host's story effect {b} '{text}' failed: {ex.Message}"); return; } finally { ApplyingRemote = false; } } if ((b == 10 || b == 15 || b == 16) && Active) { heldScreen[b] = new HeldEffect { Op = b, Text = text, A = a, B = b3, C = c, Kind = kind }; } } else { ApplyEffect(b, text, a, b3, c, kind); } } public static string Describe() { if (CoopSession.Role != CoopRole.Host) { return $"phase={phase} saved='{savedScene}' director={haveDirector} preset='{hostPreset}' wisp={hostWispActive} teleports={Teleports} joinFix={joinFixPending || joinArriving}"; } return $"director story={StorySync.HostStoryActive}"; } private static bool AppliesOutsideStandIn(byte op, out bool apply) { apply = false; switch (op) { case 18: apply = true; return true; case 11: case 12: case 14: apply = true; return true; case 7: case 8: case 9: { RemotePlayer host = PlayerSync.Host; apply = host != null && host.Scene == MainGame.PlayerData?.currentGameSceneId; return true; } default: return false; } } private static void ApplyStoryEffect(byte op, string text, float a, float b, float c, int kind) { //IL_005b: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown switch (op) { case 7: if (kind == 1) { LazyAudio.Stop(text); } else { LazyAudio.Play(text); } break; case 8: ApplyPlaylist(text, a, kind); break; case 9: WorldFX.Spawn(new Vector3(a, b, c), text, (Action)null, Vector3.one); break; case 10: { GUIElements instance2 = GUIElements.Instance; if (instance2 != null) { instance2.SetVisibilityState(kind == 1); } hudHidden = kind != 1; break; } case 12: { string[] array2 = (text ?? "").Split('|'); if (kind == 1) { LazySingleton.Instance.ShowNewBodyNotification(array2[0]); } else { LazySingleton.Instance.ShowSimpleTextWithIconNotification(array2[0], (array2.Length > 1) ? array2[1] : ""); } break; } case 13: ((LazyWindow)(object)LazyUI.GetWindow())?.Open(new UITutorialWindowData(text, (Action)null, false)); break; case 16: ApplySleepFade(kind, (int)a); break; case 17: { UIFadeWithText val4 = LazyUI.Get(); if (!((Object)(object)val4 == (Object)null)) { string[] array = (text ?? "").Split('\u001f'); if (array.Length > 1) { val4.ScreenTextFade(new List(array), a, b, c, (float)kind / 1000f, (Action)null, (Action)null, (TextStyle)null); } else { val4.ScreenTextFade(text ?? "", a, b, c, (float)kind / 1000f, (Action)null, (Action)null, (TextStyle)null); } CoopLog.Info("Showing the host's text screen '" + (text ?? "").Replace('\u001f', '/') + "'."); } break; } case 18: { CoopLog.Info("The host's game is complete: the credits."); MainGame instance3 = MainGame.Instance; if (instance3 != null) { instance3.CompleteGame(kind == 1, (Action)null); } break; } case 15: { UICinematic val2 = LazyUI.Get(); if (!((Object)(object)val2 == (Object)null)) { if (kind == 1) { val2.EnableCinematic((Action)null, a > 0.5f); cinematicShown = true; } else { val2.DisableCinematic((Action)null, a > 0.5f); cinematicShown = false; } } break; } case 14: { object obj2; if (!string.IsNullOrEmpty(text)) { WorldData worldData2 = MainGame.WorldData; obj2 = ((worldData2 != null) ? worldData2.GetWgoData(SGuid.Parse(text)) : null); } else { obj2 = null; } WgoData val3 = (WgoData)obj2; if (val3 != null) { LazySingleton.Instance.Attach(val3); } else { LazySingleton.Instance.UnAttach(); } break; } case 11: { MainGame instance = MainGame.Instance; object obj; if (instance == null) { obj = null; } else { GameSave gameSave = instance.GameSave; if (gameSave == null) { obj = null; } else { WorldData worldData = gameSave.worldData; if (worldData == null) { obj = null; } else { GdPointsData gdPointsData = worldData.gdPointsData; obj = ((gdPointsData != null) ? gdPointsData.GetGDPointDataById(text) : null); } } } GDPointData val = (GDPointData)obj; if (val != null && val.Enabled != (kind == 1)) { val.Enabled = kind == 1; CoopLog.Debug("Path point '" + text + "' " + ((kind == 1) ? "opened" : "closed") + " by the host's story."); } break; } } } private static void ApplyPlaylist(string text, float duration, int kind) { //IL_002f: 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_005e: Expected I4, but got Unknown musicChanged = true; string[] array = (text ?? "").Split('|'); string text2 = array[0]; string text3 = ((array.Length > 1) ? array[1] : ""); ControlType val = (ControlType)kind; switch ((int)val) { case 0: if (duration <= 0f) { LazyAudio.PlayPlaylist(text2); } else { LazyAudio.PlayPlaylist(text2, duration); } break; case 1: case 7: if (duration <= 0f) { LazyAudio.PausePlaylist(text2); } else { LazyAudio.PausePlaylist(text2, duration); } break; case 8: case 9: if (duration <= 0f) { LazyAudio.UnPausePlaylist(text2); } else { LazyAudio.UnPausePlaylist(text2, duration); } break; case 2: if (duration <= 0f) { LazyAudio.StopPlaylist(text2); } else { LazyAudio.StopPlaylist(text2, duration); } break; case 3: LazyAudio.PlayNextTrack(text2); break; case 4: if (!string.IsNullOrEmpty(text3)) { LazyAudio.PlayTrackInPlaylist(text3, text2); } break; case 5: case 6: break; } } private static void ApplySleepFade(int kind, int anim) { UISleepFade val = LazyUI.Get(); if (!((Object)(object)val == (Object)null)) { switch (kind) { case 0: case 2: val.FadeIn((Action)null, (FadeFlag)0, false, (SleepAnimType)anim, kind == 2); ShowHostInBed(val); sleepFadeShown = true; break; case 1: val.FadeOut((Action)null, false); sleepFadeShown = false; break; case 3: val.FadeOut((Action)null, true); sleepFadeShown = false; break; } CoopLog.Debug("The host's sleep screen: " + ((kind == 0 || kind == 2) ? $"up ({(object)(SleepAnimType)anim})" : "down") + ((kind >= 2) ? " at once" : "") + "."); } } private static void ShowHostInBed(UISleepFade sleep) { try { RemotePlayer host = PlayerSync.Host; object obj; if (host == null) { obj = null; } else { PlayerPhysicalBody body = host.Body; if (body == null) { obj = null; } else { PlayerView playerView = body.PlayerView; if (playerView == null) { obj = null; } else { PlayerAnimation playerAnimation = playerView.PlayerAnimation; obj = ((playerAnimation != null) ? ((AnimationComponentBase)playerAnimation).SkinPreset : null); } } } SkinPresetGK2 val = (SkinPresetGK2)obj; if (!((Object)(object)val == (Object)null) && !(SleepSkinPartMethod == null)) { SleepSkinPartMethod.Invoke(null, new object[4] { SleepBeardField.Invoke(sleep), val.beard.id + "_brd_static_down", val, val.beard }); SleepSkinPartMethod.Invoke(null, new object[4] { SleepHairField.Invoke(sleep), val.hairstyle.id + "_hrs_static_down", val, val.hairstyle }); } } catch (Exception ex) { CoopLog.Debug("Putting the host's look in the bed failed: " + ex.Message); } } private static void ClearStoryEffects() { if (sleepFadeShown) { sleepFadeShown = false; PlayerController playerController = MainGame.PlayerController; EnergySystem val = ((playerController == null) ? null : playerController.PlayerData?.energySystem); UISleepFade val2 = LazyUI.Get(); if ((Object)(object)val2 != (Object)null && ((UIBasicFade)val2).IsFadeShowing && (val == null || (!val.IsSleeping && !val.IsInTransitionBetweenSleep))) { val2.FadeOut((Action)null, true); } } if (cinematicShown) { cinematicShown = false; UICinematic obj = LazyUI.Get(); if (obj != null) { obj.DisableCinematic((Action)null, true); } } if (hudHidden) { hudHidden = false; GUIElements instance = GUIElements.Instance; if (instance != null) { instance.SetVisibilityState(true); } } if (musicChanged) { musicChanged = false; LazyAudio.StopAllPlaylistsImmediately(); LazyAudio.PlayPlaylist("gameplay"); } } } [HarmonyPatch(typeof(UICinematic), "EnableCinematic")] internal static class MirrorCinematicBarsOn { private static void Prefix(bool instant) { StandIn.HostEffect(15, "", instant ? 1f : 0f, 0f, 0f, 1); } } [HarmonyPatch(typeof(UICinematic), "DisableCinematic")] internal static class MirrorCinematicBarsOff { private static void Prefix(bool instant) { StandIn.HostEffect(15, "", instant ? 1f : 0f); } } [HarmonyPatch(typeof(UIBasicFade), "FadeIn", new Type[] { typeof(float), typeof(Action), typeof(FadeFlag), typeof(bool) })] internal static class MirrorFadeIn { private static void Prefix(UIBasicFade __instance, float fadeTime, FadeFlag fadeFlag) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (__instance is UIFade && (int)fadeFlag == 1) { StandIn.HostEffect(2, "", fadeTime); } } } [HarmonyPatch(typeof(UIBasicFade), "FadeOut", new Type[] { typeof(float), typeof(Action), typeof(FadeFlag) })] internal static class MirrorFadeOut { private static void Prefix(UIBasicFade __instance, float fadeTime, FadeFlag fadeFlag) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (__instance is UIFade && (int)fadeFlag == 1) { StandIn.HostEffect(2, "", fadeTime, 0f, 0f, 1); } } } [HarmonyPatch(typeof(UIBasicFade), "FadeInInstant")] internal static class MirrorFadeInInstant { private static void Prefix(UIBasicFade __instance, FadeFlag fadeFlag) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (__instance is UIFade && (int)fadeFlag == 1) { StandIn.HostEffect(2, "", 0f, 0f, 0f, 2); } } } [HarmonyPatch(typeof(UIBasicFade), "FadeOutInstant")] internal static class MirrorFadeOutInstant { private static void Prefix(UIBasicFade __instance, FadeFlag fadeFlag) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (__instance is UIFade && (int)fadeFlag == 1) { StandIn.HostEffect(2, "", 0f, 0f, 0f, 3); } } } [HarmonyPatch(typeof(Flow_SleepFade), "DoSleepFade")] internal static class MirrorSleepFade { private static readonly FieldRef> InstantField = AccessTools.FieldRefAccess>("instant"); private static void Prefix(Flow_SleepFade __instance) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 //IL_0032: Unknown result type (might be due to invalid IL or missing references) bool flag = false; try { flag = InstantField.Invoke(__instance)?.value ?? false; } catch { } bool flag2 = (int)__instance.fadeType == 0; StandIn.HostEffect(16, "", (float)__instance.animType, 0f, 0f, ((!flag2) ? 1 : 0) + (flag ? 2 : 0)); } } [HarmonyPatch(typeof(UIFadeWithText), "ScreenTextFade", new Type[] { typeof(string), typeof(float), typeof(float), typeof(float), typeof(float), typeof(Action), typeof(Action), typeof(TextStyle) })] internal static class MirrorTextScreen { private static void Prefix(string text, float screenFadeTime, float textFadeTime, float pauseTime, float textHoldTime) { StandIn.HostEffect(17, text ?? "", screenFadeTime, textFadeTime, pauseTime, (int)(textHoldTime * 1000f)); } } [HarmonyPatch(typeof(UIFadeWithText), "ScreenTextFade", new Type[] { typeof(List), typeof(float), typeof(float), typeof(float), typeof(float), typeof(Action), typeof(Action), typeof(TextStyle) })] internal static class MirrorTextScreenLines { private static void Prefix(List ids, float screenFadeTime, float textFadeTime, float pauseTime, float textHoldTime) { if (ids != null && ids.Count > 0) { StandIn.HostEffect(17, string.Join('\u001f'.ToString(), ids), screenFadeTime, textFadeTime, pauseTime, (int)(textHoldTime * 1000f)); } } } [HarmonyPatch(typeof(MainGame), "CompleteGame")] internal static class MirrorGameComplete { private static void Prefix(bool shouldGoToMenuOnReturn) { if (CoopSession.Role == CoopRole.Host) { StandIn.HostEffect(18, "", 0f, 0f, 0f, shouldGoToMenuOnReturn ? 1 : 0); } } } [HarmonyPatch(typeof(Flow_SetWGOPosition), "Teleport")] internal static class MirrorScriptedMovePuffs { private static readonly MethodInfo GetWgoDataMethod = AccessTools.Method(typeof(GKCustomFlowNodeWithWgoData), "GetWgoData", (Type[])null, (Type[])null); private static WgoData Target(Flow_SetWGOPosition node) { object? obj = GetWgoDataMethod?.Invoke(node, null); return (WgoData)((obj is WgoData) ? obj : null); } private static void Prefix(Flow_SetWGOPosition __instance) { //IL_0024: 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_003a: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !__instance.fxBefore) { return; } try { WgoData val = Target(__instance); if (val != null) { StandIn.HostEffect(9, "puff_npc", val.Position.x, val.Position.y, val.Position.z); } } catch (Exception ex) { CoopLog.Debug("Mirroring a puff failed: " + ex.Message); } } private static void Postfix(Flow_SetWGOPosition __instance) { //IL_0024: 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_003a: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !__instance.fxAfter) { return; } try { WgoData val = Target(__instance); if (val != null) { StandIn.HostEffect(9, "puff_npc", val.Position.x, val.Position.y, val.Position.z); } } catch (Exception ex) { CoopLog.Debug("Mirroring a puff failed: " + ex.Message); } } } [HarmonyPatch(typeof(MainCamera), "PlayAnimation")] internal static class MirrorCameraAnimation { private static void Prefix(string animationName) { StandIn.HostEffect(3, animationName); } } [HarmonyPatch(typeof(MainCamera), "SetPostProcessProfile")] internal static class MirrorPostProcess { private static void Prefix(string profileName) { StandIn.HostEffect(4, profileName); } } [HarmonyPatch(typeof(MainCamera), "ResetPostProcessProfile")] internal static class MirrorPostProcessReset { private static void Prefix() { StandIn.HostEffect(4, ""); } } [HarmonyPatch(typeof(EnvironmentEngine), "ApplyOverridePreset", new Type[] { typeof(string), typeof(float) })] internal static class MirrorLightOverride { private static void Prefix(string presetName, float intensity) { StandIn.HostEffect(5, presetName, intensity); } } [HarmonyPatch(typeof(EnvironmentEngine), "ApplyOverridePreset", new Type[] { typeof(LightEnvironmentPreset), typeof(float) })] internal static class MirrorLightOverrideReset { private static void Prefix(LightEnvironmentPreset preset) { if ((Object)(object)preset == (Object)null) { StandIn.HostEffect(5, ""); } } } [HarmonyPatch(typeof(CameraController), "ShakeCamera", new Type[] { typeof(CameraNoiseType), typeof(float), typeof(float), typeof(float), typeof(Action) })] internal static class MirrorCameraShake { private static void Prefix(CameraController __instance, CameraNoiseType type, float duration, float fadeDuration, float amplitude) { //IL_0001: 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_0018: Expected I4, but got Unknown if ((int)__instance.CameraType == 0) { StandIn.HostEffect(6, "", duration, fadeDuration, amplitude, (int)type); } } } [HarmonyPatch(typeof(CameraController), "ShakeCamera", new Type[] { typeof(float), typeof(float), typeof(float) })] internal static class MirrorSimpleCameraShake { private static void Prefix(CameraController __instance, float duration, float fadeDuration, float amplitude) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.CameraType == 0) { StandIn.HostEffect(6, "", duration, fadeDuration, amplitude, -1); } } } [HarmonyPatch(typeof(Flow_PlaySound), "PlaySound")] internal static class MirrorStorySound { private static readonly FieldRef> SoundIdField = AccessTools.FieldRefAccess>("soundId"); private static void Prefix(Flow_PlaySound __instance) { try { string text = SoundIdField.Invoke(__instance)?.value; if (!string.IsNullOrEmpty(text)) { StandIn.HostEffect(7, text, 0f, 0f, 0f, __instance.stop ? 1 : 0); } } catch (Exception ex) { CoopLog.Debug("Couldn't mirror a story sound: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_PlaylistControl), "Control")] internal static class MirrorStoryMusic { private static readonly FieldRef> PlaylistField = AccessTools.FieldRefAccess>("playlistId"); private static readonly FieldRef> TrackField = AccessTools.FieldRefAccess>("trackId"); private static readonly FieldRef> DurationField = AccessTools.FieldRefAccess>("duration"); private static void Prefix(Flow_PlaylistControl __instance) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected I4, but got Unknown try { string text = PlaylistField.Invoke(__instance)?.value ?? ""; string text2 = (((int)__instance.controlType != 4) ? "" : (TrackField.Invoke(__instance)?.value ?? "")); float a = DurationField.Invoke(__instance)?.value ?? 0f; StandIn.HostEffect(8, text + "|" + text2, a, 0f, 0f, (int)__instance.controlType); } catch (Exception ex) { CoopLog.Debug("Couldn't mirror story music: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_SpawnFX), "SpawnFX")] internal static class MirrorStoryFx { private static readonly FieldRef> NameField = AccessTools.FieldRefAccess>("fxName"); private static readonly FieldRef> PositionField = AccessTools.FieldRefAccess>("worldPos"); private static void Prefix(Flow_SpawnFX __instance) { //IL_003a: 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_003f: 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_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) try { string text = NameField.Invoke(__instance)?.value; Vector3 val = ((PositionField.Invoke(__instance) != null) ? PositionField.Invoke(__instance).value : Vector3.zero); if (!string.IsNullOrEmpty(text)) { StandIn.HostEffect(9, text, val.x, val.y, val.z); } } catch (Exception ex) { CoopLog.Debug("Couldn't mirror a story effect: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_DisableUI), "DoFade")] internal static class MirrorStoryHud { private static readonly FieldRef> EnableField = AccessTools.FieldRefAccess>("enable"); private static void Prefix(Flow_DisableUI __instance) { try { ValueInput val = EnableField.Invoke(__instance); StandIn.HostEffect(10, "", 0f, 0f, 0f, (val == null || val.value) ? 1 : 0); } catch (Exception ex) { CoopLog.Debug("Couldn't mirror the HUD state: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_ShowSimpleTextWithIconNotification), "ShowWindow")] internal static class MirrorStoryNotice { private static readonly FieldRef> LocaleField = AccessTools.FieldRefAccess>("locale"); private static readonly FieldRef> IconField = AccessTools.FieldRefAccess>("iconId"); private static void Prefix(Flow_ShowSimpleTextWithIconNotification __instance) { try { StandIn.HostEffect(12, LocaleField.Invoke(__instance)?.value + "|" + IconField.Invoke(__instance)?.value); } catch (Exception ex) { CoopLog.Debug("Couldn't mirror a story notice: " + ex.Message); } } } [HarmonyPatch(typeof(Flow_ShowNewBodyNotification), "ShowWindow")] internal static class MirrorNewBodyNotice { private static readonly FieldRef> BodyField = AccessTools.FieldRefAccess>("bodyId"); private static void Prefix(Flow_ShowNewBodyNotification __instance) { try { StandIn.HostEffect(12, BodyField.Invoke(__instance)?.value ?? "", 0f, 0f, 0f, 1); } catch (Exception ex) { CoopLog.Debug("Couldn't mirror a new body notice: " + ex.Message); } } } [HarmonyPatch(typeof(UITutorialArrow), "Attach", new Type[] { typeof(WgoData) })] internal static class MirrorTutorialArrow { private static void Postfix(WgoData wgo) { if (((wgo != null) ? wgo.UniqueId : null) != (SGuid)null) { StandIn.HostEffect(14, wgo.UniqueId.Id); } } } [HarmonyPatch(typeof(UITutorialArrow), "UnAttach")] internal static class MirrorTutorialArrowOff { private static void Postfix() { StandIn.HostEffect(14, ""); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class MirrorPathPoint { private static void Postfix(GDPointData __instance, bool value) { if (!string.IsNullOrEmpty((__instance != null) ? __instance.Id : null)) { StandIn.HostEffect(11, __instance.Id, 0f, 0f, 0f, value ? 1 : 0); } } } internal static class SteamLobby { internal struct HostedWorld { public ulong Lobby; public string Host; public string Version; public bool SameVersion; } private const string KeyProtocol = "gk2coop"; private const string KeyVersion = "gk2coop_version"; private const string KeyHost = "gk2coop_host"; private const string KeyName = "gk2coop_name"; private const int MaxMembers = 8; private const float ScanInterval = 3f; private static readonly FieldInfo SteamManagerInstance = AccessTools.Field(typeof(SteamManager), "s_instance"); private static readonly FieldRef SteamManagerReady = AccessTools.FieldRefAccess("m_bInitialized"); private static bool callbacksMade; private static float nextAvailableCheck; private static Callback joinRequested; private static Callback lobbyDataUpdated; private static readonly List requests = new List(); private static CSteamID lobby = CSteamID.Nil; private static bool hosting; private static string joiningLabel; private static List hosts = new List(); private static readonly Dictionary askedForData = new Dictionary(); private static float nextScan; private static bool commandLineChecked; public static ulong InviteLobby { get; private set; } public static string InviteFrom { get; private set; } public static string HostStatus { get; private set; } public static bool IsOpen { get { if (hosting) { return ((CSteamID)(ref lobby)).IsValid(); } return false; } } public static bool Joining => joiningLabel != null; public static bool Available { get { try { object? obj = SteamManagerInstance?.GetValue(null); SteamManager val = (SteamManager)((obj is SteamManager) ? obj : null); return (Object)(object)val != (Object)null && SteamManagerReady.Invoke(val); } catch { return false; } } } public static string LocalName { get { try { return Available ? SteamFriends.GetPersonaName() : null; } catch { return null; } } } public static string Describe(CSteamID id) { //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_000d: Unknown result type (might be due to invalid IL or missing references) try { string text = ((id == SteamUser.GetSteamID()) ? SteamFriends.GetPersonaName() : SteamFriends.GetFriendPersonaName(id)); return (string.IsNullOrEmpty(text) || text == "[unknown]") ? "a Steam user" : text; } catch { return "a Steam user"; } } public static bool MayJoin(CSteamID who) { //IL_0000: 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_002f: 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_003a: Unknown result type (might be due to invalid IL or missing references) if (SteamFriends.HasFriend(who, (EFriendFlags)4)) { return true; } if (!hosting || !((CSteamID)(ref lobby)).IsValid()) { return false; } int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(lobby); for (int i = 0; i < numLobbyMembers; i++) { if (SteamMatchmaking.GetLobbyMemberByIndex(lobby, i) == who) { return true; } } return false; } public static void Tick() { if (!callbacksMade) { if (Time.unscaledTime < nextAvailableCheck) { return; } nextAvailableCheck = Time.unscaledTime + 1f; if (!Available) { return; } MakeCallbacks(); } if (!commandLineChecked && CoopSession.IsInMainMenu) { commandLineChecked = true; CheckCommandLine(); } } private static void MakeCallbacks() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) callbacksMade = true; joinRequested = Callback.Create((DispatchDelegate)OnJoinRequested); lobbyDataUpdated = Callback.Create((DispatchDelegate)delegate { nextScan = 0f; }); SteamNetworkingUtils.InitRelayNetworkAccess(); CoopLog.Info("Steam is running (signed in as " + Describe(SteamUser.GetSteamID()) + "): hosting and joining through Steam are available."); } private static void CheckCommandLine() { string[] commandLineArgs = Environment.GetCommandLineArgs(); for (int i = 0; i < commandLineArgs.Length - 1; i++) { if (commandLineArgs[i] == "+connect_lobby" && ulong.TryParse(commandLineArgs[i + 1], out var result)) { Invited(result, "your friend"); break; } } } private static void OnJoinRequested(GameLobbyJoinRequested_t request) { //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_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) try { Invited(request.m_steamIDLobby.m_SteamID, Describe(request.m_steamIDFriend)); } catch (Exception e) { CoopLog.Error("Steam: handling a join request failed", e); } } private static void Invited(ulong lobbyId, string from) { CoopLog.Info("Steam: asked to join " + from + "'s world."); if (CoopSession.Role != CoopRole.Client || lobby.m_SteamID != lobbyId) { InviteLobby = lobbyId; InviteFrom = from; string why; if (CoopSession.Role == CoopRole.Host) { CoopRunner.Toast("To join " + from + ", stop hosting first (F8)."); } else if (CoopSession.Role == CoopRole.Client) { CoopRunner.Toast("To join " + from + ", disconnect from this world first (F8)."); } else if (!CoopSession.IsInMainMenu) { CoopRunner.Toast("To join " + from + ", go to the main menu and open the co-op window (F8)."); } else if (!CoopSession.CanJoin(out why)) { CoopSession.Status = why + " Then join " + from + "."; CoopRunner.ShowWindow(); } else { Join(lobbyId, from); } } } public static void Join(ulong lobbyId, string label) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (callbacksMade && CoopSession.Role == CoopRole.None && !Joining) { if (!CoopSession.CanJoin(out var why)) { CoopSession.Status = why; return; } Close(); joiningLabel = label; CoopSession.Status = "Finding " + label + "'s world on Steam..."; CoopLog.Info(CoopSession.Status); CallResult val = CallResult.Create((APIDispatchDelegate)OnLobbyEntered); val.Set(SteamMatchmaking.JoinLobby(new CSteamID(lobbyId)), (APIDispatchDelegate)null); requests.Add((IDisposable)val); } } private static void OnLobbyEntered(LobbyEnter_t result, bool ioFailure) { //IL_0002: 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_0096: 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_00a7: 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_00bc: 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_0042: 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_00db: 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_0139: Unknown result type (might be due to invalid IL or missing references) CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result.m_ulSteamIDLobby); bool flag = !ioFailure && result.m_EChatRoomEnterResponse == 1; string text = joiningLabel; joiningLabel = null; if (text == null) { if (flag) { SteamMatchmaking.LeaveLobby(val); } return; } if (!flag) { string text2 = (ioFailure ? "Steam didn't answer" : ((object)(EChatRoomEnterResponse)result.m_EChatRoomEnterResponse/*cast due to .constrained prefix*/).ToString()); CoopSession.Status = "Couldn't reach " + text + "'s world on Steam (" + text2 + "). Are they still hosting?"; CoopLog.Warn(CoopSession.Status); return; } lobby = val; hosting = false; if (InviteLobby == val.m_SteamID) { InviteLobby = 0uL; InviteFrom = null; } string lobbyData = SteamMatchmaking.GetLobbyData(val, "gk2coop"); ulong result2; if (lobbyData != 30.ToString()) { string lobbyData2 = SteamMatchmaking.GetLobbyData(val, "gk2coop_version"); CoopSession.Status = (string.IsNullOrEmpty(lobbyData) ? (text + " isn't hosting a GK2 Co-op world.") : (text + " has GK2 Co-op " + lobbyData2 + " and you have 0.27.0. You both need the same version.")); CoopLog.Warn(CoopSession.Status); Close(); } else if (!CoopSession.JoinSteam(ulong.TryParse(SteamMatchmaking.GetLobbyData(val, "gk2coop_host"), out result2) ? result2 : SteamMatchmaking.GetLobbyOwner(val).m_SteamID, text)) { Close(); } } public static void Open() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) Close(); if (!callbacksMade) { HostStatus = "Steam isn't running, so friends can't find your world."; return; } hosting = true; HostStatus = "Opening your Steam lobby..."; CallResult val = CallResult.Create((APIDispatchDelegate)OnLobbyCreated); val.Set(SteamMatchmaking.CreateLobby((ELobbyType)1, 8), (APIDispatchDelegate)null); requests.Add((IDisposable)val); } private unsafe static void OnLobbyCreated(LobbyCreated_t result, bool ioFailure) { //IL_0002: 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) //IL_0017: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_00a1: 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_00ac: 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) //IL_00de: Unknown result type (might be due to invalid IL or missing references) CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result.m_ulSteamIDLobby); bool flag = !ioFailure && (int)result.m_eResult == 1; if (!hosting || CoopSession.Role != CoopRole.Host) { if (flag) { SteamMatchmaking.LeaveLobby(val); } return; } if (!flag) { HostStatus = "Couldn't open a Steam lobby (" + (ioFailure ? "Steam didn't answer" : ((object)(*(EResult*)(&result.m_eResult))/*cast due to .constrained prefix*/).ToString()) + "), so friends can't find your world. Stop hosting and try again."; CoopLog.Warn(HostStatus); return; } lobby = val; SteamMatchmaking.SetLobbyData(val, "gk2coop", 30.ToString()); SteamMatchmaking.SetLobbyData(val, "gk2coop_version", "0.27.0"); SteamMatchmaking.SetLobbyData(val, "gk2coop_host", SteamUser.GetSteamID().m_SteamID.ToString()); SteamMatchmaking.SetLobbyData(val, "gk2coop_name", Plugin.PlayerName.Value ?? ""); SteamMatchmaking.SetLobbyJoinable(val, true); HostStatus = "Your Steam friends can join you now."; CoopLog.Info("Steam: lobby open (friends only)."); } public static void Close() { //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_0012: Unknown result type (might be due to invalid IL or missing references) joiningLabel = null; if (((CSteamID)(ref lobby)).IsValid()) { try { SteamMatchmaking.LeaveLobby(lobby); CoopLog.Info("Steam: left the lobby."); } catch (Exception ex) { CoopLog.Warn("Steam: leaving the lobby failed: " + ex.Message); } } lobby = CSteamID.Nil; hosting = false; HostStatus = null; } public static void InviteFriends() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { if (!SteamUtils.IsOverlayEnabled()) { CoopRunner.Toast("The Steam overlay is off, so Steam can't show its invite window. Friends can join from their co-op window (F8)."); } else { SteamFriends.ActivateGameOverlayInviteDialog(lobby); } } } public static List FriendsHosting() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 bool flag = Event.current == null || (int)Event.current.type == 8; if (callbacksMade && flag && Time.unscaledTime >= nextScan) { nextScan = Time.unscaledTime + 3f; try { Scan(); } catch (Exception ex) { CoopLog.Debug("Steam: listing friends' worlds failed: " + ex.Message); } } return hosts; } private static void Scan() { //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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_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_0078: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_014c: 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_00cc: 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_0115: 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_0133: Unknown result type (might be due to invalid IL or missing references) List list = new List(); HashSet hashSet = new HashSet(); AppId_t appID = SteamUtils.GetAppID(); string text = 30.ToString(); int friendCount = SteamFriends.GetFriendCount((EFriendFlags)4); FriendGameInfo_t val = default(FriendGameInfo_t); for (int i = 0; i < friendCount; i++) { CSteamID friendByIndex = SteamFriends.GetFriendByIndex(i, (EFriendFlags)4); if (!SteamFriends.GetFriendGamePlayed(friendByIndex, ref val) || ((CGameID)(ref val.m_gameID)).AppID() != appID || !((CSteamID)(ref val.m_steamIDLobby)).IsValid()) { continue; } CSteamID steamIDLobby = val.m_steamIDLobby; if (!hashSet.Add(steamIDLobby.m_SteamID)) { continue; } string lobbyData = SteamMatchmaking.GetLobbyData(steamIDLobby, "gk2coop"); if (string.IsNullOrEmpty(lobbyData)) { if (!askedForData.TryGetValue(steamIDLobby.m_SteamID, out var value) || Time.unscaledTime - value > 15f) { askedForData[steamIDLobby.m_SteamID] = Time.unscaledTime; SteamMatchmaking.RequestLobbyData(steamIDLobby); } continue; } string text2 = SteamFriends.GetFriendPersonaName(friendByIndex); if (ulong.TryParse(SteamMatchmaking.GetLobbyData(steamIDLobby, "gk2coop_host"), out var result) && result != friendByIndex.m_SteamID) { string text3 = Describe(new CSteamID(result)); text2 = ((text3 == "a Steam user") ? SteamMatchmaking.GetLobbyData(steamIDLobby, "gk2coop_name") : text3); } list.Add(new HostedWorld { Lobby = steamIDLobby.m_SteamID, Host = (string.IsNullOrEmpty(text2) ? "A friend" : text2), Version = SteamMatchmaking.GetLobbyData(steamIDLobby, "gk2coop_version"), SameVersion = (lobbyData == text) }); } hosts = list; } } internal class SteamTransport : NetworkTransport { private class Peer { public ulong Id; public HSteamNetConnection Conn; public CSteamID SteamId; public readonly Queue Pending = new Queue(); public MemoryStream Assembly; public long LastUnreliable = -1L; } private struct QueuedEvent { public NetworkEvent Type; public ulong Id; public ArraySegment Payload; public float Time; } private const byte Whole = 0; private const byte Part = 1; private const byte LastPart = 2; private const int MaxPart = 262144; private const int ReceiveBatch = 64; private const int VirtualPort = 0; private const int RefusedReason = 1001; private readonly Dictionary peers = new Dictionary(); private readonly Dictionary byConn = new Dictionary(); private readonly Dictionary accepted = new Dictionary(); private readonly Queue events = new Queue(); private readonly IntPtr[] received = new IntPtr[64]; private Callback statusChanged; private HSteamListenSocket listenSocket = HSteamListenSocket.Invalid; private HSteamNetPollGroup pollGroup = HSteamNetPollGroup.Invalid; private HSteamNetConnection serverConnection = HSteamNetConnection.Invalid; private bool isServer; private bool running; private ulong nextId = 1uL; private string lastSendError; public ulong HostSteamId; public override ulong ServerClientId => 0uL; public int Connections => peers.Count; public override void Initialize(NetworkManager networkManager = null) { } public override bool StartServer() { //IL_0016: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0062: Unknown result type (might be due to invalid IL or missing references) if (!Begin()) { return false; } SteamNetworkingConfigValue_t[] array = Options(); listenSocket = SteamNetworkingSockets.CreateListenSocketP2P(0, array.Length, array); if (listenSocket == HSteamListenSocket.Invalid) { CoopLog.Warn("Steam: couldn't open a listen socket."); ((NetworkTransport)this).Shutdown(); return false; } pollGroup = SteamNetworkingSockets.CreatePollGroup(); isServer = true; running = true; CoopLog.Info("Steam: listening for friends as " + SteamLobby.Describe(SteamUser.GetSteamID()) + "."); return true; } public override bool StartClient() { //IL_0021: 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_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_004d: 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_005e: Unknown result type (might be due to invalid IL or missing references) if (HostSteamId == 0L || !Begin()) { return false; } CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(HostSteamId); SteamNetworkingIdentity val2 = default(SteamNetworkingIdentity); ((SteamNetworkingIdentity)(ref val2)).SetSteamID(val); SteamNetworkingConfigValue_t[] array = Options(); serverConnection = SteamNetworkingSockets.ConnectP2P(ref val2, 0, array.Length, array); if (serverConnection == HSteamNetConnection.Invalid) { CoopLog.Warn("Steam: couldn't start a connection to " + SteamLobby.Describe(val) + "."); ((NetworkTransport)this).Shutdown(); return false; } isServer = false; running = true; CoopLog.Info("Steam: connecting to " + SteamLobby.Describe(val) + "..."); return true; } private bool Begin() { ((NetworkTransport)this).Shutdown(); if (!SteamLobby.Available) { CoopLog.Warn("Steam: not available (Steam isn't running, or the game wasn't started through it)."); return false; } statusChanged = Callback.Create((DispatchDelegate)OnStatusChanged); SteamNetworkingUtils.InitRelayNetworkAccess(); return true; } private static SteamNetworkingConfigValue_t[] Options() { //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_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_0035: 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_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) return (SteamNetworkingConfigValue_t[])(object)new SteamNetworkingConfigValue_t[4] { Int32((ESteamNetworkingConfigValue)24, 20000), Int32((ESteamNetworkingConfigValue)25, 60000), Int32((ESteamNetworkingConfigValue)9, 4194304), Int32((ESteamNetworkingConfigValue)11, 8388608) }; } private static SteamNetworkingConfigValue_t Int32(ESteamNetworkingConfigValue what, int value) { //IL_0002: 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_000b: 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_001c: 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_0030: Unknown result type (might be due to invalid IL or missing references) return new SteamNetworkingConfigValue_t { m_eValue = what, m_eDataType = (ESteamNetworkingConfigDataType)1, m_val = new OptionValue { m_int32 = value } }; } private void OnStatusChanged(SteamNetConnectionStatusChangedCallback_t data) { //IL_0009: 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_000f: 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) try { if (running) { OnStatus(data.m_hConn, data.m_info); } } catch (Exception e) { CoopLog.Error("Steam connection update failed", e); } } private void OnStatus(HSteamNetConnection conn, SteamNetConnectionInfo_t info) { //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_000d: 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_0013: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected I4, but got Unknown //IL_0068: 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_0072: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_0052: 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_0060: Unknown result type (might be due to invalid IL or missing references) CSteamID steamID = ((SteamNetworkingIdentity)(ref info.m_identityRemote)).GetSteamID(); ESteamNetworkingConnectionState eState = info.m_eState; switch (eState - 1) { case 0: if (isServer && listenSocket != HSteamListenSocket.Invalid && info.m_hListenSocket == listenSocket) { Accept(conn, steamID); } break; case 2: PeerOf(conn); break; case 3: case 4: Closed(conn, steamID, info); break; case 1: break; } } private void Accept(HSteamNetConnection conn, CSteamID remote) { //IL_0000: 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_0037: 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_003d: 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_0040: Invalid comparison between Unknown and I4 //IL_0024: 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_0065: 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_007c: 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_0058: Unknown result type (might be due to invalid IL or missing references) string text = SteamLobby.Describe(remote); if (!SteamLobby.MayJoin(remote)) { CoopLog.Warn("Steam: turned away " + text + " (not a Steam friend and not in your lobby)."); SteamNetworkingSockets.CloseConnection(conn, 1001, "Only the host's Steam friends can join.", false); return; } EResult val = SteamNetworkingSockets.AcceptConnection(conn); if ((int)val != 1) { CoopLog.Warn($"Steam: couldn't accept {text} ({val})."); SteamNetworkingSockets.CloseConnection(conn, 0, (string)null, false); } else { SteamNetworkingSockets.SetConnectionPollGroup(conn, pollGroup); accepted[conn.m_HSteamNetConnection] = remote; CoopLog.Info("Steam: " + text + " is connecting."); } } private Peer PeerOf(HSteamNetConnection conn) { //IL_0006: 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_005f: 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_003c: 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_008f: 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_0096: 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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) if (byConn.TryGetValue(conn.m_HSteamNetConnection, out var value)) { return value; } CSteamID value2 = default(CSteamID); ulong num; if (isServer) { if (!accepted.TryGetValue(conn.m_HSteamNetConnection, out value2)) { return null; } accepted.Remove(conn.m_HSteamNetConnection); num = nextId++; } else { if (conn != serverConnection) { return null; } ((CSteamID)(ref value2))..ctor(HostSteamId); num = ((NetworkTransport)this).ServerClientId; } value = new Peer { Id = num, Conn = conn, SteamId = value2 }; peers[num] = value; byConn[conn.m_HSteamNetConnection] = value; events.Enqueue(new QueuedEvent { Type = (NetworkEvent)1, Id = num, Time = Time.realtimeSinceStartup }); CoopLog.Info(isServer ? $"Steam: {SteamLobby.Describe(value2)} connected (transport id {num})." : ("Steam: connected to the host, " + SteamLobby.Describe(value2) + ".")); return value; } private void Closed(HSteamNetConnection conn, CSteamID remote, SteamNetConnectionInfo_t info) { //IL_0000: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_002a: 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_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0151: 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_00f1: 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_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) uint hSteamNetConnection = conn.m_HSteamNetConnection; accepted.Remove(hSteamNetConnection); bool flag = (int)info.m_eState == 4; string text = $"{((SteamNetConnectionInfo_t)(ref info)).m_szEndDebug} (code {info.m_eEndReason})"; CoopLog.Info("Steam: connection with " + SteamLobby.Describe(remote) + " " + (flag ? "closed by them" : "lost") + ": " + text); if (byConn.TryGetValue(hSteamNetConnection, out var value)) { Forget(value); if (!isServer) { ((NetworkTransport)this).SetDisconnectEvent((DisconnectEvents)(flag ? 4 : 2), text); } events.Enqueue(new QueuedEvent { Type = (NetworkEvent)2, Id = value.Id, Time = Time.realtimeSinceStartup }); } else if (!isServer && conn == serverConnection) { ((NetworkTransport)this).SetDisconnectEvent((DisconnectEvents)3, "Couldn't reach the host over Steam: " + text); events.Enqueue(new QueuedEvent { Type = (NetworkEvent)2, Id = ((NetworkTransport)this).ServerClientId, Time = Time.realtimeSinceStartup }); } SteamNetworkingSockets.CloseConnection(conn, 0, (string)null, false); if (conn == serverConnection) { serverConnection = HSteamNetConnection.Invalid; } } private void Forget(Peer peer) { peers.Remove(peer.Id); byConn.Remove(peer.Conn.m_HSteamNetConnection); peer.Pending.Clear(); peer.Assembly = null; } public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment payload, out float receiveTime) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (events.Count == 0) { clientId = 0uL; payload = default(ArraySegment); receiveTime = Time.realtimeSinceStartup; return (NetworkEvent)4; } QueuedEvent queuedEvent = events.Dequeue(); clientId = queuedEvent.Id; payload = queuedEvent.Payload; receiveTime = queuedEvent.Time; return queuedEvent.Type; } protected override void OnEarlyUpdate() { if (running) { FlushAll(); ReceiveAll(); } } protected override void OnPostLateUpdate() { if (running) { FlushAll(); } } private void ReceiveAll() { //IL_0039: 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_0010: 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_004c: 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_006d: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 32; i++) { int num; if (isServer) { if (pollGroup == HSteamNetPollGroup.Invalid) { break; } num = SteamNetworkingSockets.ReceiveMessagesOnPollGroup(pollGroup, received, 64); } else { if (serverConnection == HSteamNetConnection.Invalid) { break; } num = SteamNetworkingSockets.ReceiveMessagesOnConnection(serverConnection, received, 64); } for (int j = 0; j < num; j++) { try { Receive(SteamNetworkingMessage_t.FromIntPtr(received[j])); } catch (Exception e) { CoopLog.Error("Steam: reading a message failed", e); } finally { SteamNetworkingMessage_t.Release(received[j]); } } if (num < 64) { break; } } } private void Receive(SteamNetworkingMessage_t message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_001a: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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) Peer peer = PeerOf(message.m_conn); if (peer == null || message.m_cbSize < 1) { return; } byte[] array = new byte[message.m_cbSize]; Marshal.Copy(message.m_pData, array, 0, message.m_cbSize); if ((message.m_nFlags & 8) == 0) { if (message.m_nMessageNumber <= peer.LastUnreliable) { return; } peer.LastUnreliable = message.m_nMessageNumber; } switch (array[0]) { case 0: Deliver(peer, new ArraySegment(array, 1, array.Length - 1)); break; case 1: (peer.Assembly ?? (peer.Assembly = new MemoryStream())).Write(array, 1, array.Length - 1); break; case 2: { if (peer.Assembly == null) { CoopLog.Warn("Steam: got the end of a split message without its start; dropped it."); break; } peer.Assembly.Write(array, 1, array.Length - 1); byte[] array2 = peer.Assembly.ToArray(); peer.Assembly = null; Deliver(peer, new ArraySegment(array2)); break; } } } private void Deliver(Peer peer, ArraySegment payload) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) events.Enqueue(new QueuedEvent { Type = (NetworkEvent)0, Id = peer.Id, Payload = payload, Time = Time.realtimeSinceStartup }); } public override void Send(ulong clientId, ArraySegment payload, NetworkDelivery networkDelivery) { //IL_0019: 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_001e: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!running || !peers.TryGetValue(clientId, out var value)) { return; } if ((int)networkDelivery == 0 || (int)networkDelivery == 1) { if (payload.Count < 262144) { SendNow(value, Frame(0, payload, 0, payload.Count), 1); } return; } Flush(value); int num = 0; do { int num2 = Math.Min(262144, payload.Count - num); bool flag = num + num2 >= payload.Count; byte[] array = Frame((byte)((!(num == 0 && flag)) ? ((!flag) ? 1 : 2) : 0), payload, num, num2); if (value.Pending.Count > 0 || !SendReliable(value, array)) { value.Pending.Enqueue(array); } num += num2; } while (num < payload.Count); } private static byte[] Frame(byte kind, ArraySegment payload, int offset, int length) { byte[] array = new byte[length + 1]; array[0] = kind; if (length > 0) { Buffer.BlockCopy(payload.Array, payload.Offset + offset, array, 1, length); } return array; } private bool SendReliable(Peer peer, byte[] framed) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 return (int)SendNow(peer, framed, 9) != 25; } private EResult SendNow(Peer peer, byte[] framed, int flags) { //IL_0009: 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_0020: 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_002d: Invalid comparison between Unknown and I4 //IL_006b: 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_0032: Invalid comparison between Unknown and I4 //IL_003a: 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) GCHandle gCHandle = GCHandle.Alloc(framed, GCHandleType.Pinned); EResult val; try { long num = default(long); val = SteamNetworkingSockets.SendMessageToConnection(peer.Conn, gCHandle.AddrOfPinnedObject(), (uint)framed.Length, flags, ref num); } finally { gCHandle.Free(); } if ((int)val != 1 && (int)val != 25) { string text = $"Steam: sending to {SteamLobby.Describe(peer.SteamId)} failed ({val})."; if (text != lastSendError) { lastSendError = text; CoopLog.Warn(text); } } return val; } private void Flush(Peer peer) { while (peer.Pending.Count > 0 && SendReliable(peer, peer.Pending.Peek())) { peer.Pending.Dequeue(); } } private void FlushAll() { foreach (Peer value in peers.Values) { if (value.Pending.Count > 0) { Flush(value); } } } public override void DisconnectRemoteClient(ulong clientId) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (peers.TryGetValue(clientId, out var value)) { Flush(value); Forget(value); SteamNetworkingSockets.CloseConnection(value.Conn, 0, "Disconnected by the host.", true); } } public override void DisconnectLocalClient() { //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_0037: 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_004f: Unknown result type (might be due to invalid IL or missing references) if (!(serverConnection == HSteamNetConnection.Invalid)) { if (peers.TryGetValue(((NetworkTransport)this).ServerClientId, out var value)) { Flush(value); Forget(value); } SteamNetworkingSockets.CloseConnection(serverConnection, 0, "Left.", true); serverConnection = HSteamNetConnection.Invalid; } } public override ulong GetCurrentRtt(ulong clientId) { //IL_0016: 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_002e: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (!peers.TryGetValue(clientId, out var value)) { return 0uL; } try { SteamNetConnectionRealTimeStatus_t val = new SteamNetConnectionRealTimeStatus_t { reserved = new uint[16] }; SteamNetConnectionRealTimeLaneStatus_t val2 = new SteamNetConnectionRealTimeLaneStatus_t { reserved = new uint[10] }; return (ulong)(((int)SteamNetworkingSockets.GetConnectionRealTimeStatus(value.Conn, ref val, 0, ref val2) == 1) ? Math.Max(0, val.m_nPing) : 0); } catch { return 0uL; } } public override void Shutdown() { //IL_0168: 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) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: 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_0032: 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_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_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) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: 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_0108: Unknown result type (might be due to invalid IL or missing references) if (!running && statusChanged == null) { return; } running = false; try { foreach (Peer value in peers.Values) { SteamNetworkingSockets.CloseConnection(value.Conn, 0, "Shutting down.", true); } foreach (uint key in accepted.Keys) { SteamNetworkingSockets.CloseConnection(new HSteamNetConnection(key), 0, (string)null, false); } if (serverConnection != HSteamNetConnection.Invalid && !byConn.ContainsKey(serverConnection.m_HSteamNetConnection)) { SteamNetworkingSockets.CloseConnection(serverConnection, 0, (string)null, false); } if (listenSocket != HSteamListenSocket.Invalid) { SteamNetworkingSockets.CloseListenSocket(listenSocket); } if (pollGroup != HSteamNetPollGroup.Invalid) { SteamNetworkingSockets.DestroyPollGroup(pollGroup); } } catch (Exception e) { CoopLog.Error("Steam: closing connections failed", e); } statusChanged?.Dispose(); statusChanged = null; peers.Clear(); byConn.Clear(); accepted.Clear(); events.Clear(); listenSocket = HSteamListenSocket.Invalid; pollGroup = HSteamNetPollGroup.Invalid; serverConnection = HSteamNetConnection.Invalid; isServer = false; nextId = 1uL; lastSendError = null; } } internal static class StorySync { private delegate void WriteBody(FastBufferWriter writer); private struct StoryLine { public string Speaker; public string Text; } private const byte OpStoryState = 1; private const byte OpCinematic = 2; private const byte OpCinematicEnd = 3; private const byte OpPhrase = 4; private const byte OpNotice = 5; private const byte OpAnswers = 6; private const byte RequestInteract = 1; private const float StoryDebounce = 0.35f; private const float LinesVisibleAfterScene = 8f; private static readonly FieldInfo BubbleInstanceField = AccessTools.Field(typeof(Bubble), "instance"); private static readonly FieldRef> BubblePresets = AccessTools.FieldRefAccess>("speechBubblePresets"); private static readonly List lines = new List(); private static bool hostStoryActive; private static bool pendingStoryActive; private static float pendingSince; private static string currentCinematicId; private static bool hostInStory; private static string hostStoryKind = ""; private static bool watching; private static float nextLockCheck; private static Transform savedCameraTarget; private static bool cameraOnHost; private static float lastLineTime; public static bool ReplayingCinematic; private static float cinematicSince = -1f; private const byte OpAnswerChosen = 8; private const byte OpLineEnd = 9; private const byte OpAnswerHover = 10; private const byte RequestAnswer = 3; private const byte RequestHover = 4; private static readonly FieldInfo MultiAnswersField = AccessTools.Field(typeof(UIMultiAnswer), "multiAnswers"); private static readonly FieldInfo VisualDataField = AccessTools.Field(typeof(UIMultiAnswer), "visualData"); private static ulong talkOwner; private static List answerIds = new List(); private static List answerTexts = new List(); private static float answerShownAt; private static string picking; private static readonly MethodInfo ChangeInteractableMethod = AccessTools.Method(typeof(UIMultiAnswer), "ChangeInteractable", (Type[])null, (Type[])null); private static readonly MethodInfo StartDisappearMethod = AccessTools.Method(typeof(UIMultiAnswer), "StartDisappearAnimation", (Type[])null, (Type[])null); private static UIMultiAnswer realAnswers; private static bool realAnswersMine; private static bool realAnswersLocked; private static float realAnswersShownAt; private static string realAnswersChooser; private static readonly List> dismissed = new List>(); private static readonly FieldRef> OptionsField = AccessTools.FieldRefAccess>("answerOptions"); private static readonly FieldRef OptionDataField = AccessTools.FieldRefAccess("visualData"); private static readonly FieldRef OptionMenuField = AccessTools.FieldRefAccess("multiAnswer"); private static bool applyingHover; private static string lastHoverSent; private static UIMultiAnswer closingAnswers; private static float closeAnswersAt; private static float lastLockedNotice; private static bool soloTalk; private static float soloTalkSince; private const byte RequestCancelGather = 2; private const byte OpGather = 7; private const float GatherTimeout = 150f; private static Wgo gatherWgo; private static ulong gatherRequester; private static float gatherStarted; private static float nextGatherUpdate; private static bool gatherRunning; private static string gatherTitle; private static List gatherLines = new List(); private static readonly List mirroredBubbles = new List(); private static readonly FieldInfo BubbleAnimationField = AccessTools.Field(typeof(UIDialogBubble), "animationComponent"); private static readonly FieldInfo BubbleVoiceOverField = AccessTools.Field(typeof(UIDialogBubble), "hasVoiceOver"); private static readonly MethodInfo ForceHideMethod = AccessTools.Method(typeof(UISpeechBubble), "ForceHide", (Type[])null, (Type[])null); private static readonly MethodInfo VoiceKeyGetter = AccessTools.PropertyGetter(typeof(UISpeechBubble), "VoiceOverLocalKey"); private static readonly FieldInfo BubblePhraseField = AccessTools.Field(typeof(UIDialogBubble), "phraseData"); private static readonly MethodInfo DrawNpcStuffMethod = AccessTools.Method(typeof(UIDialogBubble), "DrawNPCRelatedStuff", (Type[])null, (Type[])null); private static bool endingWatch; private static float storyStartedAt; private static int storyLines; public static bool CinematicPlaying { get { if (cinematicSince > 0f) { return Time.unscaledTime - cinematicSince < 240f; } return false; } } public static bool Watching => watching; public static bool GatherActive { get { if (CoopSession.Role != CoopRole.Host) { return gatherTitle != null; } return (Object)(object)gatherWgo != (Object)null; } } private static bool OnlyHostNeeded => StandIn.Enabled; public static bool ClientMaySkip { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && Plugin.HostRunsWorld.Value) { return SermonSync.Active; } return true; } } public static bool EndingWatch => endingWatch; public static bool HostStoryActive => hostStoryActive; public static bool StoryIsSubstantial { get { if (hostInStory) { if (!(hostStoryKind == "cinematic") && storyLines <= 0) { return Time.unscaledTime - storyStartedAt > 4f; } return true; } return false; } } public static bool WantsCursor { get { if (CoopSession.Role == CoopRole.Client) { return answerIds.Count > 0; } return false; } } public static string Describe() { if (CoopSession.Role != CoopRole.Host) { return $"host story={hostInStory}({hostStoryKind}) watching={watching} camOnHost={cameraOnHost}"; } return $"host story={hostStoryActive} cinematic='{currentCinematicId}'"; } public static void Reset() { StandIn.ForceStop(); EndWatch("session ended"); endingWatch = false; hostStoryActive = false; pendingStoryActive = false; currentCinematicId = null; hostInStory = false; lines.Clear(); gatherWgo = null; gatherTitle = null; gatherLines = new List(); talkOwner = 0uL; mirroredBubbles.Clear(); picking = null; ClearAnswers(); } public static void Tick() { if (CoopSession.Role == CoopRole.Host) { TickHost(); } else if (CoopSession.Role == CoopRole.Client) { TickClient(); } } private static void TickHost() { PlayerController playerController = MainGame.PlayerController; if (!CoopSession.IsInGame || (Object)(object)playerController == (Object)null) { return; } TickGather(); bool flag = HostSceneNow(); if (flag != pendingStoryActive) { pendingStoryActive = flag; pendingSince = Time.unscaledTime; } if (pendingStoryActive != hostStoryActive && Time.unscaledTime - pendingSince >= 0.35f) { hostStoryActive = pendingStoryActive; if (!hostStoryActive) { SetTalkOwner(0uL); } string text = ((!playerController.IsControlEnabledByType((TakenControlType)11)) ? "cinematic" : "scene"); CoopLog.Info("Host story " + (hostStoryActive ? "started" : "ended") + " (" + text + ")."); SendStoryState(hostStoryActive, text, null); } } public static bool HostSceneNow() { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController == (Object)null) { return false; } EnergySystem val = MainGame.PlayerData?.energySystem; bool flag = SleepSync.InSharedSleep || (val != null && (val.IsSleeping || val.IsInTransitionBetweenSleep)); bool hasAnyModalWindowOpened = LazyWindowsStackController.HasAnyModalWindowOpened; bool flag2 = !playerController.IsControlEnabledByType((TakenControlType)11); if (soloTalk) { bool flag3 = !playerController.IsControlEnabledByType((TakenControlType)3) || flag2 || hasAnyModalWindowOpened; if (!flag2 && (flag3 || !(Time.unscaledTime - soloTalkSince > 1.5f)) && !(Time.unscaledTime - soloTalkSince > 300f)) { return !flag && flag2; } soloTalk = false; } if (!flag) { if (playerController.IsControlEnabledByType((TakenControlType)11)) { if (!playerController.IsControlEnabledByType((TakenControlType)3)) { if (hasAnyModalWindowOpened) { return hostStoryActive; } return true; } return false; } return true; } return false; } private static void SendStoryState(bool active, string kind, ulong? onlyTo) { Send(onlyTo, delegate(FastBufferWriter w) { //IL_0008: 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_0025: 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) byte b = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (active ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, kind); }); } public static void OnClientReady(ulong clientId) { if (hostStoryActive) { SendStoryState(active: true, (currentCinematicId != null) ? "cinematic" : "scene", clientId); } if (currentCinematicId != null) { SendCinematic(currentCinematicId, clientId); } } public static void HostCinematicStarted(string id) { if (!ReplayingCinematic && CoopSession.Role == CoopRole.Host) { currentCinematicId = id; CoopLog.Info("Host cinematic '" + id + "' started."); SendCinematic(id, null); } } private static void SendCinematic(string id, ulong? onlyTo) { Send(onlyTo, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 2; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, id); }); } public static void HostCinematicEnded() { if (CoopSession.Role == CoopRole.Host && currentCinematicId != null) { CoopLog.Info("Host cinematic '" + currentCinematicId + "' ended."); currentCinematicId = null; Send(null, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 3; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); }); } } public static void HostPhrase(PhraseData data) { //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 (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || !Plugin.MirrorSpeech.Value || string.IsNullOrEmpty(data.text)) { return; } Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_003c: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected I4, but got Unknown //IL_00b4: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected I4, but got Unknown //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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_0115: Unknown result type (might be due to invalid IL or missing references) byte b = 4; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, data.text); b = (data.isPlayer ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); WgoData npcWgoData = data.npcWgoData; object obj; if (npcWgoData == null) { obj = null; } else { SGuid uniqueId = npcWgoData.UniqueId; obj = ((uniqueId != null) ? uniqueId.Id : null); } if (obj == null) { obj = ""; } CoopMessages.WriteString(ref w, (string)obj); CoopMessages.WriteString(ref w, ((ObjectLinkedToDefinition)(object)data.npcWgoData)?.id ?? ""); int num = (int)data.speechType; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); num = (int)data.cornerPosition; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref data.fixedShowTimeValue, default(ForPrimitives)); b = (data.isOverBlackout ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); }); CoopLog.Debug("Mirrored line '" + data.text + "' (" + (data.isPlayer ? "host" : ((ObjectLinkedToDefinition)(object)data.npcWgoData)?.id) + ")."); } private static void SetTalkOwner(ulong owner) { if (talkOwner != owner) { CoopLog.Debug("Talk owner is now " + ((owner == 0L) ? "the host" : (PlayerSync.Get(owner)?.Label ?? owner.ToString())) + "."); } talkOwner = owner; } private static void ClearAnswers() { answerIds = new List(); answerTexts = new List(); } public static void HostAnswers(List answers, object[] args) { //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_0136: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || answers == null || (soloTalk && !hostStoryActive)) { return; } lastHoverSent = null; KnowledgeSystem knowledge = MainGame.Instance.GameSave.knowledgeSystem; List list = answers.Where((AnswerVisualData a) => a != null && !string.IsNullOrEmpty(a.id) && (!a.hiddenByDefault || knowledge.unlockedPhrases.Contains(a.id)) && !knowledge.blackListPhrases.Contains(a.id)).ToList(); List ids = list.Select((AnswerVisualData a) => a.id).ToList(); bool flag = talkOwner != 0L && CoopSession.ReadyClients.Contains(talkOwner); string chooser = ((!flag) ? (Plugin.PlayerName.Value ?? "Host") : (PlayerSync.Get(talkOwner)?.Label ?? "Your partner")); Transform val = (Transform)((args != null && args.Length > 1) ? /*isinst with value type is only supported in some contexts*/: null); WgoData participant = (WgoData)((args != null && args.Length > 2) ? /*isinst with value type is only supported in some contexts*/: null); int corner = ((args != null && args.Length > 5 && args[5] is ForceCornerPosition val2) ? ((int)val2) : 0); bool flag2 = default(bool); int num; if (args != null && args.Length > 6) { object obj = args[6]; if (obj is bool) { flag2 = (bool)obj; num = 1; } else { num = 0; } } else { num = 0; } bool isMain = (byte)((uint)num & (flag2 ? 1u : 0u)) != 0; bool flag3 = default(bool); int num2; if (args != null && args.Length > 7) { object obj = args[7]; if (obj is bool) { flag3 = (bool)obj; num2 = 1; } else { num2 = 0; } } else { num2 = 0; } bool overBlackout = (byte)((uint)num2 & (flag3 ? 1u : 0u)) != 0; PlayerController playerController = MainGame.PlayerController; bool onPlayer = (Object)(object)val == (Object)null || ((Object)(object)playerController != (Object)null && val.IsChildOf(((Component)playerController).transform)); byte[] full = null; try { full = SerializationUtility.SerializeValue>(list, (DataFormat)0, (SerializationContext)null); } catch (Exception ex) { CoopLog.Debug("Answer details didn't serialize (" + ex.Message + "); sending the ids only."); } foreach (ulong item in CoopSession.ReadyClients.ToList()) { byte mine = (byte)((flag && item == talkOwner) ? 1 : 0); Send(item, delegate(FastBufferWriter w) { //IL_0008: 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_002b: 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_007f: 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) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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_014b: Unknown result type (might be due to invalid IL or missing references) byte b = 6; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); int count = ids.Count; ((FastBufferWriter)(ref w)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item2 in ids) { CoopMessages.WriteString(ref w, item2); } ((FastBufferWriter)(ref w)).WriteValueSafe(ref mine, default(ForPrimitives)); CoopMessages.WriteString(ref w, chooser); WgoData obj2 = participant; object obj3; if (obj2 == null) { obj3 = null; } else { SGuid uniqueId = obj2.UniqueId; obj3 = ((uniqueId != null) ? uniqueId.Id : null); } if (obj3 == null) { obj3 = ""; } CoopMessages.WriteString(ref w, (string)obj3); b = (onPlayer ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref corner, default(ForPrimitives)); b = (isMain ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = (overBlackout ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref w, full ?? new byte[0]); }); } if (flag) { CoopRunner.Toast(chooser + " is picking the answer (you can still pick if needed).", 6f); } } public static void HostAnswerChosen(string answerId) { if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients) { string who = picking ?? Plugin.PlayerName.Value ?? "Host"; Send(null, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 8; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, answerId ?? ""); CoopMessages.WriteString(ref w, who); }); } } private static void ChooseAnswerFor(ulong sender, string answerId) { string text = PlayerSync.Get(sender)?.Label ?? $"Player {sender}"; if (sender != talkOwner) { CoopLog.Info(text + " tried to pick '" + answerId + "', but the talk isn't theirs; ignored."); return; } UIMultiAnswer val = null; if (MultiAnswersField?.GetValue(null) is IList list) { int num = list.Count - 1; while (num >= 0 && (Object)(object)val == (Object)null) { object? obj = list[num]; UIMultiAnswer val2 = (UIMultiAnswer)((obj is UIMultiAnswer) ? obj : null); if (val2 != null && VisualDataField?.GetValue(val2) is List source && source.Any((AnswerVisualData v) => v != null && v.id == answerId)) { val = val2; } num--; } } if ((Object)(object)val == (Object)null) { CoopLog.Info(text + " picked '" + answerId + "', but no open choice has it."); SendNotice(sender, "That choice isn't open any more."); return; } CoopLog.Info(text + " picked '" + answerId + "'."); picking = text; try { val.OnAnswerSelect(answerId); } finally { picking = null; } } private static bool ShowRealAnswers(List ids, byte[] full, bool mine, string chooser, string participantUid, bool onPlayer, int corner, bool isMain, bool overBlackout) { try { List list = null; if (full != null && full.Length != 0) { try { list = SerializationUtility.DeserializeValue>(full, (DataFormat)0, (DeserializationContext)null); } catch (Exception ex) { CoopLog.Debug("Answer details didn't load (" + ex.Message + "); using the ids."); } } if (list == null || list.Count != ids.Count) { list = ((IEnumerable)ids).Select((Func)((string id) => new AnswerVisualData { id = id })).ToList(); } foreach (AnswerVisualData item in list) { item.hiddenByDefault = false; } object obj; if (!string.IsNullOrEmpty(participantUid)) { WorldData worldData = MainGame.WorldData; obj = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(participantUid)) : null); } else { obj = null; } WgoData val = (WgoData)obj; PlayerController playerController = MainGame.PlayerController; object obj2; if (!StandIn.ActsAsHost) { RemotePlayer host = PlayerSync.Host; if (host == null) { obj2 = null; } else { PlayerController controller = host.Controller; obj2 = ((controller != null) ? controller.BubblePoint : null); } if (obj2 == null) { obj2 = playerController.BubblePoint; } } else { obj2 = playerController.BubblePoint; } Transform val2 = (Transform)obj2; if (!onPlayer && val != null) { Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(val.UniqueId); if ((Object)(object)wgoViewGlobal != (Object)null) { val2 = ((Component)wgoViewGlobal).transform; } } CloseRealAnswers(); UIMultiAnswer.ShowAnswers(list, val2, val, (Action)delegate(string id) { OnRealAnswer(id); }, (Action)delegate { realAnswers = null; }, (ForceCornerPosition)corner, isMain, overBlackout); object? obj3 = (MultiAnswersField?.GetValue(null) as IList)?.Cast().LastOrDefault(); realAnswers = (UIMultiAnswer)((obj3 is UIMultiAnswer) ? obj3 : null); realAnswersMine = mine; realAnswersLocked = false; lastHoverSent = null; realAnswersShownAt = Time.unscaledTime; realAnswersChooser = chooser; CoopLog.Info("Showing the answer menu (" + (mine ? "we pick" : (chooser + " picks")) + "): " + string.Join(" / ", ids) + "."); return (Object)(object)realAnswers != (Object)null; } catch (Exception e) { CoopLog.Error("Showing the answer menu failed; using the story panel", e); return false; } } private static void OnRealAnswer(string id) { realAnswers = null; if (!realAnswersMine) { CoopRunner.Toast(realAnswersChooser + " picks this answer."); } else { SendAnswerId(id); } } private static void TickRealAnswers() { if ((Object)(object)realAnswers == (Object)null || realAnswersMine || realAnswersLocked || Time.unscaledTime - realAnswersShownAt < 0.4f) { return; } realAnswersLocked = true; try { ChangeInteractableMethod?.Invoke(realAnswers, new object[1] { false }); } catch (Exception ex) { CoopLog.Debug("Couldn't lock the answer menu: " + ex.Message); } } private static void CloseRealAnswers() { if (!((Object)(object)realAnswers == (Object)null)) { UIMultiAnswer open = realAnswers; realAnswers = null; Dismiss(open); } } private static void Dismiss(UIMultiAnswer open) { try { if ((Object)(object)open != (Object)null && (Object)(object)((Component)open).gameObject != (Object)null) { StartDisappearMethod?.Invoke(open, null); dismissed.Add(new KeyValuePair(open, Time.unscaledTime + 1f)); } } catch (Exception ex) { CoopLog.Debug("Couldn't close the answer menu: " + ex.Message); } } private static void TickDismissed() { for (int num = dismissed.Count - 1; num >= 0; num--) { KeyValuePair keyValuePair = dismissed[num]; if ((Object)(object)keyValuePair.Key == (Object)null) { dismissed.RemoveAt(num); } else if (!(Time.unscaledTime < keyValuePair.Value)) { dismissed.RemoveAt(num); try { (MultiAnswersField?.GetValue(null) as IList)?.Remove(keyValuePair.Key); Object.Destroy((Object)(object)((Component)keyValuePair.Key).gameObject); CoopLog.Debug("Removed an answer menu that hadn't closed."); } catch (Exception ex) { CoopLog.Debug("Couldn't remove a stuck answer menu: " + ex.Message); } } } } public static bool AllowLocalHover(UIMultiAnswerOption option) { if (applyingHover || (Object)(object)option == (Object)null) { return true; } if (CoopSession.Role == CoopRole.Client && (Object)(object)realAnswers != (Object)null && !realAnswersMine) { return !((Object)(object)OptionMenuField.Invoke(option) == (Object)(object)realAnswers); } return true; } public unsafe static void OnLocalHover(UIMultiAnswerOption option, bool over) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if (applyingHover || (Object)(object)option == (Object)null) { return; } string text = ((!over) ? "" : OptionDataField.Invoke(option)?.id); if (text == null || text == lastHoverSent) { return; } if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && talkOwner == 0L) { lastHoverSent = text; SendHover(null, text); } else if (CoopSession.Role == CoopRole.Client && (Object)(object)realAnswers != (Object)null && realAnswersMine && (Object)(object)OptionMenuField.Invoke(option) == (Object)(object)realAnswers) { lastHoverSent = text; FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text); CoopMessages.SendToHost("GK2Coop.Request", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } } private unsafe static void SendHover(ulong? except, string id) { //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_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_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_0033: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 10; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, id ?? ""); CoopMessages.SendToReadyClients("GK2Coop.Story", writer, (NetworkDelivery)3, except); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void HoverFor(ulong sender, string id) { if (sender == talkOwner) { Highlight(LatestMenuWith(id) ?? LatestMenu(), id, picked: false); SendHover(sender, id); } } private static UIMultiAnswer LatestMenu() { object? obj = (MultiAnswersField?.GetValue(null) as IList)?.Cast().LastOrDefault(); return (UIMultiAnswer)((obj is UIMultiAnswer) ? obj : null); } private static UIMultiAnswer LatestMenuWith(string id) { if (string.IsNullOrEmpty(id) || !(MultiAnswersField?.GetValue(null) is IList list)) { return null; } for (int num = list.Count - 1; num >= 0; num--) { object? obj = list[num]; UIMultiAnswer val = (UIMultiAnswer)((obj is UIMultiAnswer) ? obj : null); if (val != null && VisualDataField?.GetValue(val) is List source && source.Any((AnswerVisualData v) => v != null && v.id == id)) { return val; } } return null; } private static void Highlight(UIMultiAnswer menu, string id, bool picked) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)menu == (Object)null || (Object)(object)((Component)menu).gameObject == (Object)null) { return; } List list = OptionsField.Invoke(menu); if (list == null) { return; } applyingHover = true; try { foreach (UIMultiAnswerOption item in list) { if ((Object)(object)item == (Object)null) { continue; } if (!string.IsNullOrEmpty(id) && OptionDataField.Invoke(item)?.id == id) { item.OnItemOver(); if (picked) { ShortcutExtensions.DOPunchScale(((Component)item).transform, new Vector3(0.12f, 0.12f, 0f), 0.4f, 6, 0.6f); } } else { item.OnItemOut(); } } } catch (Exception ex) { CoopLog.Debug("Highlighting answer '" + id + "' failed: " + ex.Message); } finally { applyingHover = false; } } private static void TickClosingAnswers() { if (!((Object)(object)closingAnswers == (Object)null) && !(Time.unscaledTime < closeAnswersAt)) { UIMultiAnswer open = closingAnswers; closingAnswers = null; Dismiss(open); } } private unsafe static void SendAnswerId(string id) { //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_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_0010: 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_0029: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, id); CoopMessages.SendToHost("GK2Coop.Request", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Picked answer '" + id + "'."); } private unsafe static void SendAnswer(int index) { //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_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_002e: 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_0048: Unknown result type (might be due to invalid IL or missing references) if (index >= 0 && index < answerIds.Count) { string text = answerIds[index]; FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text); CoopMessages.SendToHost("GK2Coop.Request", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Picked answer '" + text + "'."); ClearAnswers(); } } public static void SendNotice(ulong? onlyTo, string text) { Send(onlyTo, delegate(FastBufferWriter w) { //IL_0008: 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) byte b = 5; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, text); }); } private unsafe static void Send(ulong? onlyTo, WriteBody body) { //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: 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_002b: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host) { return; } FastBufferWriter writer = CoopMessages.NewWriter(); try { body(writer); if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Story", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Story", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } } public static bool IsStoryTarget(WgoData data) { if (data == null) { return false; } if (Plugin.StoryGateScope.Value == StoryScope.AllScriptedObjects) { return true; } if (((ObjectLinkedToDefinition)(object)data).id != null) { return ((ObjectLinkedToDefinition)(object)data).id.StartsWith("npc_"); } return false; } public static bool PartnerMayFire(WgoData data) { if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || data == null || WorldSync.ApplyingRemote || EffectSync.ApplyingRemote) { return true; } List events = data.Events; if (events == null || events.Count == 0 || events[0] == null || events[0].isFake) { return true; } if (IsStoryTarget(data) && data.UniqueId != (SGuid)null) { ForwardTalk(data); return false; } string text = PlayerSync.Host?.Label ?? "the host"; CoopRunner.Think("That's part of the story: " + text + " does it."); CoopLog.Info("Didn't run the story step '" + events[0].str + "' on " + ((ObjectLinkedToDefinition)(object)data).id + " here: that's the host's to do."); return false; } public static bool StoryLocked(WgoData data) { if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || data == null) { return false; } if (TutorialSync.Active && ((ObjectLinkedToDefinition)(object)data).id != null && (((ObjectLinkedToDefinition)(object)data).id.StartsWith("intro_") || ((ObjectLinkedToDefinition)(object)data).id.EndsWith("_quest"))) { return true; } List events = data.Events; if (events == null || events.Count == 0 || events[0] == null || IsStoryTarget(data)) { return false; } if (events[0].isFake) { return TutorialSync.GuidedIntro; } return true; } public static void NoteStoryLocked(WgoData data) { if (!(Time.unscaledTime - lastLockedNotice < 3f)) { lastLockedNotice = Time.unscaledTime; string text = PlayerSync.Host?.Label ?? "the host"; CoopRunner.Think("That's part of the story: " + text + " does it."); CoopLog.Info("Didn't start work on " + ((ObjectLinkedToDefinition)(object)data)?.id + " here: it's part of the story, the host's to do."); } } private unsafe static void ForwardTalk(WgoData data) { //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_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_0010: 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_0049: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, data.UniqueId.Id); CoopMessages.WriteString(ref writer, ((ObjectLinkedToDefinition)(object)data).id ?? ""); CoopMessages.SendToHost("GK2Coop.Request", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Forwarded talk with " + ((ObjectLinkedToDefinition)(object)data).id + " (" + data.UniqueId.Id + ") to the host."); } private static void BeginSoloTalk(WgoData data) { soloTalk = true; soloTalkSince = Time.unscaledTime; CoopLog.Info("Host talks to " + ((ObjectLinkedToDefinition)(object)data).id + " (a shopkeeper) with partners away: they come if it becomes a cutscene."); } public unsafe static bool AllowInteraction(Wgo wgo) { //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_0154: 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_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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) WgoData val = ((wgo != null) ? wgo.Data : null); if (val == null) { return true; } if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && !gatherRunning && VendorSync.FindVendorForNpc(val) != null && PlayersNotNear(val).Any()) { ClearGather("talk started"); SetTalkOwner(0uL); BeginSoloTalk(val); return true; } if (!IsStoryTarget(val)) { return true; } DisplayName(val); if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && Plugin.RequireBothPlayers.Value) { if (gatherRunning) { return true; } if (PlayersNotNear(val).ToList().Count == 0) { CoopLog.Info("Host talks to " + ((ObjectLinkedToDefinition)(object)val).id + " with everyone nearby."); ClearGather("talk started"); SetTalkOwner(0uL); return true; } if (OnlyHostNeeded) { CoopLog.Info("Host talks to " + ((ObjectLinkedToDefinition)(object)val).id + "; partners watch it with us."); ClearGather("talk started"); SetTalkOwner(0uL); return true; } StartGather(wgo, 0uL); return false; } if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && VendorSync.TryOpenShop(val)) { return false; } if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && Plugin.ClientTalksRunOnHost.Value) { FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, val.UniqueId.Id); CoopMessages.WriteString(ref writer, ((ObjectLinkedToDefinition)(object)val).id ?? ""); CoopMessages.SendToHost("GK2Coop.Request", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Forwarded talk with " + ((ObjectLinkedToDefinition)(object)val).id + " (" + val.UniqueId.Id + ") to the host."); return false; } return true; } private static IEnumerable PlayersNotNear(WgoData target) { float radius = Plugin.PartnerRadius.Value; foreach (ulong readyClient in CoopSession.ReadyClients) { RemotePlayer remotePlayer = PlayerSync.Get(readyClient); if (remotePlayer == null || !remotePlayer.HasState || !(Vector3.Distance(remotePlayer.Target, target.Position) <= radius)) { yield return remotePlayer ?? new RemotePlayer { ClientId = readyClient }; } } } private static bool SameScene(string a, string b) { if (!string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b)) { return a == b; } return true; } public static void OnRequest(ulong sender, FastBufferReader reader) { //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) if (CoopSession.Role != CoopRole.Host) { return; } byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 2: ClearGather("cancelled by " + (PlayerSync.Get(sender)?.Label ?? $"Player {sender}")); break; case 3: ChooseAnswerFor(sender, CoopMessages.ReadString(ref reader)); break; case 4: HoverFor(sender, CoopMessages.ReadString(ref reader)); break; case 1: { string text = CoopMessages.ReadString(ref reader); string text2 = CoopMessages.ReadString(ref reader); string text3 = PlayerSync.Get(sender)?.Label ?? $"Player {sender}"; Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(text)); if ((Object)(object)wgoViewGlobal == (Object)null || wgoViewGlobal.Data == null) { SendNotice(sender, "The host needs to be in the same area as " + PrettyId(text2) + " for you to talk to them together."); CoopRunner.Toast(text3 + " wants to talk to " + PrettyId(text2) + ". Go there together."); CoopLog.Info("Talk request from " + text3 + " for " + text2 + ": not loaded on the host."); } else if (OnlyHostNeeded && !HostNear(wgoViewGlobal.Data)) { SendNotice(sender, (PlayerSync.Host?.Label ?? Plugin.PlayerName.Value) + " has to be here for that talk."); CoopLog.Info(text3 + " tried to talk to " + ((ObjectLinkedToDefinition)(object)wgoViewGlobal.Data).id + " without the host there; refused."); } else if (!OnlyHostNeeded && (!HostNear(wgoViewGlobal.Data) || PlayersNotNear(wgoViewGlobal.Data).Any())) { StartGather(wgoViewGlobal, sender); } else { RunTalk(wgoViewGlobal, text3, sender); } break; } } } private static bool HostNear(WgoData target) { //IL_0025: 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) PlayerController playerController = MainGame.PlayerController; if ((Object)(object)((playerController != null) ? playerController.PhysicalBody : null) != (Object)null) { return Vector3.Distance(((Component)playerController.PhysicalBody).transform.position, target.Position) <= Plugin.PartnerRadius.Value; } return false; } private static void StartGather(Wgo wgo, ulong requester) { gatherWgo = wgo; gatherRequester = requester; gatherStarted = Time.unscaledTime; nextGatherUpdate = 0f; string text = ((requester != 0L) ? (PlayerSync.Get(requester)?.Label ?? $"Player {requester}") : (PlayerSync.Host?.Label ?? Plugin.PlayerName.Value)); if (OnlyHostNeeded) { CoopLog.Info(text + " wants to talk to " + ((ObjectLinkedToDefinition)(object)wgo.Data).id + "; waiting for the host to come over."); CoopRunner.Toast($"{text} wants to talk to {DisplayName(wgo.Data)}. It starts when the host is within {Plugin.PartnerRadius.Value:0} m of them."); } else { CoopLog.Info(text + " wants to talk to " + ((ObjectLinkedToDefinition)(object)wgo.Data).id + "; waiting for everyone to gather."); CoopRunner.Toast($"Story talk with {DisplayName(wgo.Data)}: everyone needs to be within {Plugin.PartnerRadius.Value:0} m. It starts when you're all there."); } } public unsafe static void CancelGather() { //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_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_002b: 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_003c: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role == CoopRole.Host) { ClearGather("cancelled"); } else if (CoopSession.Role == CoopRole.Client) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.SendToHost("GK2Coop.Request", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } gatherTitle = null; } } private static void ClearGather(string why) { if (!((Object)(object)gatherWgo == (Object)null)) { CoopLog.Info("Talk gathering for " + ((ObjectLinkedToDefinition)(object)gatherWgo.Data)?.id + " ended (" + why + ")."); gatherWgo = null; Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_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) byte b = 7; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = 0; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); }); } } private static void RunTalk(Wgo wgo, string who, ulong owner) { PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController != (Object)null && !playerController.IsControlsEnabled) { CoopRunner.Toast("The host is busy; the talk will start when they're free."); return; } CoopRunner.Toast((owner == 0L) ? (who + " started a conversation with " + DisplayName(wgo.Data) + ".") : (who + " started a conversation with " + DisplayName(wgo.Data) + " and picks the answers.")); CoopLog.Info("Running " + who + "'s talk with " + ((ObjectLinkedToDefinition)(object)wgo.Data).id + " on the host."); SetTalkOwner(owner); gatherRunning = true; try { List events = wgo.Data.Events; if (events.Count <= 0 || events[0] == null || events[0].isFake || !wgo.Data.FireInteractionEvent()) { GlobalEventsSystem.FireTrigger((Type)26, ((ObjectLinkedToDefinition)(object)wgo.Data).id ?? ""); wgo.FireEvent("interaction"); } } finally { gatherRunning = false; } } private static void TickGather() { //IL_0103: 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_0158: 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) if ((Object)(object)gatherWgo == (Object)null || Time.unscaledTime < nextGatherUpdate) { return; } nextGatherUpdate = Time.unscaledTime + 0.5f; WgoData val = (((Object)(object)gatherWgo != (Object)null) ? gatherWgo.Data : null); if ((Object)(object)gatherWgo == (Object)null || val == null) { ClearGather("the NPC is gone"); return; } if (Time.unscaledTime - gatherStarted > 150f) { ClearGather("timed out"); return; } if (gatherRequester != 0L && !CoopSession.ReadyClients.Contains(gatherRequester)) { ClearGather("requester left"); return; } float value = Plugin.PartnerRadius.Value; List lines = new List(); bool flag = true; PlayerController playerController = MainGame.PlayerController; bool flag2 = HostNear(val); flag = flag && flag2; lines.Add(Describe(PlayerSync.Host?.Label ?? Plugin.PlayerName.Value, ((Component)playerController.PhysicalBody).transform.position, playerController.PlayerData.currentGameSceneId, val, value)); foreach (ulong readyClient in CoopSession.ReadyClients) { RemotePlayer remotePlayer = PlayerSync.Get(readyClient); bool flag3 = remotePlayer != null && remotePlayer.HasState && Vector3.Distance(remotePlayer.Target, val.Position) <= value; flag &= flag3 || OnlyHostNeeded; lines.Add((remotePlayer == null) ? $"Player {readyClient}: ?" : Describe(remotePlayer.Label, remotePlayer.Target, remotePlayer.Scene, val, value)); } if (flag) { Wgo wgo = gatherWgo; string who = ((gatherRequester == 0L) ? Plugin.PlayerName.Value : (PlayerSync.Get(gatherRequester)?.Label ?? "a partner")); ulong owner = gatherRequester; ClearGather("everyone is here"); RunTalk(wgo, who, owner); return; } string title = (OnlyHostNeeded ? $"Talk with {DisplayName(val)}: waiting for the host (within {value:0} m)" : $"Talk with {DisplayName(val)}: everyone within {value:0} m"); gatherTitle = title; gatherLines = lines; Send(null, delegate(FastBufferWriter w) { //IL_0008: 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_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_0047: 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) byte b = 7; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); b = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, title); int count = lines.Count; ((FastBufferWriter)(ref w)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item in lines) { CoopMessages.WriteString(ref w, item); } }); } private static string Describe(string name, Vector3 position, string scene, WgoData target, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) float num = Vector3.Distance(position, target.Position); if (num > 150f) { return name + ": far away"; } if (!(num <= radius)) { return $"{name}: {num:0} m away"; } return name + ": here"; } public static void DrawGather(GUIStyle style) { //IL_0076: 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_00f6: Unknown result type (might be due to invalid IL or missing references) if ((CoopSession.Role == CoopRole.Host && (Object)(object)gatherWgo == (Object)null) || (CoopSession.Role == CoopRole.Client && gatherTitle == null) || gatherTitle == null || LazyWindowsStackController.HasAnyModalWindowOpened) { return; } float num = 360f; float num2 = 34f + (float)gatherLines.Count * 22f; Rect val = default(Rect); ((Rect)(ref val))..ctor((float)Screen.width - num - 20f, (float)Screen.height * 0.35f, num, num2); GUI.Box(val, GUIContent.none); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 6f, num - 20f, 22f), gatherTitle, style); float num3 = ((Rect)(ref val)).y + 28f; foreach (string gatherLine in gatherLines) { GUI.Label(new Rect(((Rect)(ref val)).x + 16f, num3, num - 26f, 22f), gatherLine, style); num3 += 22f; } } public static void OnStory(ulong sender, FastBufferReader reader) { //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_0049: 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_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 1: { byte b7 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b7, default(ForPrimitives)); string text2 = CoopMessages.ReadString(ref reader); if (b7 != 0 && !hostInStory) { storyStartedAt = Time.unscaledTime; storyLines = 0; } hostInStory = b7 != 0; hostStoryKind = text2; CoopLog.Info("Host story " + (hostInStory ? "started" : "ended") + " (" + text2 + ")."); if (hostInStory) { BeginWatch(text2); break; } EndWatch("host scene ended"); ClearAnswers(); if ((Object)(object)realAnswers != (Object)null && !realAnswersMine) { CloseRealAnswers(); } break; } case 9: { string onlyKey = CoopMessages.ReadString(ref reader); CloseMirroredBubbles("the host's line ended", onlyKey); break; } case 10: { string id = CoopMessages.ReadString(ref reader); if ((Object)(object)realAnswers != (Object)null && !realAnswersMine) { Highlight(realAnswers, id, picked: false); } break; } case 8: { string text3 = CoopMessages.ReadString(ref reader); string speaker = CoopMessages.ReadString(ref reader); ClearAnswers(); if ((Object)(object)realAnswers != (Object)null && !realAnswersMine && (Object)(object)((Component)realAnswers).gameObject != (Object)null) { Highlight(realAnswers, text3, picked: true); closingAnswers = realAnswers; closeAnswersAt = Time.unscaledTime + 0.55f; realAnswers = null; } else { CloseRealAnswers(); } if (!StandIn.Showing) { AddLine(speaker, Localize(text3)); } break; } case 2: { string text5 = CoopMessages.ReadString(ref reader); if (CoopSession.ClientInWorld) { CoopLog.Info("Playing the host's cinematic '" + text5 + "'."); cinematicSince = Time.unscaledTime; ReplayingCinematic = true; try { LazySingleton.Instance.DisplayCinematicsScene(text5, (Action)null); break; } finally { ReplayingCinematic = false; } } break; } case 3: cinematicSince = -1f; if (CoopSession.ClientInWorld) { bool flag = LazySingleton.Instance.Cancel(); CoopLog.Info("Host cinematic ended (" + (flag ? "stopped ours" : "ours had already finished") + ")."); } break; case 4: storyLines++; ShowPhrase(ref reader); break; case 5: { string text4 = CoopMessages.ReadString(ref reader); CoopRunner.Toast(text4); CoopLog.Info("Notice from host: " + text4); break; } case 7: { byte b6 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b6, default(ForPrimitives)); if (b6 == 0) { gatherTitle = null; gatherLines = new List(); break; } gatherTitle = CoopMessages.ReadString(ref reader); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); List list2 = new List(); for (int j = 0; j < num2; j++) { list2.Add(CoopMessages.ReadString(ref reader)); } gatherLines = list2; break; } case 6: { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List list = new List(); for (int i = 0; i < num; i++) { list.Add(CoopMessages.ReadString(ref reader)); } byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); string text = CoopMessages.ReadString(ref reader); string participantUid = CoopMessages.ReadString(ref reader); byte b3 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b3, default(ForPrimitives)); int corner = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref corner, default(ForPrimitives)); byte b4 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b4, default(ForPrimitives)); byte b5 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b5, default(ForPrimitives)); byte[] full = CoopMessages.ReadBytes(ref reader); List values = list.Select(Localize).ToList(); if (StandIn.Showing && ShowRealAnswers(list, full, b2 != 0, text, participantUid, b3 != 0, corner, b4 != 0, b5 != 0)) { ClearAnswers(); } else if (b2 != 0) { answerIds = list; answerTexts = values; answerShownAt = Time.unscaledTime; AddLine("Your answer", "pick one below"); CoopLog.Info("We pick the answer: " + string.Join(" / ", list) + "."); } else { ClearAnswers(); AddLine(text + " is choosing", string.Join(" / ", values)); } break; } } } private static string KeyOf(UISpeechBubble bubble) { try { return VoiceKeyGetter?.Invoke(bubble, null) as string; } catch { return null; } } private static void ShowPhrase(ref FastBufferReader reader) { //IL_000c: 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_002c: 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_003e: 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_0050: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Invalid comparison between Unknown and I4 //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: 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_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) string text = CoopMessages.ReadString(ref reader); byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); string text2 = CoopMessages.ReadString(ref reader); string text3 = CoopMessages.ReadString(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); float num3 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); WgoData val = null; if (b == 0 && !string.IsNullOrEmpty(text2)) { WorldData worldData = MainGame.WorldData; val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text2)) : null); } bool flag = b == 0 && text3 == "player_wisp"; string speaker = ((b == 0) ? ((val != null) ? DisplayName(val) : PrettyId(text3)) : (PlayerSync.Host?.Label ?? "Host")); if (!CoopSession.ClientInWorld) { AddLine(speaker, Localize(text)); return; } if (hostInStory) { CloseMirroredBubbles("next line"); } if ((Object)(object)realAnswers != (Object)null && !realAnswersMine && Time.unscaledTime - realAnswersShownAt > 0.5f) { CloseRealAnswers(); } try { List obj = BubblePresets.Invoke((Bubble)BubbleInstanceField.GetValue(null)); SpeechBubbleType val2 = (SpeechBubbleType)num; SpeechBubblePreset preset = obj[((int)val2 != 1) ? 1 : 0]; UISpeechBubble val3 = null; bool flag2 = val != null && ((ObjectLinkedToDefinition)(object)val).Definition != null && ((ObjectLinkedToDefinition)(object)val).Definition.usePortraitInDialogues; if (StandIn.ActsAsHost || (val != null && !flag && b == 0 && !flag2)) { PhraseData val4 = new PhraseData { text = text, isPlayer = (b != 0), npcWgoData = ((b != 0) ? null : val), onFinished = delegate { }, cornerPosition = (ForceCornerPosition)num2, fixedShowTimeValue = num3, isOverBlackout = (b2 != 0), speechType = val2, preset = preset }; if (b != 0 || val != null) { val3 = (UISpeechBubble)(object)UIDialogBubble.ShowMessage(val4); } } else if (b != 0 || flag2) { RemotePlayer host = PlayerSync.Host; if ((Object)(object)host?.Controller != (Object)null && (host.Visible || flag2)) { Transform point = host.Controller.BubblePoint; val3 = ShowFloatingLine(text, preset, (ForceCornerPosition)num2, num3, flag2 ? ((object)val).GetHashCode() : ((object)host.Controller).GetHashCode(), () => (!((Object)(object)point != (Object)null)) ? (host.Position + Vector3.up * 1.6f) : point.position, (AnimationComponent)(object)(flag2 ? null : host.Body.PlayerView.PlayerAnimation), flag2 ? val : null, b2 != 0); } } else if (flag && StandIn.HostWispVisible) { val3 = ShowFloatingLine(text, preset, (ForceCornerPosition)num2, num3, 356695, () => StandIn.HostWispPosition + Vector3.up * 0.4f, null, null, b2 != 0); } if ((Object)(object)val3 != (Object)null) { mirroredBubbles.Add(val3); } else { AddLine(speaker, Localize(text)); } } catch (Exception e) { CoopLog.Error("Showing the host's line '" + text + "' failed", e); AddLine(speaker, Localize(text)); } } private static UISpeechBubble ShowFloatingLine(string text, SpeechBubblePreset preset, ForceCornerPosition corner, float fixedShowTime, int speakerId, Func worldPosition, AnimationComponent mouth, WgoData portrait = null, bool overBlackout = false) { //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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) Vector3 val = CameraSystem.WorldToScreenPoint(worldPosition()); VoiceID val2 = ((((ObjectLinkedToDefinition)(object)portrait)?.Definition != null && ((ObjectLinkedToDefinition)(object)portrait).Definition.voiceId != null && (Enumeration)(object)((ObjectLinkedToDefinition)(object)portrait).Definition.voiceId != (Enumeration)(object)VoiceID.None) ? ((ObjectLinkedToDefinition)(object)portrait).Definition.voiceId : VoiceID.Feather); UISpeechBubble val3 = UISpeechBubble.ShowMessage(speakerId, text, Vector2.op_Implicit(val), preset, val2, (Action)null, (Action)null, (Func)(() => Vector2.op_Implicit(CameraSystem.WorldToScreenPoint(worldPosition()))), (Func)null, corner, fixedShowTime, true, default(Vector3)); UIDialogBubble val4 = (UIDialogBubble)(object)((val3 is UIDialogBubble) ? val3 : null); if (val4 != null) { BubbleVoiceOverField?.SetValue(val4, LazyAudio.VoiceOverPlayer.HasVoiceOver && VoiceOverSettings.IsEnabled); if ((Object)(object)mouth != (Object)null) { BubbleAnimationField?.SetValue(val4, mouth); } Canvas component = ((Component)val4).GetComponent(); if ((Object)(object)component != (Object)null) { component.overrideSorting = true; component.sortingOrder = (overBlackout ? 900 : 350); } if (portrait != null) { try { BubblePhraseField?.SetValue(val4, (object)new PhraseData { text = text, isPlayer = false, npcWgoData = portrait, cornerPosition = corner }); DrawNpcStuffMethod?.Invoke(val4, null); } catch (Exception ex) { CoopLog.Debug("Couldn't show " + ((ObjectLinkedToDefinition)(object)portrait).id + "'s portrait: " + ex.Message); } } } return val3; } private static void CloseMirroredBubbles(string why, string onlyKey = null) { bool flag = false; for (int num = mirroredBubbles.Count - 1; num >= 0; num--) { UISpeechBubble val = mirroredBubbles[num]; if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf) { mirroredBubbles.RemoveAt(num); } else if (onlyKey == null || !(KeyOf(val) != onlyKey)) { try { ForceHideMethod?.Invoke(val, null); flag = true; } catch (Exception ex) { CoopLog.Debug("Closing a bubble (" + why + ") failed: " + ex.Message); } mirroredBubbles.RemoveAt(num); } } if (flag && LazyAudio.VoiceOverPlayer.IsPlaying) { LazyAudio.VoiceOverPlayer.Stop(); } } public static void HostLineEnded(UISpeechBubble bubble) { if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || !(bubble is UIDialogBubble)) { return; } string key = KeyOf(bubble); if (!string.IsNullOrEmpty(key)) { Send(null, delegate(FastBufferWriter w) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) byte b = 9; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, key); }); } } private static void TickClient() { if (!CoopSession.ClientInWorld) { return; } TickRealAnswers(); TickClosingAnswers(); TickDismissed(); if (hostInStory && !watching) { BeginWatch(hostStoryKind); } if (watching && !(Time.unscaledTime < nextLockCheck)) { nextLockCheck = Time.unscaledTime + 0.25f; PlayerController playerController = MainGame.PlayerController; if ((Object)(object)playerController != (Object)null && playerController.IsControlEnabledByType((TakenControlType)11)) { playerController.SetControlTakenType((TakenControlType)11, false); } if (!cameraOnHost && !StandIn.Enabled) { FollowHostCamera(); } } } private static void BeginWatch(string kind) { if (watching) { endingWatch = false; StandIn.CancelEnd(); } else if (CoopSession.ClientInWorld && Plugin.WatchHostScenes.Value) { watching = true; endingWatch = false; MainGame.PlayerController.SetControlTakenType((TakenControlType)11, false); if (!StandIn.Enabled) { BringNearHost(); FollowHostCamera(); } CoopLog.Info("Watch mode on (" + kind + ")."); } } private static void EndWatch(string why) { if (watching) { if (StandIn.Engaged) { endingWatch = true; StandIn.RequestEnd(); CoopLog.Info("Watch ending (" + why + "); waiting a moment in case another scene follows."); } else { FinishWatch(why); } } } public static void FinishWatch(string why) { if (!watching) { return; } watching = false; endingWatch = false; try { PlayerController playerController = MainGame.PlayerController; if (playerController != null) { playerController.SetControlTakenType((TakenControlType)11, true); } RestoreCamera(); } catch (Exception e) { CoopLog.Error("Leaving watch mode failed", e); } lastLineTime = Time.unscaledTime; CoopLog.Info("Watch mode off (" + why + ")."); } private static void BringNearHost() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00e5: Unknown result type (might be due to invalid IL or missing references) RemotePlayer host = PlayerSync.Host; PlayerController playerController = MainGame.PlayerController; if (host == null || !host.HasState || (Object)(object)playerController == (Object)null) { return; } if (!SameScene(host.Scene, playerController.PlayerData.currentGameSceneId)) { CoopRunner.Toast("The host's story scene is in another area. Go to them to see it."); CoopLog.Info("Host story is in scene '" + host.Scene + "', we're in '" + playerController.PlayerData.currentGameSceneId + "'; not teleporting."); } else { float num = Vector3.Distance(((Component)playerController.PhysicalBody).transform.position, host.Target); if (num > Plugin.TeleportDistance.Value) { Vector3 val = host.Target + new Vector3(1.2f, 0f, -0.4f); playerController.SetPosition(val, true, true); CoopLog.Info($"Brought the joining player next to the host ({num:0} m away) at {val}."); } } } private static void FollowHostCamera() { RemotePlayer host = PlayerSync.Host; CameraSystem instance = CameraSystem.Instance; CameraController val = ((instance != null) ? instance.ActiveCameraController : null); if (!((Object)(object)host?.Body == (Object)null) && host.Visible && !((Object)(object)val == (Object)null)) { if (!cameraOnHost) { savedCameraTarget = val.Target; } val.SetTarget(((Component)host.Body.PlayerView).transform, 0.5f, (Action)null); cameraOnHost = true; CoopLog.Debug("Camera now follows the host."); } } private static void RestoreCamera() { //IL_0077: 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_0087: Unknown result type (might be due to invalid IL or missing references) if (!cameraOnHost) { return; } cameraOnHost = false; CameraSystem instance = CameraSystem.Instance; CameraController val = ((instance != null) ? instance.ActiveCameraController : null); PlayerController playerController = MainGame.PlayerController; object obj; if (playerController == null) { obj = null; } else { PlayerView view = playerController.View; obj = ((view != null) ? ((Component)view).transform : null); } Transform val2 = (Transform)obj; Transform val3 = (((Object)(object)savedCameraTarget != (Object)null) ? savedCameraTarget : val2); if ((Object)(object)val != (Object)null && (Object)(object)val3 != (Object)null) { Transform target = val.Target; if ((Object)(object)target != (Object)null) { Vector3 val4 = target.position - val3.position; if (((Vector3)(ref val4)).sqrMagnitude > 225f) { val.SetTargetInstant(val3); goto IL_00ad; } } val.SetTarget(val3, 0.5f, (Action)null); } goto IL_00ad; IL_00ad: savedCameraTarget = null; CoopLog.Debug("Camera back on our character."); } private static void AddLine(string speaker, string text) { if (!string.IsNullOrEmpty(text)) { lines.Add(new StoryLine { Speaker = speaker, Text = text }); if (lines.Count > 4) { lines.RemoveAt(0); } lastLineTime = Time.unscaledTime; } } public static void DrawPanel(GUIStyle style) { //IL_00cc: 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_01ca: Invalid comparison between Unknown and I4 //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Invalid comparison between Unknown and I4 //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Invalid comparison between Unknown and I4 //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Expected I4, but got Unknown if (CoopSession.Role != CoopRole.Client || (lines.Count == 0 && answerIds.Count == 0)) { return; } if (answerIds.Count > 0 && Time.unscaledTime - answerShownAt > 300f) { ClearAnswers(); } if ((StandIn.Showing && answerIds.Count == 0) || (!watching && answerIds.Count == 0 && Time.unscaledTime - lastLineTime > 8f)) { return; } float num = Mathf.Min(760f, (float)Screen.width - 40f); float num2 = 128f + (float)answerIds.Count * 30f; GUILayout.BeginArea(new Rect(((float)Screen.width - num) / 2f, (float)Screen.height - 22f - num2, num, num2), GUI.skin.box); GUILayout.Label(watching ? "Watching with the host" : "Host's story", style, Array.Empty()); foreach (StoryLine line in lines) { GUILayout.Label(line.Speaker + ": " + line.Text, style, Array.Empty()); } for (int i = 0; i < answerIds.Count; i++) { if (GUILayout.Button($"{i + 1}. {answerTexts[i]}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) })) { SendAnswer(i); break; } } GUILayout.EndArea(); Event current2 = Event.current; if (answerIds.Count > 0 && (int)current2.type == 4 && (int)current2.keyCode >= 49 && (int)current2.keyCode <= 57) { int num3 = current2.keyCode - 49; if (num3 < answerIds.Count) { SendAnswer(num3); current2.Use(); } } } private static string Localize(string key) { if (string.IsNullOrEmpty(key)) { return key; } try { string text = LLBase.L(key); return string.IsNullOrEmpty(text) ? key : text; } catch { return key; } } public static string DisplayName(WgoData data) { string text = Localize(((ObjectLinkedToDefinition)(object)data).id); if (!string.IsNullOrEmpty(text) && !(text == ((ObjectLinkedToDefinition)(object)data).id)) { return text; } return PrettyId(((ObjectLinkedToDefinition)(object)data).id); } private static string PrettyId(string id) { if (string.IsNullOrEmpty(id)) { return "someone"; } string text = (id.StartsWith("npc_") ? id.Substring(4) : id); text = text.Replace('_', ' '); if (text.Length <= 0) { return text; } return char.ToUpperInvariant(text[0]) + text.Substring(1); } } public enum StoryScope { NpcsOnly, AllScriptedObjects } [HarmonyPatch(typeof(UISpeechBubble), "CheckSkip")] internal static class HostOnlySkips { private static bool Prefix() { return StorySync.ClientMaySkip; } } [HarmonyPatch(typeof(UISpeechBubble), "DisableBubble")] internal static class MirrorLineEnd { private static void Prefix(UISpeechBubble __instance) { try { StorySync.HostLineEnded(__instance); } catch (Exception ex) { CoopLog.Debug("Mirroring a line ending failed: " + ex.Message); } } } [HarmonyPatch(typeof(UIMultiAnswerOption), "OnItemOver")] internal static class MirrorAnswerHover { private static bool Prefix(UIMultiAnswerOption __instance) { return StorySync.AllowLocalHover(__instance); } private static void Postfix(UIMultiAnswerOption __instance) { try { StorySync.OnLocalHover(__instance, over: true); } catch (Exception ex) { CoopLog.Debug("Mirroring an answer hover failed: " + ex.Message); } } } [HarmonyPatch(typeof(UIMultiAnswerOption), "OnItemOut")] internal static class MirrorAnswerHoverOut { private static bool Prefix(UIMultiAnswerOption __instance) { return StorySync.AllowLocalHover(__instance); } private static void Postfix(UIMultiAnswerOption __instance) { try { StorySync.OnLocalHover(__instance, over: false); } catch (Exception ex) { CoopLog.Debug("Mirroring an answer hover failed: " + ex.Message); } } } [HarmonyPatch(typeof(WgoData), "FireInteractionEvent")] internal static class HostDoesStorySteps { private static bool Prefix(WgoData __instance, ref bool __result) { try { if (!StorySync.PartnerMayFire(__instance)) { __result = true; return false; } } catch (Exception e) { CoopLog.Error("Checking a story step failed; letting it run", e); } return true; } } [HarmonyPatch] internal static class HostWorksStoryProps { private static IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(PlayerHPActivity), "CanStartActivity", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(PlayerCraftActivity), "CanStartActivity", (Type[])null, (Type[])null); } private static void Postfix(PlayerActivity __instance, ref bool __result) { try { if (__result && StorySync.StoryLocked((__instance != null) ? __instance.WgoData : null)) { __result = false; StorySync.NoteStoryLocked(__instance.WgoData); } } catch (Exception ex) { CoopLog.Debug("Checking a story prop failed: " + ex.Message); } } } internal static class TimeSync { private const float SendInterval = 1f; private const float MaxDriftSeconds = 2f; private static float nextSend; private static bool warnedAhead; private static bool pausedForHost; private const string LackOfSleep = "lack_of_sleep_debuff"; private static readonly FieldRef DayField = AccessTools.FieldRefAccess("day"); public static int LastHostDay { get; private set; } public static float LastHostTime { get; private set; } public static bool CountsTimeAwake { get { if (CoopSession.Role == CoopRole.Client) { return !CoopSession.ClientInWorld; } return true; } } private static bool HasLackOfSleep() { MainGame instance = MainGame.Instance; bool? obj; if (instance == null) { obj = null; } else { GameSave gameSave = instance.GameSave; if (gameSave == null) { obj = null; } else { PerkSystemData perkSystemData = gameSave.perkSystemData; obj = ((perkSystemData != null) ? new bool?(perkSystemData.HasPerk("lack_of_sleep_debuff")) : ((bool?)null)); } } bool? flag = obj; return flag == true; } public static bool AllowClockUpdate(EnvironmentEngine engine, float deltaTime) { if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || LastHostDay <= 0 || engine.IsPaused) { return true; } MainGame instance = MainGame.Instance; EnvironmentData val = ((instance == null) ? null : instance.GameSave?.environmentData); if (val == null) { return true; } if (!(engine.timeOfDay + engine.ConvertDeltaTimeToGameplayTime01(deltaTime) < 1f)) { return LastHostDay > val.Day; } return true; } public unsafe static void Tick() { //IL_0056: 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_005c: 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_006c: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_00ac: 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_00ca: 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) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !CoopSession.IsInGame || Time.unscaledTime < nextSend) { return; } nextSend = Time.unscaledTime + 1f; EnvironmentData val = MainGame.Instance.GameSave?.environmentData; EnvironmentEngine instance = EnvironmentEngine.Instance; if (val == null || (Object)(object)instance == (Object)null) { return; } FastBufferWriter val2 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val2; int day = val.Day; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref day, default(ForPrimitives)); float timeOfDay = val.TimeOfDay; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref timeOfDay, default(ForPrimitives)); byte b = (instance.IsPaused ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = (HasLackOfSleep() ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref instance.timeOfDay, default(ForPrimitives)); CoopMessages.SendToReadyClients("GK2Coop.Time", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } public static void OnTime(ulong sender, FastBufferReader reader) { //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_0019: 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_002c: 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_003f: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); float num2 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); float num3 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); LastHostDay = num; LastHostTime = num2; if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld) { return; } EnvironmentData val = MainGame.Instance.GameSave?.environmentData; EnvironmentEngine instance = EnvironmentEngine.Instance; if (val == null || (Object)(object)instance == (Object)null) { return; } EnergySystem val2 = MainGame.PlayerData?.energySystem; bool flag = SleepSync.InSharedSleep || (val2 != null && (val2.IsSleeping || val2.IsInTransitionBetweenSleep)); if (flag && pausedForHost) { pausedForHost = false; instance.IsPaused = false; CoopLog.Debug("Sleeping: our clock runs (sleep needs it)."); } if (b != 0 && !instance.IsPaused && !flag) { instance.IsPaused = true; pausedForHost = true; CoopLog.Debug("The host's clock stopped (a scene); stopped ours too."); } else if (b == 0 && pausedForHost) { pausedForHost = false; instance.IsPaused = false; CoopLog.Debug("The host's clock runs again; so does ours."); } ApplyLackOfSleep(b2 != 0); if (b != 0 && instance.IsPaused && Mathf.Abs(instance.timeOfDay - num3) > 0.002f) { instance.SetTimeOfDay(num3); val.SetTimeOfDay(num2); CoopLog.Debug($"Showing the host's scene time {num3:0.000} (real clock {num2:0.000})."); } int day = val.Day; float timeOfDay = val.TimeOfDay; if (num > day && !instance.IsPaused) { CoopLog.Info($"Host is on day {num}, we're on {day}: rolling over to the next day."); instance.SetTimeOfDay(0.99999f); return; } if (num < day) { DayField.Invoke(val) = num; if (!warnedAhead) { warnedAhead = true; CoopLog.Warn($"This game was on day {day} but the host is on {num}; set our day back to the host's."); } } float num4 = Mathf.Abs(num2 - timeOfDay); num4 = Mathf.Min(num4, 1f - num4); float num5 = num4 * instance.gameplayDayInMinutes * 60f; if (num5 > 2f) { CoopLog.Debug($"Time drift {num5:0.0}s (host {num2:0.0000}, us {timeOfDay:0.0000}, host clock paused {b != 0}, ours {instance.IsPaused}); snapping to host."); if (instance.IsPaused) { val.SetTimeOfDay(num2); } else { instance.SetTimeOfDay(num2); } } } public static string Describe() { try { MainGame instance = MainGame.Instance; EnvironmentData val = ((instance == null) ? null : instance.GameSave?.environmentData); EnvironmentEngine instance2 = EnvironmentEngine.Instance; string text = ((val != null && (Object)(object)instance2 != (Object)null) ? $"day {val.Day} t={instance2.timeOfDay:0.0000} paused={instance2.IsPaused}" : "n/a"); return (CoopSession.Role == CoopRole.Client) ? $"{text} (host day {LastHostDay} t={LastHostTime:0.0000})" : text; } catch (Exception ex) { return "n/a: " + ex.Message; } } private static void ApplyLackOfSleep(bool host) { MainGame instance = MainGame.Instance; PerkSystemData val = ((instance == null) ? null : instance.GameSave?.perkSystemData); if (val != null && val.HasPerk("lack_of_sleep_debuff") != host) { if (host) { val.AddPerk("lack_of_sleep_debuff"); } else { val.RemovePerk("lack_of_sleep_debuff", false); } CoopLog.Info("Lack of sleep " + (host ? "on" : "off") + ", as for the host."); } } public static void Reset() { if (pausedForHost && (Object)(object)EnvironmentEngine.Instance != (Object)null) { EnvironmentEngine.Instance.IsPaused = false; } pausedForHost = false; warnedAhead = false; LastHostDay = 0; LastHostTime = 0f; } } [HarmonyPatch(typeof(EnvironmentEngine), "CustomUpdate")] internal static class HostDecidesMidnight { private static bool Prefix(EnvironmentEngine __instance, float deltaTime) { return TimeSync.AllowClockUpdate(__instance, deltaTime); } } [HarmonyPatch(typeof(EnergySystem), "TrackTimeWithoutSleep")] internal static class HostDecidesLackOfSleep { private static bool Prefix() { return TimeSync.CountsTimeAwake; } } internal static class TroopSync { private struct Pending { public string Other; public float Until; } private const byte OpRequest = 1; private const byte OpDone = 2; private const byte OpDropped = 3; private const byte OpRefused = 4; private const byte OpStandFlag = 5; private const byte OpFighterLook = 6; private static readonly FieldRef AssignedWgoField = AccessTools.FieldRefAccess("assignedWgo"); private static readonly FieldRef InteractorField = AccessTools.FieldRefAccess("interactor"); public static bool Replaying; public static bool RunningForPartner; public static int Sent; public static int Applied; private static readonly Dictionary pendingStands = new Dictionary(); private static readonly Dictionary pendingLooks = new Dictionary(); public static bool ClientSkipsStandSpawn { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { return Plugin.HostRunsWorld.Value; } return false; } } public static void Reset() { Replaying = false; RunningForPartner = false; pendingStands.Clear(); pendingLooks.Clear(); Sent = 0; Applied = 0; } private static string TargetUid(WGOInteractionHandlerBase handler) { Wgo val = ((handler != null) ? AssignedWgoField.Invoke(handler) : null); if ((Object)(object)val != (Object)null) { WgoData data = val.Data; if (((data != null) ? data.UniqueId : null) != (SGuid)null) { return val.Data.UniqueId.Id; } } return null; } public static string TargetOf(WGOInteractionHandlerBase handler) { return TargetUid(handler); } private static string CarriedBy(PlayerController controller) { Wgo val = (((Object)(object)controller != (Object)null) ? controller.attachedWgo : null); if ((Object)(object)val != (Object)null) { WgoData data = val.Data; if (((data != null) ? data.UniqueId : null) != (SGuid)null) { return val.Data.UniqueId.Id; } } return ""; } private static PlayerController CarrierController(ulong carrier) { if (CoopNetwork.LocalClientId == carrier) { return MainGame.PlayerController; } RemotePlayer remotePlayer = PlayerSync.Get(carrier); if (remotePlayer == null || !((Object)(object)remotePlayer.Body != (Object)null)) { return null; } return remotePlayer.Controller; } public unsafe static bool BeforeFlagInteract(WGOInteractionHandlerBase handler, PlayerController interactor, ref bool result) { //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_004e: 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_0058: 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_0084: Unknown result type (might be due to invalid IL or missing references) if (Replaying || CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || !Plugin.HostRunsWorld.Value) { return true; } if ((Object)(object)interactor == (Object)null || (Object)(object)interactor != (Object)(object)MainGame.PlayerController) { return true; } string text = TargetUid(handler); if (text == null) { return true; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, ((object)handler).GetType().Name); CoopMessages.WriteString(ref writer, text); CoopMessages.SendToHost("GK2Coop.Troops", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; string[] obj = new string[7] { "Flag action ", ((object)handler).GetType().Name, " on ", null, null, null, null }; Wgo obj2 = AssignedWgoField.Invoke(handler); obj[3] = ((obj2 == null) ? null : ((ObjectLinkedToDefinition)(object)obj2.Data)?.id); obj[4] = " ("; obj[5] = text; obj[6] = ") sent to the host's fight."; CoopLog.Info(string.Concat(obj)); try { interactor.PlayerInteractionComponent.ResetInteractionState(); } catch { } result = true; return false; } public static void AfterFlagInteract(WGOInteractionHandlerBase handler, PlayerController interactor, bool result, string uid) { if (result && !Replaying && uid != null && CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && !((Object)(object)interactor == (Object)null) && !((Object)(object)interactor != (Object)(object)MainGame.PlayerController)) { SendDone(0uL, ((object)handler).GetType().Name, uid, CarriedBy(interactor)); CoopLog.Info("Our flag action " + ((object)handler).GetType().Name + " on " + uid + ": sent for replay (we carry '" + CarriedBy(interactor) + "')."); } } private static void RunFor(ulong sender, string handlerType, string uid) { //IL_016c: Unknown result type (might be due to invalid IL or missing references) RemotePlayer remotePlayer = PlayerSync.Get(sender); string text = remotePlayer?.Label ?? $"Player {sender}"; string text2 = Plugin.PlayerName.Value ?? "the host"; if ((Object)(object)remotePlayer?.Controller == (Object)null || (Object)(object)remotePlayer.Body == (Object)null) { Refuse(sender, "Your character isn't in the host's game yet; try again in a moment."); return; } Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(uid)); if ((Object)(object)wgoViewGlobal == (Object)null || wgoViewGlobal.Data == null) { Refuse(sender, "The fight runs in " + text2 + "'s game, which needs that area loaded: do it while " + text2 + " is in the same area."); CoopLog.Info(text + "'s flag action on " + uid + ": not loaded here."); return; } IWGOInteractionHandler interactionHandler = wgoViewGlobal.InteractionHandler; WGOInteractionHandlerBase val = (WGOInteractionHandlerBase)(object)((interactionHandler is WGOInteractionHandlerBase) ? interactionHandler : null); if (val == null || ((object)val).GetType().Name != handlerType) { Refuse(sender, "That works differently in the host's game; nothing happened."); CoopLog.Warn(text + "'s flag action: expected " + handlerType + ", the host has " + (((object)val)?.GetType().Name ?? "none") + "."); return; } PlayerController controller = remotePlayer.Controller; PlayerData playerData = controller.PlayerData; if (playerData != null) { playerData.position.Value = remotePlayer.Target; playerData.currentGameSceneId = remotePlayer.Scene; } bool flag = false; PlayerController val2 = InteractorField.Invoke(val); Replaying = true; RunningForPartner = true; InteractorField.Invoke(val) = controller; try { if (!val.HasInteraction(controller)) { Refuse(sender, "Someone just did something with that flag; try again."); CoopLog.Info(text + "'s " + handlerType + " on " + ((ObjectLinkedToDefinition)(object)wgoViewGlobal.Data).id + ": not possible here right now."); return; } flag = val.Interact(controller); Applied++; } catch (Exception e) { CoopLog.Error("Running " + text + "'s flag action on " + ((ObjectLinkedToDefinition)(object)wgoViewGlobal.Data).id + " failed", e); Refuse(sender, "The host's game couldn't do that (see the host's co-op log)."); return; } finally { InteractorField.Invoke(val) = val2; Replaying = false; RunningForPartner = false; } if (!flag) { Refuse(sender, "Nothing to do with that flag right now."); return; } MoveCarriedFlags(); string text3 = CarriedBy(controller); SendDone(sender, handlerType, uid, text3); CoopLog.Info("Ran " + text + "'s " + handlerType + " on " + ((ObjectLinkedToDefinition)(object)wgoViewGlobal.Data).id + "; they carry '" + text3 + "'."); } private unsafe static void SendDone(ulong carrier, string handlerType, string uid, string carried) { //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_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_0010: 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_0022: 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_0054: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref carrier, default(ForPrimitives)); CoopMessages.WriteString(ref writer, handlerType); CoopMessages.WriteString(ref writer, uid); CoopMessages.WriteString(ref writer, carried ?? ""); CoopMessages.SendToReadyClients("GK2Coop.Troops", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } private unsafe static void SendDropped(ulong carrier) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_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_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) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.HasReadyClients) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref carrier, default(ForPrimitives)); CoopMessages.SendToReadyClients("GK2Coop.Troops", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } } private unsafe static void Refuse(ulong sender, string text) { //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_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_0010: 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_002a: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text); CoopMessages.SendTo("GK2Coop.Troops", sender, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void MoveCarriedFlags() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) foreach (RemotePlayer remote in PlayerSync.Remotes) { Wgo val = (((Object)(object)remote.Controller != (Object)null) ? remote.Controller.attachedWgo : null); if ((Object)(object)val != (Object)null && val.Data != null && remote.HasState) { val.Data.Position = remote.Target; } } } private static void DropFor(RemotePlayer remote, string why) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) PlayerController val = remote?.Controller; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.attachedWgo == (Object)null)) { Wgo val2 = val.RemoveTheFlag(); if ((Object)(object)val2 != (Object)null && val2.Data != null) { val2.Data.Position = remote.Target; AgentsGroupFlagController.SetInteractionLocked(val2, false); } SendDropped(remote.ClientId); CoopLog.Info(remote.Label + " dropped their flag (" + why + ")."); } } public static void AfterFightCleared(bool hostCarried) { if (CoopSession.Role != CoopRole.Host) { return; } if (hostCarried && (Object)(object)MainGame.PlayerController != (Object)null && (Object)(object)MainGame.PlayerController.attachedWgo == (Object)null) { SendDropped(0uL); } foreach (RemotePlayer item in PlayerSync.Remotes.ToList()) { DropFor(item, "the fight is over"); } } public static void OnCopyGone(RemotePlayer remote) { if ((Object)(object)remote?.Controller == (Object)null || (Object)(object)remote.Controller.attachedWgo == (Object)null) { return; } try { if (CoopSession.Role == CoopRole.Host) { DropFor(remote, "they left"); } else { remote.Controller.RemoveTheFlag(); } } catch (Exception ex) { CoopLog.Warn("Dropping " + remote.Label + "'s flag failed: " + ex.Message); } } public unsafe static void AfterStandSpawn(string flagUid) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00dd: 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_0109: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || string.IsNullOrEmpty(flagUid) || MainGame.WorldData?.gameSceneDataList == null) { return; } foreach (GameSceneData gameSceneData in MainGame.WorldData.gameSceneDataList) { if (gameSceneData?.wgoDataList == null) { continue; } foreach (WgoData wgoData in gameSceneData.wgoDataList) { if (!(((wgoData != null) ? wgoData.UniqueId : null) == (SGuid)null) && !(((ObjectLinkedToDefinition)(object)wgoData).id != "flag_stand_one_time") && wgoData.GameResStr != null && !(wgoData.GameResStr.Get("flag_stand_sguid", "") != flagUid)) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 5; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, wgoData.UniqueId.Id); CoopMessages.WriteString(ref writer, flagUid); CoopMessages.SendToReadyClients("GK2Coop.Troops", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Info("A fallen barricade left a flag stand (" + wgoData.UniqueId.Id + ") with flag " + flagUid + ": sent."); return; } } } } public unsafe static void OnFighterLook(Wgo wgo) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_004a: 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_0072: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || Replaying || (Object)(object)wgo == (Object)null) { return; } WgoData data = wgo.Data; if (!(((data != null) ? data.UniqueId : null) == (SGuid)null)) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 6; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, wgo.Data.UniqueId.Id); CoopMessages.SendToReadyClients("GK2Coop.Troops", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } } public static void Tick() { if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients) { MoveCarriedFlags(); } if (CoopSession.Role != CoopRole.Client) { return; } if (pendingStands.Count > 0) { foreach (string item in pendingStands.Keys.ToList()) { Pending pending = pendingStands[item]; if (TryHangFlag(item, pending.Other) || Time.unscaledTime > pending.Until) { pendingStands.Remove(item); } } } if (pendingLooks.Count <= 0) { return; } foreach (string item2 in pendingLooks.Keys.ToList()) { if (TryFighterLook(item2) || Time.unscaledTime > pendingLooks[item2]) { pendingLooks.Remove(item2); } } } private static void Replay(ulong carrier, string handlerType, string uid, string carried) { PlayerController val = CarrierController(carrier); Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(uid)); WGOInteractionHandlerBase val2 = (WGOInteractionHandlerBase)(((Object)(object)wgoViewGlobal != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val != (Object)null && val2 != null && ((object)val2).GetType().Name == handlerType) { PlayerController val3 = InteractorField.Invoke(val2); Replaying = true; WorldSync.ApplyingRemote = true; InteractorField.Invoke(val2) = val; try { val2.Interact(val); Applied++; } catch (Exception ex) { CoopLog.Warn("Replaying flag action " + handlerType + " on " + uid + " failed: " + ex.Message); } finally { InteractorField.Invoke(val2) = val3; WorldSync.ApplyingRemote = false; Replaying = false; } } else { CoopLog.Debug("Flag action " + handlerType + " on " + uid + ": " + (((Object)(object)wgoViewGlobal == (Object)null) ? "not loaded here" : (((Object)(object)val == (Object)null) ? "no character for that player here" : "different object here")) + "."); } if ((Object)(object)val != (Object)null && CarriedBy(val) != (carried ?? "")) { if ((Object)(object)val.attachedWgo != (Object)null) { val.RemoveTheFlag(); } Wgo val4 = (string.IsNullOrEmpty(carried) ? null : GameScene.GetWgoViewGlobal(SGuid.Parse(carried))); if ((Object)(object)val4 != (Object)null) { val.AttachTheFlag(val4); } CoopLog.Debug("Set what " + ((carrier == CoopNetwork.LocalClientId) ? "we" : $"player {carrier}") + " carry to '" + carried + "', as on the host."); } CoopLog.Info("Replayed " + handlerType + " on " + uid + " for " + ((carrier == CoopNetwork.LocalClientId) ? "us" : $"player {carrier}") + "; carrying '" + carried + "'."); } private static bool TryHangFlag(string standUid, string flagUid) { Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(standUid)); Wgo wgoViewGlobal2 = GameScene.GetWgoViewGlobal(SGuid.Parse(flagUid)); if ((Object)(object)wgoViewGlobal == (Object)null || (Object)(object)wgoViewGlobal2 == (Object)null) { return false; } FlagStandComponent componentInChildren = ((Component)wgoViewGlobal).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.AttachFlag(wgoViewGlobal2, false); } CoopLog.Info("Hung flag " + flagUid + " on the host's new stand " + standUid + "."); return true; } private static bool TryFighterLook(string uid) { Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(uid)); if ((Object)(object)wgoViewGlobal == (Object)null || !(wgoViewGlobal.Data is ZombieWgoData)) { return false; } Replaying = true; try { wgoViewGlobal.InitZombieFighter(); } finally { Replaying = false; } CoopLog.Debug("Gave " + ((ObjectLinkedToDefinition)(object)wgoViewGlobal.Data).id + " (" + uid + ") its fighter look, as on the host."); return true; } public static void OnTroops(ulong sender, FastBufferReader reader) { //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_007c: 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_00c2: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role == CoopRole.Client && !CoopSession.ClientInWorld) { return; } switch (b) { case 1: if (CoopSession.Role == CoopRole.Host) { string handlerType2 = CoopMessages.ReadString(ref reader); string uid2 = CoopMessages.ReadString(ref reader); RunFor(sender, handlerType2, uid2); } break; case 2: if (CoopSession.Role == CoopRole.Client) { ulong carrier = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref carrier, default(ForPrimitives)); string handlerType = CoopMessages.ReadString(ref reader); string uid = CoopMessages.ReadString(ref reader); string carried = CoopMessages.ReadString(ref reader); Replay(carrier, handlerType, uid, carried); } break; case 3: if (CoopSession.Role == CoopRole.Client) { ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); PlayerController val = CarrierController(num); if ((Object)(object)val != (Object)null && (Object)(object)val.attachedWgo != (Object)null) { val.RemoveTheFlag(); CoopLog.Info(((num == CoopNetwork.LocalClientId) ? "We" : $"Player {num}") + " dropped the flag (the host's game)."); } } break; case 4: if (CoopSession.Role == CoopRole.Client) { string text2 = CoopMessages.ReadString(ref reader); CoopRunner.Think(text2); CoopLog.Info("Flag action refused: " + text2); } break; case 5: if (CoopSession.Role == CoopRole.Client) { string text3 = CoopMessages.ReadString(ref reader); string text4 = CoopMessages.ReadString(ref reader); if (!TryHangFlag(text3, text4)) { pendingStands[text3] = new Pending { Other = text4, Until = Time.unscaledTime + 6f }; } } break; case 6: if (CoopSession.Role == CoopRole.Client) { string text = CoopMessages.ReadString(ref reader); if (!TryFighterLook(text)) { pendingLooks[text] = Time.unscaledTime + 6f; } } break; } } public static string Describe() { return $"sent={Sent} applied={Applied} pendingStands={pendingStands.Count} pendingLooks={pendingLooks.Count}"; } } [HarmonyPatch] internal static class TroopFlagActions { private static IEnumerable TargetMethods() { Type[] array = new Type[3] { typeof(FlagInteractionHandler), typeof(FlagStandInteractionHandler), typeof(BarricadeInteractionHandler) }; foreach (Type type in array) { MethodInfo methodInfo = AccessTools.Method(type, "Interact", new Type[1] { typeof(PlayerController) }, (Type[])null); if (methodInfo != null && methodInfo.DeclaringType == type) { yield return methodInfo; } } } private static bool Prefix(WGOInteractionHandlerBase __instance, PlayerController interactor, ref bool __result, out string __state) { __state = null; try { __state = TroopSync.TargetOf(__instance); return TroopSync.BeforeFlagInteract(__instance, interactor, ref __result); } catch (Exception e) { CoopLog.Error("Sending a flag action to the host failed", e); return true; } } private static void Postfix(WGOInteractionHandlerBase __instance, PlayerController interactor, bool __result, string __state, bool __runOriginal) { if (!__runOriginal) { return; } try { TroopSync.AfterFlagInteract(__instance, interactor, __result, __state); } catch (Exception e) { CoopLog.Error("Sending our flag action for replay failed", e); } } } [HarmonyPatch(typeof(FightingGameController), "ClearEnvironment")] internal static class DropFlagsAtFightEnd { private static void Prefix(out bool __state) { __state = (Object)(object)MainGame.PlayerController != (Object)null && (Object)(object)MainGame.PlayerController.attachedWgo != (Object)null; } private static void Postfix(bool __state) { try { TroopSync.AfterFightCleared(__state); } catch (Exception e) { CoopLog.Error("Dropping the partners' flags after the fight failed", e); } } } [HarmonyPatch(typeof(FlagPlacementPoint), "TrySpawnPlacementPointAndAttachFlag")] internal static class OneStandForAFallenBarricade { private static bool Prefix(FlagPlacementPoint __instance, out string __state) { __state = null; try { Wgo flagWgo = __instance.FlagWgo; object obj; if ((Object)(object)flagWgo != (Object)null) { WgoData data = flagWgo.Data; if (((data != null) ? data.UniqueId : null) != (SGuid)null) { obj = flagWgo.Data.UniqueId.Id; goto IL_0041; } } obj = null; goto IL_0041; IL_0041: __state = (string)obj; } catch { } return !TroopSync.ClientSkipsStandSpawn; } private static void Postfix(string __state, bool __runOriginal) { if (__runOriginal && __state != null) { TroopSync.AfterStandSpawn(__state); } } } [HarmonyPatch(typeof(Wgo), "InitZombieFighter")] internal static class MirrorFighterLook { private static void Postfix(Wgo __instance) { TroopSync.OnFighterLook(__instance); } } [HarmonyPatch(typeof(GlobalEventsSystem), "FireTrigger")] internal static class PartnerFlagsAreNotStorySteps { private static bool Prefix(Type type) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (TroopSync.RunningForPartner) { return (int)type != 26; } return true; } } internal static class TutorialSync { private const byte OpMode = 1; private const byte OpPage = 2; private const byte OpItem = 3; public const byte OpTools = 4; private const float PollInterval = 0.5f; private static bool sentMode; private static string sentSignature; private static float nextPoll; private static bool hostTutorial; public static int ItemsShared; private static readonly Regex IntroQuest = new Regex("^\\d+_intro_"); private static readonly Regex GuidedQuest = new Regex("^[123]_intro_"); private static bool guided; private static float guidedCheckedAt = -100f; private static bool introActive; private static float introCheckedAt = -100f; private static readonly FieldInfo DropCollectedField = AccessTools.Field(typeof(PlayerData), "OnDropCollected"); public static bool Active { get { if (CoopSession.Role == CoopRole.Host) { if (MainGame.PlayerData == null || !MainGame.PlayerData.isInTutorialMode) { return IntroActive(); } return true; } if (CoopSession.Role == CoopRole.Client) { if (!hostTutorial) { return IntroActive(); } return true; } return false; } } public static bool GuidedIntro { get { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Invalid comparison between Unknown and I4 //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Invalid comparison between Unknown and I4 if (Time.unscaledTime - guidedCheckedAt < 2f) { return guided; } guidedCheckedAt = Time.unscaledTime; guided = false; MainGame instance = MainGame.Instance; List list = ((instance == null) ? null : instance.GameSave?.questSystemData?.questCollection?.quests); if (list != null) { foreach (QuestData item in list) { if (item != null && !string.IsNullOrEmpty(((ObjectLinkedToDefinition)(object)item).id) && ((int)item.status == 2 || (int)item.status == 1) && GuidedQuest.IsMatch(((ObjectLinkedToDefinition)(object)item).id)) { guided = true; break; } } } return guided; } } public static void Reset() { sentMode = false; sentSignature = null; hostTutorial = false; ItemsShared = 0; StarterTools.Reset(); } public static bool IsStoryItem(Item item) { if (item == null || ((ObjectLinkedToDefinition)(object)item).id == null) { return false; } if (((ObjectLinkedToDefinition)(object)item).id.StartsWith("intro_") || ((ObjectLinkedToDefinition)(object)item).id.Contains("_quest")) { return true; } try { return ((ObjectLinkedToDefinition)(object)item).Definition != null && ((ObjectLinkedToDefinition)(object)item).Definition.isQuestItem; } catch { return false; } } public static bool SharedInIntro(Item item) { //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_0055: Invalid comparison between Unknown and I4 //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Invalid comparison between Unknown and I4 //IL_0057: 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_005b: Invalid comparison between Unknown and I4 //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_005d: 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_0062: Invalid comparison between Unknown and I4 //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Invalid comparison between Unknown and I4 //IL_0064: 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: Invalid comparison between Unknown and I4 if (item == null || ((ObjectLinkedToDefinition)(object)item).id == null || item.IsEmpty || IsStoryItem(item)) { return false; } ItemDef definition; try { definition = ((ObjectLinkedToDefinition)(object)item).Definition; } catch { return false; } if (definition == null) { return false; } if (definition.isTool || definition.isWeapon || definition.isBag) { return true; } ItemType type = definition.type; if ((int)type <= 23) { if (type - 1 <= 4 || type - 11 <= 1 || type - 22 <= 1) { goto IL_007f; } } else if ((int)type == 25 || (int)type == 50 || (int)type == 400) { goto IL_007f; } return false; IL_007f: return true; } private static bool IntroActive() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Invalid comparison between Unknown and I4 //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 if (Time.unscaledTime - introCheckedAt < 2f) { return introActive; } introCheckedAt = Time.unscaledTime; bool flag = false; MainGame instance = MainGame.Instance; List list = ((instance == null) ? null : instance.GameSave?.questSystemData?.questCollection?.quests); if (list != null) { foreach (QuestData item in list) { if (item != null && !string.IsNullOrEmpty(((ObjectLinkedToDefinition)(object)item).id) && ((int)item.status == 2 || (int)item.status == 1) && IntroQuest.IsMatch(((ObjectLinkedToDefinition)(object)item).id)) { flag = true; break; } } } if (flag != introActive) { CoopLog.Info(flag ? "The intro is under way: everyone gets its items." : "The intro is over: items aren't shared any more."); } introActive = flag; return introActive; } public static void Tick() { StarterTools.Tick(); if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || !CoopSession.IsInGame || Time.unscaledTime < nextPoll) { return; } nextPoll = Time.unscaledTime + 0.5f; PlayerData playerData = MainGame.PlayerData; if (playerData != null) { string text = playerData.isInTutorialMode + "|" + string.Join(",", playerData.tutorialModeExcludedList ?? new List()); if (!(text == sentSignature)) { sentSignature = text; SendMode(null); } } } public static void OnClientReady(ulong clientId) { SendMode(clientId); StarterTools.OnClientReady(clientId); } private unsafe static void SendMode(ulong? onlyTo) { //IL_001a: 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_0021: 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_0031: 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_0050: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) PlayerData playerData = MainGame.PlayerData; if (playerData == null) { return; } List list = playerData.tutorialModeExcludedList ?? new List(); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = (playerData.isInTutorialMode ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item in list) { CoopMessages.WriteString(ref writer, item ?? ""); } if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Tutorial", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Tutorial", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } if (playerData.isInTutorialMode != sentMode) { sentMode = playerData.isInTutorialMode; CoopLog.Info("Tutorial mode " + (sentMode ? "on" : "off") + " (sent to partners)."); } } public unsafe static void OnHostPage(UITutorialWindowData data) { //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_002e: 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_0038: 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_0056: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && data != null && !string.IsNullOrEmpty(data.Page) && !data.CanCloseFromAnyPage) { FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, data.Page); CoopMessages.SendToReadyClients("GK2Coop.Tutorial", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Tutorial page '" + data.Page + "' sent to partners."); } } public unsafe static void OnHostScriptItem(Item item, bool add) { //IL_005c: 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_0062: 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_006d: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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 (CoopSession.Role == CoopRole.Host && CoopSession.HasReadyClients && item != null && !item.IsEmpty && Active && SharedInIntro(item)) { byte[] bytes; try { bytes = SerializationUtility.SerializeValue(item, (DataFormat)0, (SerializationContext)null); } catch (Exception ex) { CoopLog.Warn("Couldn't send the tutorial item " + ((ObjectLinkedToDefinition)(object)item).id + ": " + ex.Message); return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = (add ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, bytes); CoopMessages.SendToReadyClients("GK2Coop.Tutorial", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } ItemsShared++; CoopLog.Info(string.Format("Tutorial: the story {0} {1} x{2}; partners {3} theirs too.", add ? "gave" : "took", ((ObjectLinkedToDefinition)(object)item).id, item.Count, add ? "get" : "lose")); } } public static void OnTutorial(ulong sender, FastBufferReader reader) { //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_003a: 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_004c: 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_015c: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (CoopSession.Role != CoopRole.Client) { return; } switch (b) { case 1: { byte b3 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b3, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List list = new List(); for (int i = 0; i < num; i++) { list.Add(CoopMessages.ReadString(ref reader)); } hostTutorial = b3 != 0; PlayerData playerData2 = MainGame.PlayerData; if (playerData2 != null && CoopSession.ClientInWorld) { playerData2.isInTutorialMode = hostTutorial; playerData2.tutorialModeExcludedList = list; CoopLog.Debug(string.Format("Tutorial mode {0}, {1} objects allowed.", hostTutorial ? "on" : "off", list.Count)); } break; } case 2: { string text = CoopMessages.ReadString(ref reader); if (CoopSession.ClientInWorld) { try { ((LazyWindow)(object)LazyUI.GetWindow())?.Open(new UITutorialWindowData(text, (Action)null, false)); CoopLog.Info("Showing the host's tutorial page '" + text + "'."); break; } catch (Exception ex) { CoopLog.Warn("Couldn't show tutorial page '" + text + "': " + ex.Message); break; } } break; } case 4: StarterTools.OnTools(ref reader); break; case 3: { byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); Item val = SerializationUtility.DeserializeValue(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null); PlayerData playerData = MainGame.PlayerData; if (val != null && playerData != null && CoopSession.ClientInWorld) { if (b2 != 0) { GiveToMe(val); } else { playerData.inventory.RemoveItemById(((ObjectLinkedToDefinition)(object)val).id, val.Count, (Item)null, (Item)null, false); } CoopLog.Info(string.Format("Tutorial item {0} x{1} {2} our inventory.", ((ObjectLinkedToDefinition)(object)val).id, val.Count, (b2 != 0) ? "added to" : "taken from")); } break; } } } public static void GiveToMe(Item item) { //IL_008e: 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_0053: 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) PlayerData playerData = MainGame.PlayerData; if (item == null || item.IsEmpty || playerData?.inventory == null) { return; } if (((ObjectLinkedToDefinition)(object)item).Definition != null && ((ObjectLinkedToDefinition)(object)item).Definition.CanItemBeEquipped() && StarterTools.Has(playerData, ((ObjectLinkedToDefinition)(object)item).Definition.type)) { CoopLog.Info($"Already have a {((ObjectLinkedToDefinition)(object)item).Definition.type}: not taking another {((ObjectLinkedToDefinition)(object)item).id}."); StarterTools.MarkHad(playerData, ((ObjectLinkedToDefinition)(object)item).Definition.type); return; } if (((ObjectLinkedToDefinition)(object)item).Definition != null) { StarterTools.MarkHad(playerData, ((ObjectLinkedToDefinition)(object)item).Definition.type); } if (EquipIfFree(playerData, item)) { ShowPickup(new List { item }); } else { List list = default(List); playerData.inventory.AddItemToInventory(item, ref list, (Item)null, false); ShowPickup((list != null && list.Count > 0) ? list : new List { item }); } } public static bool PartnerSkipsPickup(Item item) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) try { PlayerData playerData = MainGame.PlayerData; return CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && playerData != null && Active && SharedInIntro(item) && ((ObjectLinkedToDefinition)(object)item).Definition.CanItemBeEquipped() && StarterTools.Has(playerData, ((ObjectLinkedToDefinition)(object)item).Definition.type); } catch { return false; } } public static bool EquipIfFree(PlayerData data, Item item) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) try { ItemDef definition = ((ObjectLinkedToDefinition)(object)item).Definition; if (definition == null || !definition.CanItemBeEquipped()) { return false; } Item itemByType = data.toolBeltInventory.Data.GetItemByType(definition.type); if (itemByType != null && !itemByType.IsEmpty && ((ObjectLinkedToDefinition)(object)itemByType).id != "empty") { return false; } return data.toolBeltInventory.AddItemToInventory(item, (Item)null, false); } catch { return false; } } public static void ShowPickup(List items) { PlayerData playerData = MainGame.PlayerData; if (playerData == null || items == null || items.Count == 0) { return; } try { (DropCollectedField?.GetValue(playerData) as Action>)?.Invoke(items); } catch (Exception ex) { CoopLog.Debug("Couldn't show the pop-up for " + ((ObjectLinkedToDefinition)(object)items[0])?.id + ": " + ex.Message); CoopRunner.Toast("Got " + Name(((ObjectLinkedToDefinition)(object)items[0])?.id) + "."); } } private static string Name(string id) { try { string text = LLBase.L(id); return (string.IsNullOrEmpty(text) || text == id) ? id : text; } catch { return id; } } public static string Describe() { return $"tutorial={Active} itemsShared={ItemsShared}"; } } [HarmonyPatch(typeof(UITutorialWindow), "Open", new Type[] { typeof(UITutorialWindowData) })] internal static class MirrorTutorialWindow { private static void Postfix(UITutorialWindowData data) { try { TutorialSync.OnHostPage(data); } catch (Exception ex) { CoopLog.Debug("Couldn't mirror a tutorial page: " + ex.Message); } } } internal static class StarterTools { private static readonly ItemType[] Kinds; private const float PollInterval = 1f; private static readonly Dictionary hostHad; private static bool seeded; private static float nextPoll; private static readonly List> pending; private static float nextMark; public static void Reset() { hostHad.Clear(); seeded = false; nextPoll = 0f; nextMark = 0f; pending.Clear(); } public static bool IsStarterKind(Item item) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) try { return ((ObjectLinkedToDefinition)(object)item)?.Definition != null && Array.IndexOf(Kinds, ((ObjectLinkedToDefinition)(object)item).Definition.type) >= 0; } catch { return false; } } public static Item FindIn(Item container, ItemType kind, int depth = 0) { //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) //IL_0098: Unknown result type (might be due to invalid IL or missing references) List list = ((container != null) ? container.Inventory : null); if (list == null || depth > 3) { return null; } foreach (Item item in list) { if (item != null && !item.IsEmpty && ((ObjectLinkedToDefinition)(object)item).id != "empty" && ((ObjectLinkedToDefinition)(object)item).Definition != null && ((ObjectLinkedToDefinition)(object)item).Definition.type == kind) { return item; } } foreach (Item item2 in list) { if (item2 != null && item2.IsBag) { Item val = FindIn(item2, kind, depth + 1); if (val != null) { return val; } } } return null; } private static Item Find(PlayerData data, ItemType kind) { //IL_0012: 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) Inventory toolBeltInventory = data.toolBeltInventory; Item obj = FindIn((toolBeltInventory != null) ? toolBeltInventory.Data : null, kind); if (obj == null) { Inventory inventory = data.inventory; obj = FindIn((inventory != null) ? inventory.Data : null, kind); } return obj; } public static bool Has(PlayerData data, ItemType kind) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) if (data != null) { return Find(data, kind) != null; } return false; } private unsafe static string Marker(ItemType kind) { return "gk2coop_had_" + ((object)(*(ItemType*)(&kind))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); } public static void MarkHad(PlayerData data, ItemType kind) { //IL_0010: 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: Unknown result type (might be due to invalid IL or missing references) if (data != null && CoopSession.Role == CoopRole.Client && Array.IndexOf(Kinds, kind) >= 0 && !HadBefore(data, kind)) { data.SetResWithoutSystemsCheck(Marker(kind), 1f); } } private static bool HadBefore(PlayerData data, ItemType kind) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return data.GetRes(Marker(kind), 0f) > 0.5f; } public static void Tick() { //IL_00da: 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_006d: 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_00ef: 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_00fd: 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) if (CoopSession.Role == CoopRole.Client) { PlayerData playerData = MainGame.PlayerData; if (!CoopSession.ClientInWorld || playerData == null) { return; } if (pending.Count > 0) { List> tools = pending.ToList(); pending.Clear(); Apply(tools); } if (!(Time.unscaledTime >= nextMark)) { return; } nextMark = Time.unscaledTime + 2f; ItemType[] kinds = Kinds; foreach (ItemType kind in kinds) { if (Has(playerData, kind)) { MarkHad(playerData, kind); } } } else { if (CoopSession.Role != CoopRole.Host || !CoopSession.IsInGame || Time.unscaledTime < nextPoll) { return; } nextPoll = Time.unscaledTime + 1f; PlayerData playerData2 = MainGame.PlayerData; if (playerData2 == null) { return; } List> list = new List>(); ItemType[] kinds = Kinds; foreach (ItemType val in kinds) { Item val2 = Find(playerData2, val); if (val2 != null && !hostHad.ContainsKey(val)) { hostHad[val] = ((ObjectLinkedToDefinition)(object)val2).id; if (seeded) { list.Add(new KeyValuePair(val, ((ObjectLinkedToDefinition)(object)val2).id)); } } } seeded = true; if (list.Count > 0 && CoopSession.HasReadyClients) { Send(list, null); CoopLog.Info("Our first " + string.Join(", ", list.Select((KeyValuePair t) => t.Value)) + ": partners without one get theirs."); } } } public static void OnClientReady(ulong clientId) { //IL_001d: 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_002e: Unknown result type (might be due to invalid IL or missing references) PlayerData playerData = MainGame.PlayerData; if (playerData == null) { return; } List> list = new List>(); ItemType[] kinds = Kinds; foreach (ItemType val in kinds) { Item val2 = Find(playerData, val); if (val2 != null) { list.Add(new KeyValuePair(val, ((ObjectLinkedToDefinition)(object)val2).id)); } } if (list.Count > 0) { Send(list, clientId); } } private unsafe static void Send(List> tools, ulong? onlyTo) { //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_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_0010: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected I4, but got Unknown //IL_0058: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); int count = tools.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (KeyValuePair tool in tools) { count = (int)tool.Key; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); CoopMessages.WriteString(ref writer, tool.Value); } if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Tutorial", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Tutorial", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public static void OnTools(ref FastBufferReader reader) { //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_0020: 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) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List> list = new List>(); int num2 = default(int); for (int i = 0; i < num; i++) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); list.Add(new KeyValuePair((ItemType)num2, CoopMessages.ReadString(ref reader))); } if (CoopSession.Role == CoopRole.Client) { if (!CoopSession.ClientInWorld || MainGame.PlayerData == null) { pending.AddRange(list); } else { Apply(list); } } } private static void Apply(List> tools) { //IL_0034: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_0091: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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) PlayerData playerData = MainGame.PlayerData; List list = new List(); foreach (KeyValuePair tool in tools) { if (string.IsNullOrEmpty(tool.Value)) { continue; } if (Has(playerData, tool.Key)) { MarkHad(playerData, tool.Key); continue; } if (HadBefore(playerData, tool.Key)) { CoopLog.Info($"The host has a {tool.Key}; we had one before (not on us now): not getting another."); continue; } try { Item val = new Item(tool.Value, 1); if (((ObjectLinkedToDefinition)(object)val).Definition != null) { if (!TutorialSync.EquipIfFree(playerData, val) && !playerData.inventory.AddItemToInventory(val, (Item)null, false)) { MainGame.Instance.dropSystem.DropItem(val, playerData.currentGameSceneId, ((Component)MainGame.PlayerController.PhysicalBody).transform.position, (List)null); } list.Add(val); MarkHad(playerData, tool.Key); CoopLog.Info($"The host has a {tool.Key} ({tool.Value}) and we had none: got ours."); } } catch (Exception ex) { CoopLog.Warn("Couldn't get our own " + tool.Value + ": " + ex.Message); } } TutorialSync.ShowPickup(list); } static StarterTools() { ItemType[] array = new ItemType[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); Kinds = (ItemType[])(object)array; hostHad = new Dictionary(); pending = new List>(); } } [HarmonyPatch(typeof(Flow_AddItem), "ChangeGameRes")] internal static class ShareTutorialItems { private static readonly FieldRef> ItemField = AccessTools.FieldRefAccess>("item"); private static readonly FieldRef> IdField = AccessTools.FieldRefAccess>("itemId"); private static readonly FieldRef> CountField = AccessTools.FieldRefAccess>("itemCount"); private static void Prefix(Flow_AddItem __instance) { //IL_0054: 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: Invalid comparison between Unknown and I4 try { if (__instance.addToPlayer && TutorialSync.Active && CoopSession.Role == CoopRole.Host) { Item val = (Item)((!__instance.instanceAsInput) ? ((object)new Item(IdField.Invoke(__instance)?.value, CountField.Invoke(__instance)?.value ?? 1)) : ((object)ItemField.Invoke(__instance)?.value)); if (val != null) { TutorialSync.OnHostScriptItem((Item)(__instance.instanceAsInput ? ((object)/*isinst with value type is only supported in some contexts*/) : ((object)val)), (int)__instance.giveType == 0); } } } catch (Exception ex) { CoopLog.Debug("Couldn't share a tutorial item: " + ex.Message); } } } internal static class FightGear { private const string StarterArmor = "armor_0"; private const string StarterSword = "sword_0"; private static bool Equipped(PlayerData data, ItemType type) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) Item itemByType = data.toolBeltInventory.Data.GetItemByType(type); if (itemByType != null && !itemByType.IsEmpty) { return ((ObjectLinkedToDefinition)(object)itemByType).id != "empty"; } return false; } private static Item Carried(PlayerData data, ItemType type) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = data.inventory; return StarterTools.FindIn((inventory != null) ? inventory.Data : null, type); } private static void EquipFromBag(PlayerData data, Item item) { data.inventory.RemoveItemFromInventoryByUID(item, -1); data.toolBeltInventory.AddItemToInventory(item, (Item)null, false); } public static string EnsureOwn(PlayerController controller, bool withSword) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown if (CoopSession.Role != CoopRole.Client || ((controller != null) ? controller.PlayerData : null) == null) { return ""; } PlayerData playerData = controller.PlayerData; List list = new List(); List list2 = new List(); try { if (!Equipped(playerData, (ItemType)12)) { Item val = Carried(playerData, (ItemType)12); if (val != null) { EquipFromBag(playerData, val); CoopLog.Info("Put on our own armor from the bag (" + ((ObjectLinkedToDefinition)(object)val).id + ")."); } else { Item val2 = new Item("armor_0", 1); playerData.EquipItem(val2); list2.Add(val2); list.Add("armor"); CoopLog.Info("We had no armor of our own: got the intro's (armor_0)."); } } if (withSword && !Equipped(playerData, (ItemType)11) && !Equipped(playerData, (ItemType)22)) { Item val3 = Carried(playerData, (ItemType)11) ?? Carried(playerData, (ItemType)22); if (val3 != null) { EquipFromBag(playerData, val3); CoopLog.Info("Equipped our own weapon from the bag (" + ((ObjectLinkedToDefinition)(object)val3).id + ")."); } else { Item val4 = new Item("sword_0", 1); playerData.EquipItem(val4); list2.Add(val4); list.Add("a sword"); CoopLog.Info("We had no weapon of our own: got the intro's sword (sword_0)."); } } } catch (Exception e) { CoopLog.Error("Getting our own fight gear failed", e); } TutorialSync.ShowPickup(list2); return string.Join(" and ", list); } public static bool DrawWeapon(PlayerController controller) { Item sword = controller.Sword; Item bow = controller.Bow; ItemDef val = ((sword != null && ((ObjectLinkedToDefinition)(object)sword).id != "empty") ? ((ObjectLinkedToDefinition)(object)sword).Definition : ((bow != null && ((ObjectLinkedToDefinition)(object)bow).id != "empty") ? ((ObjectLinkedToDefinition)(object)bow).Definition : null)); if (val == null) { CoopRunner.Think("I have no sword or bow equipped: I can't fight."); CoopLog.Warn("No weapon equipped: not drawing one (an empty one breaks attacking)."); return false; } controller.AttackComponent.EquipWeapon(val); return true; } } internal static class VendorSync { private const byte OpVendor = 1; private const float PollInterval = 2f; private static readonly FieldRef IconField = AccessTools.FieldRefAccess("icon"); private static readonly FieldRef CurMoneyField = AccessTools.FieldRefAccess("curMoney"); private static readonly FieldRef CurTierField = AccessTools.FieldRefAccess("curTier"); private static readonly FieldRef UsedHappinessField = AccessTools.FieldRefAccess("usedHappinessThisWeek"); private static readonly FieldRef SoldWithHappinessField = AccessTools.FieldRefAccess("soldItemsWithHappinessThisWeek"); private static readonly Dictionary signatures = new Dictionary(); private static string openVendorId; private static float nextPoll; public static int Sent; public static int Applied; private static List Vendors { get { MainGame instance = MainGame.Instance; if (instance == null) { return null; } return instance.GameSave?.vendorSystem?.vendors; } } public static void Reset() { signatures.Clear(); openVendorId = null; nextPoll = 0f; Sent = 0; Applied = 0; } public static Vendor FindVendorForNpc(WgoData npc) { if (npc == null || ((ObjectLinkedToDefinition)(object)npc).id == null || Vendors == null) { return null; } string text = (((ObjectLinkedToDefinition)(object)npc).id.StartsWith("npc_") ? ((ObjectLinkedToDefinition)(object)npc).id.Substring(4) : ((ObjectLinkedToDefinition)(object)npc).id); foreach (Vendor vendor in Vendors) { if (vendor != null && ((ObjectLinkedToDefinition)(object)vendor).id != null && (((((ObjectLinkedToDefinition)(object)vendor).Definition != null) ? IconField.Invoke(((ObjectLinkedToDefinition)(object)vendor).Definition) : null) == ((ObjectLinkedToDefinition)(object)npc).id || ((ObjectLinkedToDefinition)(object)vendor).id == ((ObjectLinkedToDefinition)(object)npc).id || ((ObjectLinkedToDefinition)(object)vendor).id == text)) { return vendor; } } return null; } public static bool TryOpenShop(WgoData npc) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0025: Unknown result type (might be due to invalid IL or missing references) Vendor val = FindVendorForNpc(npc); if (val == null) { return false; } try { UIVendorWindowData val2 = new UIVendorWindowData(); string vendorId = ((ObjectLinkedToDefinition)(object)val).id; new Trading().FillVendorWindowData(val2, vendorId, (Action)delegate { OnShopClosed(vendorId); }); ((LazyWindow)(object)LazyUI.GetWindow()).Open(val2); openVendorId = vendorId; CoopLog.Info("Opened " + vendorId + "'s shop locally (talking and story with them happens with the host)."); return true; } catch (Exception e) { CoopLog.Error("Opening " + ((ObjectLinkedToDefinition)(object)val).id + "'s shop failed", e); return false; } } private static void OnShopClosed(string vendorId) { openVendorId = null; Vendor val = Vendors?.FirstOrDefault((Func)((Vendor v) => ((ObjectLinkedToDefinition)(object)v).id == vendorId)); if (val != null && CoopSession.Role == CoopRole.Client) { signatures[vendorId] = Signature(val); Send(val, null); CoopLog.Info("Closed " + vendorId + "'s shop; sent the vendor's stock and money to the host."); } } private static int Signature(Vendor vendor) { int num = 17; num = num * 31 + CurMoneyField.Invoke(vendor); num = num * 31 + CurTierField.Invoke(vendor); num = num * 31 + Mathf.RoundToInt(UsedHappinessField.Invoke(vendor) * 100f); Inventory inventory = vendor.Inventory; object obj; if (inventory == null) { obj = null; } else { Item data = inventory.Data; obj = ((data != null) ? data.Inventory : null); } List list = (List)obj; if (list != null) { foreach (Item item in list) { num = num * 31 + (((ObjectLinkedToDefinition)(object)item)?.id?.GetHashCode()).GetValueOrDefault(); num = num * 31 + ((item != null) ? item.Count : 0); } } return num; } public static void Tick() { if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || Vendors == null || Time.unscaledTime < nextPoll) { return; } nextPoll = Time.unscaledTime + 2f; foreach (Vendor vendor in Vendors) { if (vendor != null && ((ObjectLinkedToDefinition)(object)vendor).id != null) { int num = Signature(vendor); int value; bool num2 = signatures.TryGetValue(((ObjectLinkedToDefinition)(object)vendor).id, out value); signatures[((ObjectLinkedToDefinition)(object)vendor).id] = num; if (num2 && value != num) { Send(vendor, null); } } } } public static void OnClientReady(ulong clientId) { if (Vendors == null) { return; } foreach (Vendor item in Vendors.Where((Vendor v) => ((ObjectLinkedToDefinition)(object)v)?.id != null)) { signatures[((ObjectLinkedToDefinition)(object)item).id] = Signature(item); Send(item, clientId); } } private unsafe static void Send(Vendor vendor, ulong? onlyTo, ulong? except = null) { //IL_0066: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_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_00a1: 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_00bd: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_0122: Unknown result type (might be due to invalid IL or missing references) byte[] bytes; byte[] bytes2; try { Inventory inventory = vendor.Inventory; object obj; if (inventory == null) { obj = null; } else { Item data = inventory.Data; obj = ((data != null) ? data.Inventory : null); } if (obj == null) { obj = new List(); } bytes = SerializationUtility.SerializeValue>((List)obj, (DataFormat)0, (SerializationContext)null); bytes2 = SerializationUtility.SerializeValue(SoldWithHappinessField.Invoke(vendor), (DataFormat)0, (SerializationContext)null); } catch (Exception e) { CoopLog.Error("Serializing vendor " + ((ObjectLinkedToDefinition)(object)vendor).id + " failed", e); return; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, ((ObjectLinkedToDefinition)(object)vendor).id); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref CurMoneyField.Invoke(vendor), default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref CurTierField.Invoke(vendor), default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref UsedHappinessField.Invoke(vendor), default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, bytes); CoopMessages.WriteBytes(ref writer, bytes2); if (CoopSession.Role == CoopRole.Client) { CoopMessages.SendToHost("GK2Coop.Vendors", writer, (NetworkDelivery)3); } else if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.Vendors", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.Vendors", writer, (NetworkDelivery)4, except); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; } public static void OnVendors(ulong sender, FastBufferReader reader) { //IL_000c: 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_0031: 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_0044: 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_0057: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (b != 1) { return; } string id = CoopMessages.ReadString(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); float num3 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); byte[] array = CoopMessages.ReadBytes(ref reader); byte[] array2 = CoopMessages.ReadBytes(ref reader); if (CoopSession.Role == CoopRole.Client && (!CoopSession.ClientInWorld || openVendorId == id)) { return; } Vendor val = Vendors?.FirstOrDefault((Func)((Vendor v) => ((ObjectLinkedToDefinition)(object)v).id == id)); if (val == null) { return; } try { CurMoneyField.Invoke(val) = num; CurTierField.Invoke(val) = num2; UsedHappinessField.Invoke(val) = num3; GameRes val2 = SerializationUtility.DeserializeValue(array2, (DataFormat)0, (DeserializationContext)null); if (val2 != (GameRes)null) { SoldWithHappinessField.Invoke(val) = val2; } List list = SerializationUtility.DeserializeValue>(array, (DataFormat)0, (DeserializationContext)null) ?? new List(); Inventory inventory = val.Inventory; Item val3 = ((inventory != null) ? inventory.Data : null); if (val3 != null) { val3.RemoveAllItems(); if (list.Count > 0) { val3.AddItemsToInventory(list); } } signatures[id] = Signature(val); Applied++; CoopLog.Debug(string.Format("Vendor {0} updated from {1}: money {2}, {3} stacks.", id, (CoopSession.Role == CoopRole.Host) ? $"client {sender}" : "host", num, list.Count)); if (CoopSession.Role == CoopRole.Host) { CoopLog.Info((PartnerProfiles.NameOf(sender) ?? $"Player {sender}") + " traded with " + id + "."); Send(val, null, sender); } } catch (Exception e) { CoopLog.Error("Applying vendor " + id + " failed", e); } } public static string Describe() { return $"tracked={signatures.Count} sent={Sent} applied={Applied} open='{openVendorId}'"; } } [HarmonyPatch(typeof(WorldData), "RemoveWgoDataFromGameScene", new Type[] { typeof(WgoData), typeof(bool) })] internal static class MirrorRemoveByData { private static void Prefix(WorldData __instance, WgoData wgoData, out WgoData __state) { __state = ((!WorldSync.InCompound && ((wgoData != null) ? wgoData.UniqueId : null) != (SGuid)null && __instance.GetWgoData(wgoData.UniqueId) != null) ? wgoData : null); } private static void Postfix(WgoData __state) { if (__state != null) { WorldSync.OnLocalChange(WorldSync.Op.Remove, __state.UniqueId.Id, "", "", __state); } } } [HarmonyPatch(typeof(WorldData), "RemoveWgoDataFromGameScene", new Type[] { typeof(SGuid) })] internal static class MirrorRemoveById { private static void Prefix(WorldData __instance, SGuid sGuid, out WgoData __state) { __state = ((!WorldSync.InCompound && sGuid != (SGuid)null) ? __instance.GetWgoData(sGuid) : null); } private static void Postfix(WgoData __state) { if (__state != null) { WorldSync.OnLocalChange(WorldSync.Op.Remove, __state.UniqueId.Id, "", "", __state); } } } [HarmonyPatch(typeof(WorldData), "ReplaceWgoData")] internal static class MirrorReplace { private static void Prefix(WgoData wgoData, out string __state) { object obj; if (wgoData == null) { obj = null; } else { SGuid uniqueId = wgoData.UniqueId; obj = ((uniqueId != null) ? uniqueId.Id : null); } __state = (string)obj; WorldSync.BeginCompound(); } private static void Postfix(string newWgoId, WgoData __result, string __state) { WorldSync.EndCompound(); if (__state != null && ((__result != null) ? __result.UniqueId : null) != (SGuid)null) { WorldSync.OnLocalChange(WorldSync.Op.Replace, __state, newWgoId, __result.UniqueId.Id, __result); } } private static void Finalizer(Exception __exception) { if (__exception != null) { WorldSync.EndCompound(); } } } [HarmonyPatch(typeof(WorldData), "ChangeWgoData")] internal static class MirrorChange { private static void Prefix(WgoData wgoData, out string __state) { object obj; if (wgoData == null) { obj = null; } else { SGuid uniqueId = wgoData.UniqueId; obj = ((uniqueId != null) ? uniqueId.Id : null); } __state = (string)obj; WorldSync.BeginCompound(); } private static void Postfix(WgoData wgoData, string newWgoId, string __state) { WorldSync.EndCompound(); if (__state != null) { WorldSync.OnLocalChange(WorldSync.Op.Change, __state, newWgoId, "", wgoData); } } private static void Finalizer(Exception __exception) { if (__exception != null) { WorldSync.EndCompound(); } } } [HarmonyPatch(typeof(WorldData), "MoveWgoDataToAnotherGameScene")] internal static class MirrorMove { private static void Prefix(WgoData wgoData, out string __state) { object obj; if (wgoData == null) { obj = null; } else { SGuid uniqueId = wgoData.UniqueId; obj = ((uniqueId != null) ? uniqueId.Id : null); } __state = (string)obj; WorldSync.BeginCompound(); } private static void Postfix(WgoData wgoData, string gameSceneIdTo, string __state) { WorldSync.EndCompound(); if (__state != null) { WorldSync.OnLocalChange(WorldSync.Op.Move, __state, gameSceneIdTo, "", wgoData); } } private static void Finalizer(Exception __exception) { if (__exception != null) { WorldSync.EndCompound(); } } } [HarmonyPatch(typeof(WorldData), "AddWgoData", new Type[] { typeof(WgoData) })] internal static class MirrorAdd { private static void Postfix(WgoData data) { if (((data != null) ? data.UniqueId : null) != (SGuid)null) { WorldSync.OnLocalChange(WorldSync.Op.Add, data.UniqueId.Id, ((ObjectLinkedToDefinition)(object)data).id, data.WorldId, data); } } } [HarmonyPatch(typeof(WorldData), "AddWgoData", new Type[] { typeof(WgoData), typeof(bool) })] internal static class MirrorAddRecheck { private static void Postfix(WgoData data) { if (((data != null) ? data.UniqueId : null) != (SGuid)null) { WorldSync.OnLocalChange(WorldSync.Op.Add, data.UniqueId.Id, ((ObjectLinkedToDefinition)(object)data).id, data.WorldId, data); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class MirrorAddById { private static void Postfix(bool __result, ref WgoData wgoData) { if (__result) { WgoData obj = wgoData; if (((obj != null) ? obj.UniqueId : null) != (SGuid)null) { WorldSync.OnLocalChange(WorldSync.Op.Add, wgoData.UniqueId.Id, ((ObjectLinkedToDefinition)(object)wgoData).id, wgoData.WorldId, wgoData); } } } } [HarmonyPatch(typeof(Wgo), "ForceDeath")] internal static class ForwardDeathFromView { private static bool Prefix(Wgo __instance) { WgoData data = __instance.Data; if (data == null || !WorldSync.ForwardDeath(data, fromView: true)) { return true; } data.ForceDeath(); return false; } } [HarmonyPatch(typeof(WgoData), "ForceDeath")] internal static class ForwardDeathFromData { private static void Prefix(WgoData __instance) { WorldSync.ForwardDeath(__instance); } } [HarmonyPatch(typeof(WgoData), "HandleDeath")] internal static class ForwardDeathFromCombat { private static void Prefix(WgoData __instance) { WorldSync.ForwardDeath(__instance); } } [HarmonyPatch(typeof(Inventory), "NotifyItemsAdded")] internal static class ContainerAddsGoOut { private static void Postfix(Inventory __instance) { ContainerSync.OnInventoryEvent(__instance); } } [HarmonyPatch(typeof(Inventory), "NotifyItemsRemoved")] internal static class ContainerRemovesGoOut { private static void Postfix(Inventory __instance) { ContainerSync.OnInventoryEvent(__instance); } } [HarmonyPatch(typeof(GameSceneData), "AddDrop", new Type[] { typeof(DropData) })] internal static class MirrorDropAdd { private static void Postfix(GameSceneData __instance, DropData drop) { try { DropSync.OnLocalAdd(__instance.id, drop); } catch (Exception e) { CoopLog.Error("Mirroring a drop failed", e); } } } [HarmonyPatch(typeof(GameSceneData), "RemoveDrop")] internal static class MirrorDropRemove { private static void Postfix(GameSceneData __instance, DropData drop) { try { DropSync.OnLocalRemove(__instance.id, drop); } catch (Exception e) { CoopLog.Error("Mirroring a pickup failed", e); } } } [HarmonyPatch] internal static class HostRunsWorldSystems { private static IEnumerable TargetMethods() { Type[] array = new Type[10] { typeof(NPCLifeSimulator), typeof(ZombieSystem), typeof(ZombiePorterSystem), typeof(WgoDelayedSpawnSystem), typeof(WgoDelayedEventsSystem), typeof(WgoCustomDeathSystem), typeof(ConveyorSystem), typeof(CraftSystem), typeof(GameLogicsSystem), typeof(DropSystem) }; foreach (Type type in array) { yield return AccessTools.Method(type, "CustomUpdate", new Type[1] { typeof(float) }, (Type[])null); } } private static bool Prefix() { return !NpcSync.ClientFollowsHost; } } [HarmonyPatch(typeof(MovementSystem), "CustomUpdate")] internal static class HostRunsMovement { private static bool Prefix(float deltaTime) { if (!NpcSync.ClientFollowsHost) { return true; } List list = MainGame.Instance.GameSave?.movementSystemData?.movingObjects; if (list != null) { for (int i = 0; i < list.Count; i++) { if (NpcSync.IsWisp(list[i]) || SermonSync.IsActorMover(list[i])) { list[i].Update(deltaTime); } } } return false; } } [HarmonyPatch(typeof(WgoPartData), "TryApplyState")] internal static class MirrorPartState { private static void Prefix(WgoPartData __instance, out int __state) { __state = __instance.GetStateHash(); } private static void Postfix(WgoPartData __instance, bool __result, SGuid parent, int __state) { if (__result && parent != (SGuid)null && __instance.GetStateHash() != __state) { WorldSync.MarkStateDirty(parent); } } } [HarmonyPatch] internal static class MirrorObjectValues { private static IEnumerable TargetMethods() { MethodInfo[] methods = typeof(WgoData).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "SetGameRes" || methodInfo.Name == "AddGameRes" || methodInfo.Name == "SubGameRes" || methodInfo.Name == "MultiplyGameRes") { yield return methodInfo; } } } private static void Postfix(WgoData __instance) { WorldSync.OnResChanged(__instance); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class MirrorHiddenState { private static void Prefix(WgoData __instance, out bool __state) { __state = __instance.IsHidden; } private static void Postfix(WgoData __instance, bool __state) { if (__instance.IsHidden != __state) { WorldSync.MarkStateDirty(__instance); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class MirrorInteractableState { private static void Prefix(WgoData __instance, out bool __state) { __state = __instance.IsInteractable; } private static void Postfix(WgoData __instance, bool __state) { if (__instance.IsInteractable != __state) { WorldSync.MarkStateDirty(__instance); } } } [HarmonyPatch(typeof(WgoData), "AddWgoPart")] internal static class MirrorPartAdded { private static void Postfix(WgoData __instance) { WorldSync.MarkStateDirty(__instance); } } [HarmonyPatch(typeof(WgoData), "RemoveWgoPart")] internal static class MirrorPartRemoved { private static void Postfix(WgoData __instance) { WorldSync.MarkStateDirty(__instance); } } [HarmonyPatch] internal static class MirrorWorkbenchLinks { private static IEnumerable TargetMethods() { string[] array = new string[4] { "AddWorkbenchExtension", "AddWorkbenchParent", "RemoveWorkbenchExtension", "RemoveWorkbenchParent" }; foreach (string text in array) { yield return AccessTools.Method(typeof(WgoData), text, new Type[1] { typeof(SGuid) }, (Type[])null); } } private static void Postfix(WgoData __instance) { WorldSync.MarkStateDirty(__instance); } } internal static class WorldStateSync { private delegate void WriteBody(FastBufferWriter writer); private const byte OpWeather = 1; private const byte OpTown = 2; private const float PollInterval = 1f; private const float ResendInterval = 10f; private static string lastWeather; private static int lastTownQuality = int.MinValue; private static float nextPoll; private static float nextResend; public static bool ClientFollowsHost { get { if (CoopSession.Role == CoopRole.Client) { return CoopSession.ClientInWorld; } return false; } } public static void Reset() { lastWeather = null; lastTownQuality = int.MinValue; nextPoll = 0f; nextResend = 0f; } public static void OnClientReady() { lastWeather = null; lastTownQuality = int.MinValue; } public static void Tick() { if (CoopSession.Role != CoopRole.Host || !CoopSession.HasReadyClients || !CoopSession.IsInGame || Time.unscaledTime < nextPoll) { return; } nextPoll = Time.unscaledTime + 1f; bool flag = Time.unscaledTime >= nextResend; if (flag) { nextResend = Time.unscaledTime + 10f; } WeatherData weather = MainGame.Instance.GameSave.weatherData; if (weather != null) { List components = (weather.enabledWeatherComponents ?? new List()).ToList(); string text = weather.stateName + "|" + string.Join(",", components); if (flag || text != lastWeather) { if (text != lastWeather) { CoopLog.Info("Weather on host: " + weather.stateName + " [" + string.Join(", ", components) + "]"); } lastWeather = text; Send(delegate(FastBufferWriter w) { //IL_0008: 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_003d: 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) byte b = 1; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref w, weather.stateName); int count = components.Count; ((FastBufferWriter)(ref w)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item in components) { CoopMessages.WriteString(ref w, item); } }); } } TownSystem town = MainGame.Instance.GameSave.townSystem; if (town != null && (flag || town.Quality != lastTownQuality)) { lastTownQuality = town.Quality; Send(delegate(FastBufferWriter w) { //IL_0008: 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_0026: 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) byte b = 2; ((FastBufferWriter)(ref w)).WriteValueSafe(ref b, default(ForPrimitives)); int quality = town.Quality; ((FastBufferWriter)(ref w)).WriteValueSafe(ref quality, default(ForPrimitives)); }); } } private unsafe static void Send(WriteBody body) { //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_0012: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter writer = CoopMessages.NewWriter(); try { body(writer); CoopMessages.SendToReadyClients("GK2Coop.WorldState", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } } public static void OnWorldState(ulong sender, FastBufferReader reader) { //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_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) //IL_006f: 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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 1: { string state = CoopMessages.ReadString(ref reader); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); List list = new List(); for (int i = 0; i < num2; i++) { list.Add(CoopMessages.ReadString(ref reader)); } if (ClientFollowsHost) { ApplyWeather(state, list); } break; } case 2: { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); MainGame instance = MainGame.Instance; TownSystem val = ((instance == null) ? null : instance.GameSave?.townSystem); if (ClientFollowsHost && val != null && val.Quality != num) { CoopLog.Info($"Town quality {val.Quality} -> {num} (host)."); val.Quality = num; } break; } } } private static void ApplyWeather(string state, List components) { WeatherSystem instance = WeatherSystem.Instance; MainGame instance2 = MainGame.Instance; WeatherData val = ((instance2 == null) ? null : instance2.GameSave?.weatherData); if ((Object)(object)instance == (Object)null || val == null) { return; } try { List mine = (val.enabledWeatherComponents ?? new List()).ToList(); foreach (string item in components.Where((string c) => !mine.Contains(c))) { instance.SetWeatherComponent(item, true); } foreach (string item2 in mine.Where((string c) => !components.Contains(c))) { instance.SetWeatherComponent(item2, false); } if (!string.IsNullOrEmpty(state) && val.stateName != state) { CoopLog.Info("Weather " + val.stateName + " -> " + state + " (host)."); instance.SetWeatherState(state, false); } } catch (Exception e) { CoopLog.Error("Applying the host's weather '" + state + "' failed", e); } } } [HarmonyPatch(typeof(WeatherSystem), "RollNextWeatherState")] internal static class HostRollsWeather { private static bool Prefix() { return !WorldStateSync.ClientFollowsHost; } } [HarmonyPatch(typeof(VendorSystem), "UpdateSystemAtTheEndOfDay")] internal static class HostRestocksVendors { private static bool Prefix() { return !WorldStateSync.ClientFollowsHost; } } internal static class WorldSync { public enum Op : byte { Remove = 1, Replace, Change, Move, Add, Kill, State } private struct Change { public Op Op; public string UniqueId; public string A; public string B; public string C; public Vector3 Position; public Vector2 Direction; public byte[] Full; } private struct PartState { public string Id; public string Variation; public int Rotation; } private class ResValues { public List> Numbers = new List>(); public List> Texts = new List>(); } private static readonly List pendingWhileLoading = new List(); private static readonly Dictionary recentKills = new Dictionary(); private static int compoundDepth; public static bool ApplyingRemote; public static int Sent; public static int Applied; public static int Missing; private static readonly Dictionary pendingAdds = new Dictionary(); private static readonly List pendingAddOrder = new List(); private static readonly Dictionary stateDirty = new Dictionary(); private static readonly HashSet stateChangedEver = new HashSet(); private static readonly Dictionary justAdded = new Dictionary(); private static readonly Dictionary heldBackLogged = new Dictionary(); private static readonly Dictionary lastStateSent = new Dictionary(); private static readonly Dictionary deferredStates = new Dictionary(); private static readonly Dictionary stateSceneSentTo = new Dictionary(); private static bool applyingState; public static int StatesSent; public static int StatesApplied; private static readonly FieldRef GameResField = AccessTools.FieldRefAccess("gameRes"); private static readonly Dictionary resDirty = new Dictionary(); private const byte OpRes = 8; private static float nextResFlush; private static readonly FieldRef WorkerIdField = AccessTools.FieldRefAccess("workerId"); private static readonly FieldInfo WorkerChangedField = AccessTools.Field(typeof(WgoData), "OnWorkerChanged"); private static readonly FieldRef DeathTriggeredField = AccessTools.FieldRefAccess("customDeathWasTriggered"); private static readonly FieldRef DeathCallbackField = AccessTools.FieldRefAccess("customDeathCallback"); private static readonly MethodInfo RunAfterDeathMethod = AccessTools.Method(typeof(WgoData), "RunLogicsAfterDeath", (Type[])null, (Type[])null); private static WgoData deathRan; public static bool InCompound => compoundDepth > 0; public static bool ShouldBroadcast { get { if (CoopSession.Role == CoopRole.Host) { return CoopSession.HasReadyClients; } return false; } } private static bool ClientCanForward { get { if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld) { return !ApplyingRemote; } return false; } } public static bool ClientBuilding { get { if (ClientCanForward && (Object)(object)BuildController.Instance != (Object)null) { return BuildController.Instance.IsBuildModeActive; } return false; } } public static void BeginCompound() { compoundDepth++; } public static void EndCompound() { compoundDepth = Math.Max(0, compoundDepth - 1); } public static void OnLocalChange(Op op, string uniqueId, string a = "", string b = "", WgoData data = null) { if (string.IsNullOrEmpty(uniqueId) || InCompound) { return; } if (op == Op.Add && CoopSession.Role == CoopRole.Client && !ApplyingRemote) { SermonSync.NoteLocalAdd(uniqueId, ((ObjectLinkedToDefinition)(object)data)?.id ?? a); } bool num = CoopSession.Role == CoopRole.Host && ShouldBroadcast; bool flag = CoopSession.Role == CoopRole.Client && ClientBuilding && op != Op.Move; if (num || flag) { if (op == Op.Add && data != null) { if (!pendingAdds.ContainsKey(uniqueId)) { pendingAddOrder.Add(uniqueId); } pendingAdds[uniqueId] = data; } else { FlushAdd(uniqueId); Send(op, uniqueId, a, b, data, null); } if (flag) { CoopLog.Info($"Sent our build-mode {op} of {((ObjectLinkedToDefinition)(object)data)?.id ?? uniqueId} to the host."); } } if (op == Op.Add && data != null) { justAdded[uniqueId] = Time.unscaledTime + 2f; MarkStateDirty(data); } } private static void FlushAdd(string uniqueId) { if (pendingAdds.TryGetValue(uniqueId, out var value)) { pendingAdds.Remove(uniqueId); pendingAddOrder.Remove(uniqueId); Send(Op.Add, uniqueId, ((ObjectLinkedToDefinition)(object)value).id, value.WorldId, value, null); } } private static void FlushAllAdds() { if (pendingAddOrder.Count == 0) { return; } foreach (string item in pendingAddOrder.ToList()) { FlushAdd(item); } } public static void MarkStateDirty(WgoData data) { if (!(((data != null) ? data.UniqueId : null) == (SGuid)null) && !applyingState && !ApplyingRemote && !data.isTempObject && (CoopSession.Role != CoopRole.Host || CoopSession.IsInGame)) { if (CoopSession.Role != CoopRole.Host && !ClientBuilding) { OnResChanged(data); } else { stateDirty[data.UniqueId.Id] = data; } } } public static void MarkStateDirty(SGuid uid) { if (!(uid == (SGuid)null) && !applyingState && !ApplyingRemote && ((CoopSession.Role == CoopRole.Host && CoopSession.IsInGame) || ClientBuilding || (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld))) { WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(uid) : null); if (val != null) { MarkStateDirty(val); } } } public static void Tick() { //IL_0197: Unknown result type (might be due to invalid IL or missing references) FlushAllAdds(); if (CoopSession.Role == CoopRole.Client) { FlushClientRes(); } bool flag = CoopSession.Role == CoopRole.Host; if (flag && ShouldBroadcast) { SendStatesToNewArrivals(); } if (stateDirty.Count == 0) { return; } List list = stateDirty.Values.ToList(); stateDirty.Clear(); if (flag) { NpcSync.ScenesWithPartners(); } float now = Time.unscaledTime; foreach (WgoData item in list) { if (((item != null) ? item.UniqueId : null) == (SGuid)null) { continue; } WorldData worldData = MainGame.WorldData; if (((worldData != null) ? worldData.GetWgoData(item.UniqueId) : null) == null) { continue; } string id = item.UniqueId.Id; if (!flag) { SendState(item, null); continue; } stateChangedEver.Add(id); float value; bool flag2 = justAdded.TryGetValue(id, out value) && now < value; if (ShouldBroadcast && !flag2 && !NpcSync.NearPartners(item) && ((ObjectLinkedToDefinition)(object)item).id != null && ((ObjectLinkedToDefinition)(object)item).id.StartsWith("npc_") && (!heldBackLogged.TryGetValue(id, out var value2) || now - value2 > 10f)) { heldBackLogged[id] = now; CoopLog.Debug($"{((ObjectLinkedToDefinition)(object)item).id} changed in '{item.WorldId}' at {item.Position} (hidden={item.IsHidden}), away from partners; sent when one comes near."); } if (ShouldBroadcast && (flag2 || NpcSync.NearPartners(item))) { if (!flag2 && lastStateSent.TryGetValue(id, out var value3) && now - value3 < 0.25f) { deferredStates[id] = item; continue; } lastStateSent[id] = now; SendState(item, null); } } foreach (KeyValuePair deferredState in deferredStates) { stateDirty[deferredState.Key] = deferredState.Value; } deferredStates.Clear(); if (justAdded.Count <= 200) { return; } foreach (string item2 in (from p in justAdded where p.Value < now select p.Key).ToList()) { justAdded.Remove(item2); } } private static void SendStatesToNewArrivals() { //IL_011e: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) foreach (ulong item in CoopSession.ReadyClients.ToList()) { RemotePlayer remotePlayer = PlayerSync.Get(item); if (remotePlayer == null || !remotePlayer.HasState || string.IsNullOrEmpty(remotePlayer.Scene) || (stateSceneSentTo.TryGetValue(item, out var value) && value == remotePlayer.Scene)) { continue; } stateSceneSentTo[item] = remotePlayer.Scene; if (stateChangedEver.Count == 0) { continue; } int num = 0; foreach (string item2 in NpcSync.ScenesWithPartners()) { WorldData worldData = MainGame.WorldData; GameSceneData val = ((worldData != null) ? worldData.GetGameSceneDataById(item2) : null); if (val == null) { continue; } foreach (WgoData item3 in val.wgoDataList.ToList()) { if (!(((item3 != null) ? item3.UniqueId : null) != (SGuid)null) || !stateChangedEver.Contains(item3.UniqueId.Id)) { continue; } if (!(item2 == remotePlayer.Scene)) { Vector3 val2 = item3.Position - remotePlayer.Target; if (!(((Vector3)(ref val2)).sqrMagnitude < 2025f)) { continue; } } SendState(item3, null, item); num++; } } if (num > 0) { CoopLog.Debug($"Sent the state of {num} changed objects in '{remotePlayer.Scene}' to {remotePlayer.Label}."); } } } private static void WritePart(ref FastBufferWriter w, WgoPartData part) { //IL_0048: 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) CoopMessages.WriteString(ref w, part?.id ?? ""); CoopMessages.WriteString(ref w, part?.variationId ?? ""); int num = part?.rotationIndex ?? (-1); ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); } private unsafe static void SendState(WgoData data, ulong? except, ulong? onlyTo = null) { //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_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_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_005b: 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_0174: 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_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_032c: 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) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 7; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, data.UniqueId.Id); WritePart(ref writer, data.MainWgoPartData); List list = data.AdditionalWgoPartsData ?? new List(); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (WgoPartData item in list) { WritePart(ref writer, item); } List list2 = (from g in data.WorkbenchParents?.Where((SGuid g) => g != (SGuid)null) select g.Id).ToList() ?? new List(); List list3 = (from g in data.AttachedWorkbenchExtensions?.Where((SGuid g) => g != (SGuid)null) select g.Id).ToList() ?? new List(); count = list2.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item2 in list2) { CoopMessages.WriteString(ref writer, item2); } count = list3.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (string item3 in list3) { CoopMessages.WriteString(ref writer, item3); } SGuid val2 = WorkerIdField.Invoke(data); bool flag = val2 != (SGuid)null && !val2.IsEmpty && !val2.Equals(MainGame.PlayerData.Guid); CoopMessages.WriteString(ref writer, flag ? val2.Id : ""); WgoPartData mainWgoPartData = data.MainWgoPartData; int num = ((mainWgoPartData != null) ? mainWgoPartData.DockPointsCount : 0); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); for (int num2 = 0; num2 < num; num2++) { DockPointData dockPointByIndex = mainWgoPartData.GetDockPointByIndex(num2); CoopMessages.WriteString(ref writer, (((dockPointByIndex != null) ? dockPointByIndex.OccupiedBy : null) != (SGuid)null && !dockPointByIndex.OccupiedBy.IsEmpty) ? dockPointByIndex.OccupiedBy.Id : ""); } CoopMessages.WriteString(ref writer, (data.takenDockPointsParentSGuid != (SGuid)null && !data.takenDockPointsParentSGuid.IsEmpty) ? data.takenDockPointsParentSGuid.Id : ""); b = (data.IsHidden ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); b = (data.IsInteractable ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); WriteRes(ref writer, data); if (CoopSession.Role != CoopRole.Host) { CoopMessages.SendToHost("GK2Coop.World", writer, (NetworkDelivery)3); } else if (onlyTo.HasValue) { CoopMessages.SendTo("GK2Coop.World", onlyTo.Value, writer, (NetworkDelivery)3); } else { CoopMessages.SendToReadyClients("GK2Coop.World", writer, (NetworkDelivery)3, except); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } StatesSent++; } private static PartState ReadPart(ref FastBufferReader reader) { //IL_002e: 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) PartState result = new PartState { Id = CoopMessages.ReadString(ref reader), Variation = CoopMessages.ReadString(ref reader) }; ((FastBufferReader)(ref reader)).ReadValueSafe(ref result.Rotation, default(ForPrimitives)); return result; } private static void OnStateMessage(ulong sender, ref FastBufferReader reader) { //IL_0019: 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_004f: 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_008e: 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_00d5: 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) //IL_0114: 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_0126: 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) string text = CoopMessages.ReadString(ref reader); PartState state = ReadPart(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List list = new List(); for (int i = 0; i < num; i++) { list.Add(ReadPart(ref reader)); } int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); List parents = new List(); for (int j = 0; j < num2; j++) { parents.Add(CoopMessages.ReadString(ref reader)); } int num3 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); List extensions = new List(); for (int k = 0; k < num3; k++) { extensions.Add(CoopMessages.ReadString(ref reader)); } string workerId = CoopMessages.ReadString(ref reader); int num4 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num4, default(ForPrimitives)); List list2 = new List(); for (int l = 0; l < num4; l++) { list2.Add(CoopMessages.ReadString(ref reader)); } string dockParent = CoopMessages.ReadString(ref reader); byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); byte b2 = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b2, default(ForPrimitives)); ResValues res = ReadRes(ref reader); if (CoopSession.Role == CoopRole.Client && !CoopSession.ClientInWorld) { return; } WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text)) : null); if (val == null) { return; } applyingState = true; bool applyingRemote = ApplyingRemote; ApplyingRemote = CoopSession.Role == CoopRole.Client; try { ApplyPart(val, val.MainWgoPartData, state); foreach (WgoPartData existing in (val.AdditionalWgoPartsData ?? new List()).ToList()) { if (existing != null && !list.Any((PartState p) => p.Id == existing.id)) { val.RemoveWgoPart(existing.id); } } foreach (PartState part in list) { WgoPartData val2 = val.AdditionalWgoPartsData?.FirstOrDefault((Func)((WgoPartData p) => p != null && p.id == part.Id)); if (val2 == null) { val.AddWgoPart(part.Id, part.Variation ?? "", part.Rotation); } else { ApplyPart(val, val2, part); } } foreach (SGuid item in val.WorkbenchParents.Where((SGuid g) => g != (SGuid)null && !parents.Contains(g.Id)).ToList()) { val.RemoveWorkbenchParent(item); } foreach (SGuid item2 in val.AttachedWorkbenchExtensions.Where((SGuid g) => g != (SGuid)null && !extensions.Contains(g.Id)).ToList()) { val.RemoveWorkbenchExtension(item2); } foreach (string id in parents) { if (!val.WorkbenchParents.Any((SGuid g) => g != (SGuid)null && g.Id == id)) { val.AddWorkbenchParent(SGuid.Parse(id)); } } foreach (string id2 in extensions) { if (!val.AttachedWorkbenchExtensions.Any((SGuid g) => g != (SGuid)null && g.Id == id2)) { val.AddWorkbenchExtension(SGuid.Parse(id2)); } } ApplyWorkerAndDocks(val, workerId, list2, dockParent); if (val.IsHidden != (b != 0)) { val.IsHidden = b != 0; } if (val.IsInteractable != (b2 != 0)) { val.IsInteractable = b2 != 0; } ApplyRes(val, res); StatesApplied++; CoopLog.Debug($"Applied the state of {((ObjectLinkedToDefinition)(object)val).id} ({text}): {state.Variation}/{state.Rotation}, {list.Count} extra parts, {parents.Count} parents, {extensions.Count} extensions."); } catch (Exception e) { CoopLog.Error("Applying the state of " + ((ObjectLinkedToDefinition)(object)val).id + " (" + text + ") failed", e); } finally { applyingState = false; ApplyingRemote = applyingRemote; } if (CoopSession.Role == CoopRole.Host) { stateChangedEver.Add(text); SendState(val, null); } } private static void WriteRes(ref FastBufferWriter w, WgoData data) { //IL_002b: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) GameRes obj = GameResField.Invoke(data); List list = ((obj != null) ? obj.List : null); int num = list?.Count ?? 0; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num, default(ForPrimitives)); for (int i = 0; i < num; i++) { CoopMessages.WriteString(ref w, list[i].type ?? ""); float num2 = list[i].value; ((FastBufferWriter)(ref w)).WriteValueSafe(ref num2, default(ForPrimitives)); } GameResStr gameResStr = data.GameResStr; int num3 = ((gameResStr != null) ? gameResStr.Keys.Count : 0); ((FastBufferWriter)(ref w)).WriteValueSafe(ref num3, default(ForPrimitives)); for (int j = 0; j < num3; j++) { CoopMessages.WriteString(ref w, gameResStr.Keys[j] ?? ""); CoopMessages.WriteString(ref w, (j < gameResStr.Values.Count) ? (gameResStr.Values[j] ?? "") : ""); } } private static ResValues ReadRes(ref FastBufferReader reader) { //IL_000b: 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_0029: 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_0059: 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) ResValues resValues = new ResValues(); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); float value = default(float); for (int i = 0; i < num; i++) { string key = CoopMessages.ReadString(ref reader); ((FastBufferReader)(ref reader)).ReadValueSafe(ref value, default(ForPrimitives)); resValues.Numbers.Add(new KeyValuePair(key, value)); } int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); for (int j = 0; j < num2; j++) { string key2 = CoopMessages.ReadString(ref reader); string value2 = CoopMessages.ReadString(ref reader); resValues.Texts.Add(new KeyValuePair(key2, value2)); } return resValues; } private static void ApplyRes(WgoData data, ResValues res) { if (res == null) { return; } Dictionary dictionary = new Dictionary(); foreach (KeyValuePair number in res.Numbers) { dictionary[number.Key] = number.Value; } GameRes obj = GameResField.Invoke(data); List list = ((obj != null) ? obj.List : null); if (list != null) { foreach (GameResAtom item in list.ToList()) { if (!dictionary.ContainsKey(item.type) && Mathf.Abs(item.value) > 0.0001f) { data.SetGameRes(item.type, 0f); } } } foreach (KeyValuePair item2 in dictionary) { if (Mathf.Abs(data.GetGameRes(item2.Key) - item2.Value) > 0.0001f) { data.SetGameRes(item2.Key, item2.Value); } } GameResStr gameResStr = data.GameResStr; if (gameResStr == null) { return; } bool flag = gameResStr.Keys.Count == res.Texts.Count; int num = 0; while (flag && num < res.Texts.Count) { flag = gameResStr.Get(res.Texts[num].Key, (string)null) == res.Texts[num].Value; num++; } if (flag) { return; } gameResStr.Clear(); foreach (KeyValuePair text in res.Texts) { gameResStr.Set(text.Key, text.Value); } } public static void OnResChanged(WgoData data) { //IL_00a0: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if (((data != null) ? data.UniqueId : null) == (SGuid)null || applyingState || ApplyingRemote || data.isTempObject) { return; } if (CoopSession.Role == CoopRole.Host) { if (CoopSession.IsInGame) { stateDirty[data.UniqueId.Id] = data; } } else { if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || !Plugin.HostRunsWorld.Value) { return; } PlayerController playerController = MainGame.PlayerController; PlayerPhysicalBody val = ((playerController != null) ? playerController.PhysicalBody : null); if (!((Object)(object)val == (Object)null) && !(data.WorldId != MainGame.PlayerData.currentGameSceneId)) { Vector3 val2 = data.Position - ((Component)val).transform.position; if (!(((Vector3)(ref val2)).sqrMagnitude > 36f)) { resDirty[data.UniqueId.Id] = data; } } } } private unsafe static void FlushClientRes(bool now = false) { //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_006b: 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_0076: 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_00c2: 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_0112: Unknown result type (might be due to invalid IL or missing references) if (resDirty.Count == 0 || (!now && Time.unscaledTime < nextResFlush)) { return; } nextResFlush = Time.unscaledTime + 0.25f; foreach (WgoData item in resDirty.Values.ToList()) { if (((item != null) ? item.UniqueId : null) == (SGuid)null) { continue; } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 8; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, item.UniqueId.Id); WritePart(ref writer, item.MainWgoPartData); List list = item.AdditionalWgoPartsData ?? new List(); int count = list.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref count, default(ForPrimitives)); foreach (WgoPartData item2 in list) { WritePart(ref writer, item2); } WriteRes(ref writer, item); CoopMessages.SendToHost("GK2Coop.World", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Debug("Sent " + ((ObjectLinkedToDefinition)(object)item).id + "'s values to the host."); } resDirty.Clear(); } private static void OnResMessage(ulong sender, ref FastBufferReader reader) { //IL_0013: 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) string text = CoopMessages.ReadString(ref reader); PartState state = ReadPart(ref reader); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); List list = new List(); for (int i = 0; i < num; i++) { list.Add(ReadPart(ref reader)); } ResValues resValues = ReadRes(ref reader); if (CoopSession.Role != CoopRole.Host) { return; } WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(text)) : null); if (val == null) { return; } applyingState = true; try { ApplyPart(val, val.MainWgoPartData, state); foreach (PartState part in list) { WgoPartData val2 = val.AdditionalWgoPartsData?.FirstOrDefault((Func)((WgoPartData p) => p != null && p.id == part.Id)); if (val2 == null) { val.AddWgoPart(part.Id, part.Variation ?? "", part.Rotation); } else { ApplyPart(val, val2, part); } } ApplyRes(val, resValues); CoopLog.Debug($"{PlayerSync.Get(sender)?.Label ?? sender.ToString()} changed {((ObjectLinkedToDefinition)(object)val).id}: {state.Variation}/{state.Rotation}, {resValues.Numbers.Count} values."); } catch (Exception e) { CoopLog.Error("Applying " + ((ObjectLinkedToDefinition)(object)val).id + "'s values from " + (PlayerSync.Get(sender)?.Label ?? sender.ToString()) + " failed", e); } finally { applyingState = false; } MarkStateDirty(val); } private static void ApplyWorkerAndDocks(WgoData data, string workerId, List docks, string dockParent) { SGuid val = WorkerIdField.Invoke(data); bool flag = val != (SGuid)null && !val.IsEmpty && val.Equals(MainGame.PlayerData.Guid); if (((val != (SGuid)null && !val.IsEmpty && !flag) ? val.Id : "") != workerId && (!flag || !string.IsNullOrEmpty(workerId))) { if (string.IsNullOrEmpty(workerId)) { data.ClearWorker(); } else { WorkerIdField.Invoke(data) = SGuid.Parse(workerId); try { (WorkerChangedField?.GetValue(data) as Action)?.Invoke(); } catch (Exception ex) { CoopLog.Warn("Worker change listeners on " + ((ObjectLinkedToDefinition)(object)data).id + " failed: " + ex.Message); } } } WgoPartData mainWgoPartData = data.MainWgoPartData; if (mainWgoPartData != null) { for (int i = 0; i < docks.Count && i < mainWgoPartData.DockPointsCount; i++) { DockPointData dockPointByIndex = mainWgoPartData.GetDockPointByIndex(i); if (dockPointByIndex != null && !(((dockPointByIndex.OccupiedBy != (SGuid)null && !dockPointByIndex.OccupiedBy.IsEmpty) ? dockPointByIndex.OccupiedBy.Id : "") == docks[i])) { if (string.IsNullOrEmpty(docks[i])) { dockPointByIndex.UnOccupy(); } else { dockPointByIndex.Occupy(SGuid.Parse(docks[i])); } } } } data.takenDockPointsParentSGuid = (string.IsNullOrEmpty(dockParent) ? SGuid.Empty : SGuid.Parse(dockParent)); } private static void ApplyPart(WgoData data, WgoPartData part, PartState state) { if (part != null && !string.IsNullOrEmpty(state.Variation) && (!(part.variationId == state.Variation) || part.rotationIndex != state.Rotation)) { part.TryApplyState(data.UniqueId, state.Variation, state.Rotation); } } public static bool ForwardDeath(WgoData data, bool fromView = false) { if (!ClientCanForward || !Plugin.HostRunsWorld.Value || ((data != null) ? data.UniqueId : null) == (SGuid)null) { return false; } string id = data.UniqueId.Id; if (recentKills.TryGetValue(id, out var value) && Time.unscaledTime < value) { return true; } recentKills[id] = Time.unscaledTime + 3f; FlushClientRes(now: true); Send(Op.Kill, id, ((ObjectLinkedToDefinition)(object)data).id, fromView ? "view" : "", data, null); CoopLog.Info("Destroyed " + ((ObjectLinkedToDefinition)(object)data).id + " (" + id + "); asking the host to run its death."); return true; } private unsafe static void Send(Op op, string uniqueId, string a, string b, WgoData data, ulong? except) { //IL_005c: 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_0062: 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_006d: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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) //IL_00e8: 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_0100: 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_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if (op != Op.Add) { FlushAdd(uniqueId); } byte[] array = null; if (op == Op.Add && data != null) { try { array = WorldTransfer.SerializeObject(data); } catch (Exception ex) { CoopLog.Warn("Couldn't send " + ((ObjectLinkedToDefinition)(object)data).id + " whole (" + ex.Message + "); sending its type and position only."); } } FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b2 = (byte)op; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b2, default(ForPrimitives)); CoopMessages.WriteString(ref writer, uniqueId); CoopMessages.WriteString(ref writer, a); CoopMessages.WriteString(ref writer, b); CoopMessages.WriteString(ref writer, ((data != null) ? data.CustomTag : null) ?? ""); CoopMessages.WriteVector3(ref writer, (data != null) ? data.Position : Vector3.zero); Vector2 val2 = ((data != null) ? data.MovableDirection : Vector2.zero); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref val2.x, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref val2.y, default(ForPrimitives)); if (op == Op.Add) { CoopMessages.WriteBytes(ref writer, array ?? new byte[0]); } if (CoopSession.Role == CoopRole.Host) { CoopMessages.SendToReadyClients("GK2Coop.World", writer, (NetworkDelivery)3, except); } else { CoopMessages.SendToHost("GK2Coop.World", writer, (NetworkDelivery)3); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Sent++; CoopLog.Debug($"World {op} {((ObjectLinkedToDefinition)(object)data)?.id} {uniqueId} {a} {b}".TrimEnd()); } public static void OnWorld(ulong sender, FastBufferReader reader) { //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_0079: 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_008c: 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_009f: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 7: OnStateMessage(sender, ref reader); return; case 8: OnResMessage(sender, ref reader); return; } Change change = new Change { Op = (Op)b, UniqueId = CoopMessages.ReadString(ref reader), A = CoopMessages.ReadString(ref reader), B = CoopMessages.ReadString(ref reader), C = CoopMessages.ReadString(ref reader), Position = CoopMessages.ReadVector3(ref reader) }; float num = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); float num2 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); change.Direction = new Vector2(num, num2); if (change.Op == Op.Add) { change.Full = CoopMessages.ReadBytes(ref reader); } if (CoopSession.Role == CoopRole.Host) { CoopLog.Info($"Client {sender} sent world {change.Op} for {change.A} {change.UniqueId}."); EffectSync.KillFromClient = sender; try { Apply(change, fromHost: false); return; } finally { EffectSync.KillFromClient = null; } } if (CoopSession.Role == CoopRole.Client) { if (!CoopSession.ClientInWorld) { pendingWhileLoading.Add(change); } else { Apply(change, fromHost: true); } } } public static void FlushPending() { if (pendingWhileLoading.Count > 0) { CoopLog.Info($"Applying {pendingWhileLoading.Count} world changes the host made while we were loading."); } foreach (Change item in pendingWhileLoading) { Apply(item, fromHost: true); } pendingWhileLoading.Clear(); } public static void Reset() { pendingWhileLoading.Clear(); recentKills.Clear(); stateDirty.Clear(); stateChangedEver.Clear(); justAdded.Clear(); lastStateSent.Clear(); deferredStates.Clear(); resDirty.Clear(); stateSceneSentTo.Clear(); applyingState = false; StatesSent = 0; StatesApplied = 0; compoundDepth = 0; ApplyingRemote = false; Sent = 0; Applied = 0; Missing = 0; } private static WgoData Rebuild(Change change) { if (change.Full == null || change.Full.Length == 0) { return null; } try { object obj = WorldTransfer.DeserializeObject(change.Full); WgoData val = (WgoData)((obj is WgoData) ? obj : null); if (((val != null) ? val.UniqueId : null) == (SGuid)null || val.UniqueId.Id != change.UniqueId) { CoopLog.Warn("The whole copy of " + change.A + " (" + change.UniqueId + ") didn't match; using a fresh one."); return null; } if (!string.IsNullOrEmpty(change.B)) { val.WorldId = change.B; } ZombieWgoData val2 = (ZombieWgoData)(object)((val is ZombieWgoData) ? val : null); if (val2 != null) { val2.PrepareForGameBase(); } else { val.PrepareForGame(); } return val; } catch (Exception ex) { CoopLog.Warn("Couldn't rebuild " + change.A + " (" + change.UniqueId + ") whole: " + ex.Message); return null; } } public static void NoteDeathRun(WgoData data) { if (data != null && CoopSession.Role == CoopRole.Host && !DeathTriggeredField.Invoke(data)) { deathRan = data; } } private static void HostKill(WgoData data, ulong? partner, bool fromView) { string id = ((ObjectLinkedToDefinition)(object)data).id; string id2 = data.UniqueId.Id; Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(data.UniqueId); bool flag = (Object)(object)wgoViewGlobal != (Object)null && (Object)(object)wgoViewGlobal.MainWgoPart == (Object)null; string text = (((Object)(object)wgoViewGlobal == (Object)null) ? "not loaded" : (flag ? "not set up" : "loaded")); bool flag2 = DeathTriggeredField.Invoke(data); bool flag3 = DeathCallbackField.Invoke(data) != null; deathRan = null; bool flag4 = false; if (partner.HasValue) { CraftSync.BeginCredit(partner.Value); } try { if (!flag) { try { if (fromView && (Object)(object)wgoViewGlobal != (Object)null) { wgoViewGlobal.ForceDeath(); } else { data.ForceDeath(); } } catch (Exception e) { flag4 = true; CoopLog.Error("The death of " + id + " (" + id2 + ") threw (view " + text + ")", e); } } if (deathRan == data) { CoopLog.Debug("Ran the death of " + id + " (" + id2 + ") (view " + text + (fromView ? ", as the view's" : "") + ")."); return; } CoopLog.Warn("The death of " + id + " (" + id2 + ") didn't run here: view " + text + ", " + string.Format("already triggered {0}, waiting callback {1}{2}. Running it by hand.", flag2, flag3, flag4 ? ", it threw" : "")); if (!(RunAfterDeathMethod == null)) { DeathTriggeredField.Invoke(data) = false; DeathCallbackField.Invoke(data) = null; try { RunAfterDeathMethod.Invoke(data, null); } catch (Exception ex) { CoopLog.Error("Running the death of " + id + " by hand failed", ex.InnerException ?? ex); } WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(data.UniqueId) : null); CoopLog.Info((val != null && ((ObjectLinkedToDefinition)(object)val).id == id && ((ObjectLinkedToDefinition)(object)val).Definition != null && !((ObjectLinkedToDefinition)(object)val).Definition.reviveOnDie) ? (id + " is still there after its death ran by hand.") : (id + "'s death ran by hand.")); } } finally { if (partner.HasValue) { CraftSync.EndCredit(); } } } private static void Apply(Change change, bool fromHost) { //IL_0235: 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_0247: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00aa: Expected O, but got Unknown //IL_0084: 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) WorldData worldData = MainGame.WorldData; bool applyingRemote = ApplyingRemote; ApplyingRemote = fromHost; try { if (change.Op == Op.Add) { if (worldData.GetWgoData(SGuid.Parse(change.UniqueId)) != null) { return; } WgoData val = Rebuild(change); bool flag = val != null; if (val == null) { val = (WgoData)(GameBalance.Me.conveyorWgosCache.TryGetValue(change.A, out var value) ? new ConveyorWgoData(value.conveyorType, change.A, change.Position, change.B) : new WgoData(change.A, change.Position, change.B, change.C ?? "", false)); if (!string.IsNullOrEmpty(change.C)) { val.CustomTag = change.C; } val.UniqueId.Id = change.UniqueId; if (((Vector2)(ref change.Direction)).sqrMagnitude > 0.0001f) { val.MovableDirection = change.Direction; } } worldData.AddWgoData(val); ZombieWgoData val2 = (ZombieWgoData)(object)((val is ZombieWgoData) ? val : null); if (val2 != null) { ZombieBook.OnZombieAdded(val2); } Applied++; CoopLog.Debug(string.Format("Created {0} ({1}) in '{2}' at {3}{4}.", change.A, change.UniqueId, change.B, change.Position, flag ? (" (whole " + ((object)val).GetType().Name + ")") : "")); return; } WgoData wgoData = worldData.GetWgoData(SGuid.Parse(change.UniqueId)); if (wgoData == null) { Missing++; CoopLog.Debug($"World {change.Op}: object {change.UniqueId} isn't in this world (already gone or never existed here)."); return; } switch (change.Op) { case Op.Remove: { worldData.RemoveWgoDataFromGameScene(wgoData, true); ZombieWgoData val3 = (ZombieWgoData)(object)((wgoData is ZombieWgoData) ? wgoData : null); if (val3 != null && CoopSession.Role == CoopRole.Client) { ZombieBook.OnZombieRemoved(val3); } break; } case Op.Replace: { worldData.RemoveWgoDataFromGameScene(wgoData, true); WgoData val4 = new WgoData(change.A, wgoData.Position, wgoData.WorldId); val4.SpawnWGOComponent.SpawnStages = wgoData.SpawnWGOComponent.SpawnStages; val4.UniqueId.Id = change.B; worldData.AddWgoData(val4); break; } case Op.Change: worldData.ChangeWgoData(wgoData, change.A); break; case Op.Move: worldData.MoveWgoDataToAnotherGameScene(wgoData, change.A); break; case Op.Kill: if (CoopSession.Role == CoopRole.Host) { HostKill(wgoData, EffectSync.KillFromClient, change.B == "view"); } break; } Applied++; CoopLog.Debug($"Applied world {change.Op} to {((ObjectLinkedToDefinition)(object)wgoData).id} ({change.UniqueId}) {change.A}".TrimEnd()); } catch (Exception e) { CoopLog.Error($"Applying world {change.Op} to {change.UniqueId} failed", e); } finally { ApplyingRemote = applyingRemote; } } } internal static class WorldTransfer { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnBegin; public static HandleNamedMessageDelegate <1>__OnChunk; public static HandleNamedMessageDelegate <2>__OnReady; } private const string BeginMessage = "GK2Coop.WorldBegin"; private const string ChunkMessage = "GK2Coop.WorldChunk"; private const string ReadyMessage = "GK2Coop.ClientReady"; private const int ProtocolVersion = 1; private const int ChunkSize = 49152; private const int ChunksPerFrame = 8; private static readonly Dictionary sendProgress = new Dictionary(); private static byte[] incoming; private static int incomingRawLength; private static int incomingChunks; private static int receivedChunks; private static bool incomingCompressed; public static float ReceiveProgress { get { if (incomingChunks != 0) { return (float)receivedChunks / (float)incomingChunks; } return 0f; } } public static bool IsReceiving => incoming != null; public static IReadOnlyDictionary SendProgress => sendProgress; private static OdinBinaryFileSerializer Serializer => (OdinBinaryFileSerializer)AccessTools.Property(typeof(SaveSystem), "OdinBinaryFileSerializer").GetValue(null); public static byte[] SerializeObject(object value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown SerializationContext val = new SerializationContext(); try { object? obj = AccessTools.Field(typeof(OdinBinaryFileSerializer), "serializationContext")?.GetValue(Serializer); SerializationContext val2 = (SerializationContext)((obj is SerializationContext) ? obj : null); if (val2 != null) { val.Config = val2.Config; val.Binder = val2.Binder; val.StringReferenceResolver = val2.StringReferenceResolver; val.GuidReferenceResolver = val2.GuidReferenceResolver; val.IndexReferenceResolver = val2.IndexReferenceResolver; } } catch (Exception ex) { CoopLog.Debug("Using default Odin settings for an object: " + ex.Message); } byte[] array = SerializationUtility.SerializeValue(value, (DataFormat)0, val); if (array == null || array.Length == 0) { throw new InvalidOperationException("the serializer returned nothing"); } return array; } public static object DeserializeObject(byte[] bytes) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown DeserializationContext val = new DeserializationContext(); try { object? obj = AccessTools.Field(typeof(OdinBinaryFileSerializer), "deserializationContext")?.GetValue(Serializer); DeserializationContext val2 = (DeserializationContext)((obj is DeserializationContext) ? obj : null); if (val2 != null) { val.Config = val2.Config; val.Binder = val2.Binder; val.StringReferenceResolver = val2.StringReferenceResolver; val.GuidReferenceResolver = val2.GuidReferenceResolver; val.IndexReferenceResolver = val2.IndexReferenceResolver; } } catch (Exception ex) { CoopLog.Debug("Using default Odin settings for an object: " + ex.Message); } return SerializationUtility.DeserializeValue(bytes, (DataFormat)0, val); } public static void RegisterHandlers() { //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_002b: Expected O, but got Unknown //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_0051: Expected O, but got Unknown //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_0076: Expected O, but got Unknown CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; object obj = <>O.<0>__OnBegin; if (obj == null) { HandleNamedMessageDelegate val = OnBegin; <>O.<0>__OnBegin = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("GK2Coop.WorldBegin", (HandleNamedMessageDelegate)obj); object obj2 = <>O.<1>__OnChunk; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnChunk; <>O.<1>__OnChunk = val2; obj2 = (object)val2; } customMessagingManager.RegisterNamedMessageHandler("GK2Coop.WorldChunk", (HandleNamedMessageDelegate)obj2); object obj3 = <>O.<2>__OnReady; if (obj3 == null) { HandleNamedMessageDelegate val3 = OnReady; <>O.<2>__OnReady = val3; obj3 = (object)val3; } customMessagingManager.RegisterNamedMessageHandler("GK2Coop.ClientReady", (HandleNamedMessageDelegate)obj3); } public static void Reset() { sendProgress.Clear(); incoming = null; incomingChunks = 0; receivedChunks = 0; } public unsafe static IEnumerator SendWorld(ulong clientId) { int num; bool flag; byte[] payload; try { byte[] array = SerializeHostWorld(); num = array.Length; flag = TryCompress(array, out payload); CoopLog.Info(string.Format("World for player {0}: {1} KB, sending {2} KB{3}.", clientId, num / 1024, payload.Length / 1024, flag ? " (gzip)" : "")); } catch (Exception ex) { CoopSession.Status = "Couldn't package your world: " + ex.Message; CoopLog.Error("World transfer failed", ex); yield break; } int chunkCount = (payload.Length + 49152 - 1) / 49152; FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(32, (Allocator)2, -1); try { int num2 = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num2, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); num2 = payload.Length; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num2, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref chunkCount, default(ForPrimitives)); byte b = (flag ? ((byte)1) : ((byte)0)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); Send("GK2Coop.WorldBegin", clientId, writer); } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } sendProgress[clientId] = 0f; FastBufferWriter writer2 = default(FastBufferWriter); for (int i = 0; i < chunkCount; i++) { if (!IsConnected(clientId)) { sendProgress.Remove(clientId); yield break; } int num3 = i * 49152; int num4 = Math.Min(49152, payload.Length - num3); ((FastBufferWriter)(ref writer2))..ctor(num4 + 16, (Allocator)2, -1); try { ((FastBufferWriter)(ref writer2)).WriteValueSafe(ref i, default(ForPrimitives)); ((FastBufferWriter)(ref writer2)).WriteValueSafe(ref num4, default(ForPrimitives)); ((FastBufferWriter)(ref writer2)).WriteBytesSafe(payload, num4, num3); Send("GK2Coop.WorldChunk", clientId, writer2); } finally { ((IDisposable)(*(FastBufferWriter*)(&writer2))/*cast due to .constrained prefix*/).Dispose(); } sendProgress[clientId] = ((float)i + 1f) / (float)chunkCount; if ((i + 1) % 8 == 0) { yield return null; } } sendProgress.Remove(clientId); CoopSession.Status = $"World sent to player {clientId}. Waiting for them to load..."; } public unsafe static void SendReady() { //IL_0012: 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_0025: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(4, (Allocator)2, -1); try { int num = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); Send("GK2Coop.ClientReady", 0uL, writer); } finally { ((IDisposable)(*(FastBufferWriter*)(&writer))/*cast due to .constrained prefix*/).Dispose(); } } private static byte[] SerializeHostWorld() { GameSave gameSave = MainGame.Instance.GameSave; NetworkPlayersStash networkPlayersStash = NetworkPlayersStash.Strip(gameSave); try { gameSave.OnBeforeSerialize(); byte[] array = Serializer.Serialize(gameSave); if (array == null || array.Length == 0) { throw new InvalidOperationException("the save serializer returned nothing (see the log above)"); } return array; } finally { networkPlayersStash?.Restore(gameSave); } } private static void OnBegin(ulong senderClientId, FastBufferReader reader) { //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_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) //IL_0036: 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_004b: 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_005d: 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) if (CoopSession.Role == CoopRole.Client) { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref incomingRawLength, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref incomingChunks, default(ForPrimitives)); byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); if (num != 1) { CoopSession.Status = $"The host runs a different version of the co-op mod (protocol {num}, you have {1})."; CoopSession.Leave(); return; } incomingCompressed = b != 0; incoming = new byte[num2]; receivedChunks = 0; CoopSession.Status = $"Downloading the host's world ({num2 / 1024} KB)..."; } } private static void OnChunk(ulong senderClientId, FastBufferReader reader) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) if (CoopSession.Role != CoopRole.Client || incoming == null) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadBytesSafe(ref incoming, num2, num * 49152); receivedChunks++; CoopSession.Status = $"Downloading the host's world... {ReceiveProgress:P0}"; if (receivedChunks >= incomingChunks) { byte[] array = incoming; incoming = null; GameSave gameSave; try { byte[] array2 = (incomingCompressed ? Decompress(array, incomingRawLength) : array); gameSave = Serializer.Deserialize(array2); } catch (Exception ex) { CoopSession.Status = "Couldn't read the host's world: " + ex.Message; CoopLog.Error("World transfer failed", ex); CoopSession.Leave(); return; } CoopLog.Info($"Received the host's world ({incomingRawLength / 1024} KB)."); CoopSession.OnWorldReceived(gameSave); } } private static void OnReady(ulong senderClientId, FastBufferReader reader) { CoopSession.OnClientReady(senderClientId); } private static void Send(string name, ulong clientId, FastBufferWriter writer) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(name, clientId, writer, (NetworkDelivery)4); } private static bool IsConnected(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsListening) { return singleton.ConnectedClientsIds.Contains(clientId); } return false; } private static bool TryCompress(byte[] raw, out byte[] payload) { try { using MemoryStream memoryStream = new MemoryStream(raw.Length / 3); using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Compress)) { gZipStream.Write(raw, 0, raw.Length); } payload = memoryStream.ToArray(); return true; } catch (Exception ex) { CoopLog.Warn("Compression unavailable, sending the world uncompressed: " + ex.Message); payload = raw; return false; } } private static byte[] Decompress(byte[] payload, int rawLength) { byte[] array = new byte[rawLength]; using GZipStream gZipStream = new GZipStream(new MemoryStream(payload), CompressionMode.Decompress); int num; for (int i = 0; i < rawLength; i += num) { num = gZipStream.Read(array, i, rawLength - i); if (num <= 0) { throw new EndOfStreamException($"world data ended early ({i} of {rawLength} bytes)"); } } return array; } } internal sealed class NetworkPlayersStash { private NetworkPlayer hostPlayer; private List clientPlayers; public static NetworkPlayersStash Strip(GameSave gameSave) { if (gameSave == null || (gameSave.hostPlayer == null && (gameSave.clientPlayers == null || gameSave.clientPlayers.Count == 0))) { return null; } NetworkPlayersStash result = new NetworkPlayersStash { hostPlayer = gameSave.hostPlayer, clientPlayers = ((gameSave.clientPlayers == null) ? null : new List(gameSave.clientPlayers)) }; gameSave.hostPlayer = null; List list = gameSave.clientPlayers; if (list != null) { list.Clear(); return result; } return result; } public void Restore(GameSave gameSave) { gameSave.hostPlayer = hostPlayer; if (clientPlayers != null) { if (gameSave.clientPlayers == null) { gameSave.clientPlayers = new List(); } gameSave.clientPlayers.AddRange(clientPlayers); } } } internal static class InteractionProxy { private const byte OpInteract = 1; private const byte OpOverhead = 2; private const byte OpRefused = 3; private const byte OpCustom = 4; private const byte OpExhume = 5; private static readonly FieldRef AssignedWgoField = AccessTools.FieldRefAccess("assignedWgo"); private static readonly FieldRef InteractorField = AccessTools.FieldRefAccess("interactor"); private static readonly FieldRef> OverheadField = AccessTools.FieldRefAccess>("overheadItems"); private static readonly MethodInfo PlayerDataSetter = AccessTools.PropertySetter(typeof(MainGame), "PlayerData"); private static readonly MethodInfo RefreshOverheadMethod = AccessTools.Method(typeof(PlayerData), "RefreshOverheadVisuals", (Type[])null, (Type[])null); private static readonly MethodInfo ZombieOverheadMethod = AccessTools.Method(typeof(WGOInteractionHandlerBase), "TryGetInsertableZombieOverhead", (Type[])null, (Type[])null); public static bool Running; public static int Proxied; public static int Refused; private static bool customScope; private const float StoryRange = 20f; public static void Reset() { Running = false; Proxied = 0; Refused = 0; } private static bool CarriesZombie(PlayerData data) { Item val = default(Item); if (data != null) { return data.TryGetOverheadItem((Predicate)((Item item) => ((ObjectLinkedToDefinition)(object)item).Definition != null && ((ObjectLinkedToDefinition)(object)item).Definition.itemGroupIds.Contains("zombie")), ref val); } return false; } private static bool CarriesInsertableZombie(WGOInteractionHandlerBase handler) { try { object[] parameters = new object[1]; return ZombieOverheadMethod != null && (bool)ZombieOverheadMethod.Invoke(handler, parameters); } catch { return false; } } public unsafe static bool BeforeInteract(WGOInteractionHandlerBase handler, PlayerController interactor, ref bool result) { //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_00ac: 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_00b8: 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_00ee: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || !Plugin.HostRunsWorld.Value) { return true; } if ((Object)(object)interactor == (Object)null || (Object)(object)interactor != (Object)(object)MainGame.PlayerController) { return true; } Wgo val = AssignedWgoField.Invoke(handler); WgoData val2 = (((Object)(object)val != (Object)null) ? val.Data : null); if (((val2 != null) ? val2.UniqueId : null) == (SGuid)null) { return true; } bool flag = CarriesZombie(interactor.PlayerData); if (handler is CraftInteractionHandler) { if (!CarriesInsertableZombie(handler)) { return true; } } else if (!(handler is ZombieInteractionHandler) && !flag && val2.Worker == null && !HasOccupiedDock(val2)) { return true; } FastBufferWriter val3 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val3; byte b = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, ((object)handler).GetType().Name); CoopMessages.WriteString(ref writer, val2.UniqueId.Id); CoopMessages.SendToHost("GK2Coop.Zombies", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val3))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Zombie interaction with " + ((ObjectLinkedToDefinition)(object)val2).id + " (" + ((object)handler).GetType().Name + ") sent to the host's game."); try { interactor.PlayerInteractionComponent.ResetInteractionState(); } catch { } result = true; return false; } private static bool HasOccupiedDock(WgoData data) { WgoPartData mainWgoPartData = data.MainWgoPartData; if (mainWgoPartData == null) { return false; } for (int i = 0; i < mainWgoPartData.DockPointsCount; i++) { DockPointData dockPointByIndex = mainWgoPartData.GetDockPointByIndex(i); if (dockPointByIndex != null && dockPointByIndex.IsOccupied) { return true; } } return false; } public unsafe static bool BeforeCustomInteract(WGOInteractionHandlerBase handler, PlayerController interactor, ref bool result) { //IL_00ea: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_0122: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role != CoopRole.Client || !CoopSession.ClientInWorld || !Plugin.HostRunsWorld.Value) { return true; } if ((Object)(object)interactor == (Object)null || (Object)(object)interactor != (Object)(object)MainGame.PlayerController) { return true; } Wgo val = AssignedWgoField.Invoke(handler); WgoData val2 = (((Object)(object)val != (Object)null) ? val.Data : null); WGODef val3 = ((ObjectLinkedToDefinition)(object)val2)?.Definition; if (((val2 != null) ? val2.UniqueId : null) == (SGuid)null || val3 == null) { return true; } CustomInteraction val4 = ((val3.customInteraction != null && val3.customInteraction.IsInteractable(val2)) ? val3.customInteraction : val3.customInteraction2); if (PartnerReplay.MovesPlayer(val4?.execution)) { ItemScripts.Begin("Using " + ((ObjectLinkedToDefinition)(object)val2).id, storyToHost: false); customScope = true; return true; } string text = PartnerReplay.HostCallIn(val4?.execution); if (text == null) { return true; } FastBufferWriter val5 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val5; byte b = 4; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, val2.UniqueId.Id); CoopMessages.SendToHost("GK2Coop.Zombies", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val5))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info(((ObjectLinkedToDefinition)(object)val2).id + "'s action (" + text + ") sent to the host's game."); try { interactor.PlayerInteractionComponent.ResetInteractionState(); } catch { } result = true; return false; } public static void AfterCustomInteract() { if (customScope) { customScope = false; ItemScripts.End(); } } public unsafe static bool BeforeExhume(UIGraveWindowData window) { //IL_0056: 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_005c: 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_0066: 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_008e: Unknown result type (might be due to invalid IL or missing references) if (CoopSession.Role == CoopRole.Client && CoopSession.ClientInWorld && Plugin.HostRunsWorld.Value) { object obj; if (window == null) { obj = null; } else { WgoData wgoData = window.WgoData; obj = ((wgoData != null) ? wgoData.UniqueId : null); } if (!((SGuid)obj == (SGuid)null)) { MainGame.PlayerData.inventory.RemoveItemById("exhume_certificate", 1, (Item)null, (Item)null, false); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 5; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, window.WgoData.UniqueId.Id); CoopMessages.SendToHost("GK2Coop.Zombies", writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } CoopLog.Info("Exhuming " + ((ObjectLinkedToDefinition)(object)window.WgoData).id + ": sent to the host's game."); try { ((LazyWindow)(object)LazyUI.GetWindow())?.Close(); ((LazyWindow)(object)LazyUI.GetWindow())?.Close(); } catch { } return false; } } return true; } private static PlayerData PrepareStandIn(RemotePlayer remote, out List carried) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) PlayerData playerData = remote.Controller.PlayerData; playerData.position.Value = remote.Position; playerData.currentGameSceneId = remote.Scene; try { carried = ((remote.CarryBytes == null) ? new List() : (SerializationUtility.DeserializeValue>(remote.CarryBytes, (DataFormat)0, (DeserializationContext)null) ?? new List())); } catch { carried = new List(); } OverheadField.Invoke(playerData) = carried; return playerData; } private unsafe static void SendCarry(ulong sender, RemotePlayer remote, PlayerData standIn, string what, string before) { //IL_00a9: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00d5: Unknown result type (might be due to invalid IL or missing references) List list = (OverheadField.Invoke(standIn) ?? new List()).Where((Item i) => i != null && !i.IsEmpty).ToList(); CoopLog.Info("Ran " + remote.Label + "'s " + what + "; they carry [" + Signature(list) + "] (was [" + before + "])."); byte[] bytes = (remote.CarryBytes = SerializationUtility.SerializeValue>(list, (DataFormat)0, (SerializationContext)null)); PlayerSync.RefreshCarry(remote); FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, bytes); CoopMessages.SendTo("GK2Coop.Zombies", sender, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void RunCustomFor(ulong sender, string uid) { //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) RemotePlayer remotePlayer = PlayerSync.Get(sender); string text = remotePlayer?.Label ?? $"Player {sender}"; string text2 = Plugin.PlayerName.Value ?? "the host"; if ((Object)(object)remotePlayer?.Controller == (Object)null || (Object)(object)remotePlayer.Body == (Object)null) { Refuse(sender, "Your character isn't in the host's game yet; try again in a moment."); return; } WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(uid)) : null); WGODef val2 = ((ObjectLinkedToDefinition)(object)val)?.Definition; if (val == null || val2 == null) { Refuse(sender, "That isn't there in the host's game; nothing happened."); CoopLog.Info(text + "'s object action on " + uid + ": not here."); return; } List carried; PlayerData val3 = PrepareStandIn(remotePlayer, out carried); string before = Signature(carried); PlayerData playerData = MainGame.PlayerData; GameRes val4 = ItemScripts.ResField.Invoke(val3); ItemScripts.ResField.Invoke(val3) = ItemScripts.ResField.Invoke(playerData); Running = true; PlayerDataSetter.Invoke(null, new object[1] { val3 }); try { CustomInteraction val5 = ((val2.customInteraction != null && val2.customInteraction.IsInteractable(val)) ? val2.customInteraction : val2.customInteraction2); if (val5 == null || val5.execution == null) { Refuse(sender, "Nothing to do there in the host's game right now."); return; } if (PartnerReplay.StartsStory(val5.execution) && playerData != null && Vector3.Distance(playerData.position.Value, val.Position) > 20f) { Refuse(sender, "That's part of the story: " + text2 + " has to be there for it."); CoopLog.Info(text + "'s action on " + ((ObjectLinkedToDefinition)(object)val).id + " starts the story away from us; refused."); return; } Item overheadCandidate = default(Item); val5.TryGetInteractable(val, ref overheadCandidate); Item overheadCandidate2 = LazyExpressionEvaluationScope.OverheadCandidate; LazyExpressionEvaluationScope.OverheadCandidate = overheadCandidate; try { foreach (LazyExpression item in val5.execution) { if (item != null) { item.EvaluateBool(val); } } } finally { LazyExpressionEvaluationScope.OverheadCandidate = overheadCandidate2; } Proxied++; } catch (Exception e) { CoopLog.Error("Running " + text + "'s action on " + ((ObjectLinkedToDefinition)(object)val).id + " failed", e); Refuse(sender, "The host's game couldn't do that (see the host's co-op log)."); } finally { ItemScripts.ResField.Invoke(val3) = val4; PlayerDataSetter.Invoke(null, new object[1] { playerData }); Running = false; try { RefreshOverheadMethod?.Invoke(playerData, null); } catch (Exception ex) { CoopLog.Warn("Redrawing the host's carried items failed: " + ex.Message); } } try { Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(val.UniqueId); if (wgoViewGlobal != null) { wgoViewGlobal.DrawWidgets(); } } catch { } WorldSync.MarkStateDirty(val); SendCarry(sender, remotePlayer, val3, "action on " + ((ObjectLinkedToDefinition)(object)val).id, before); } private static void ExhumeFor(ulong sender, string uid) { WorldData worldData = MainGame.WorldData; WgoData val = ((worldData != null) ? worldData.GetWgoData(SGuid.Parse(uid)) : null); string text = PlayerSync.Get(sender)?.Label ?? $"Player {sender}"; if (val == null) { Refuse(sender, "That grave isn't there in the host's game; nothing happened."); return; } try { MainGame.Instance.GameSave.worldData.ChangeWgoData(val, "grave_exhume"); CoopLog.Info(text + " exhumed " + ((ObjectLinkedToDefinition)(object)val).id + " (" + uid + ")."); } catch (Exception e) { CoopLog.Error("Exhuming " + uid + " for " + text + " failed", e); } } private unsafe static void RunFor(ulong sender, string handlerType, string uid) { //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: 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) RemotePlayer remotePlayer = PlayerSync.Get(sender); string text = remotePlayer?.Label ?? $"Player {sender}"; if ((Object)(object)remotePlayer?.Controller == (Object)null || (Object)(object)remotePlayer.Body == (Object)null) { Refuse(sender, "Your character isn't in the host's game yet; try again in a moment."); return; } Wgo wgoViewGlobal = GameScene.GetWgoViewGlobal(SGuid.Parse(uid)); if ((Object)(object)wgoViewGlobal == (Object)null || wgoViewGlobal.Data == null) { string text2 = Plugin.PlayerName.Value ?? "the host"; Refuse(sender, "Zombie work runs in " + text2 + "'s game, which needs that area loaded: do it while " + text2 + " is in the same area."); CoopLog.Info(text + "'s zombie interaction with " + uid + ": not loaded here."); return; } IWGOInteractionHandler interactionHandler = wgoViewGlobal.InteractionHandler; WGOInteractionHandlerBase val = (WGOInteractionHandlerBase)(object)((interactionHandler is WGOInteractionHandlerBase) ? interactionHandler : null); if (val == null || ((object)val).GetType().Name != handlerType) { Refuse(sender, "That object works differently in the host's game; nothing happened."); CoopLog.Warn(text + "'s zombie interaction: expected " + handlerType + ", the host has " + (((object)val)?.GetType().Name ?? "none") + "."); return; } PlayerData playerData = remotePlayer.Controller.PlayerData; playerData.position.Value = remotePlayer.Position; playerData.currentGameSceneId = remotePlayer.Scene; List list; try { list = ((remotePlayer.CarryBytes == null) ? new List() : (SerializationUtility.DeserializeValue>(remotePlayer.CarryBytes, (DataFormat)0, (DeserializationContext)null) ?? new List())); } catch { list = new List(); } OverheadField.Invoke(playerData) = list; PlayerData playerData2 = MainGame.PlayerData; PlayerController val2 = InteractorField.Invoke(val); string text3 = Signature(list); Running = true; PlayerDataSetter.Invoke(null, new object[1] { playerData }); InteractorField.Invoke(val) = remotePlayer.Controller; try { val.Interact(remotePlayer.Controller); Proxied++; } catch (Exception e) { CoopLog.Error("Running " + text + "'s zombie interaction with " + ((ObjectLinkedToDefinition)(object)wgoViewGlobal.Data).id + " failed", e); Refuse(sender, "The host's game couldn't do that (see the host's co-op log)."); } finally { InteractorField.Invoke(val) = val2; PlayerDataSetter.Invoke(null, new object[1] { playerData2 }); Running = false; try { RefreshOverheadMethod?.Invoke(playerData2, null); } catch (Exception ex) { CoopLog.Warn("Redrawing the host's carried items failed: " + ex.Message); } } List list2 = (OverheadField.Invoke(playerData) ?? new List()).Where((Item i) => i != null && !i.IsEmpty).ToList(); WorldSync.MarkStateDirty(wgoViewGlobal.Data); CoopLog.Info("Ran " + text + "'s " + handlerType + " on " + ((ObjectLinkedToDefinition)(object)wgoViewGlobal.Data).id + "; they carry [" + Signature(list2) + "] (was [" + text3 + "])."); byte[] bytes = (remotePlayer.CarryBytes = SerializationUtility.SerializeValue>(list2, (DataFormat)0, (SerializationContext)null)); PlayerSync.RefreshCarry(remotePlayer); FastBufferWriter val3 = CoopMessages.NewWriter(); try { FastBufferWriter writer = val3; byte b = 2; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteBytes(ref writer, bytes); CoopMessages.SendTo("GK2Coop.Zombies", sender, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val3))/*cast due to .constrained prefix*/).Dispose(); } } private static string Signature(List items) { return string.Join(",", from i in items where i != null select ((ObjectLinkedToDefinition)(object)i).id); } private unsafe static void Refuse(ulong sender, string text) { //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_0012: 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_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_0036: Unknown result type (might be due to invalid IL or missing references) Refused++; FastBufferWriter val = CoopMessages.NewWriter(); try { FastBufferWriter writer = val; byte b = 3; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref b, default(ForPrimitives)); CoopMessages.WriteString(ref writer, text); CoopMessages.SendTo("GK2Coop.Zombies", sender, writer, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public static void OnZombies(ulong sender, FastBufferReader reader) { //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) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); switch (b) { case 1: if (CoopSession.Role == CoopRole.Host) { string handlerType = CoopMessages.ReadString(ref reader); string uid = CoopMessages.ReadString(ref reader); RunFor(sender, handlerType, uid); } break; case 2: if (CoopSession.Role == CoopRole.Client) { List list = SerializationUtility.DeserializeValue>(CoopMessages.ReadBytes(ref reader), (DataFormat)0, (DeserializationContext)null) ?? new List(); PlayerData playerData = MainGame.PlayerData; if (playerData != null) { OverheadField.Invoke(playerData) = list; RefreshOverheadMethod?.Invoke(playerData, null); CoopLog.Info("The host's game says we now carry [" + Signature(list) + "]."); } } break; case 4: if (CoopSession.Role == CoopRole.Host) { RunCustomFor(sender, CoopMessages.ReadString(ref reader)); } break; case 5: if (CoopSession.Role == CoopRole.Host) { ExhumeFor(sender, CoopMessages.ReadString(ref reader)); } break; case 3: if (CoopSession.Role == CoopRole.Client) { string text = CoopMessages.ReadString(ref reader); CoopRunner.Think(text); CoopLog.Info("Zombie interaction refused: " + text); } break; } } public static string Describe() { return $"proxied={Proxied} refused={Refused}"; } } [HarmonyPatch(typeof(CustomInteractionHandler), "Interact", new Type[] { typeof(PlayerController) })] internal static class PartnerObjectActionsRunOnHost { private static bool Prefix(WGOInteractionHandlerBase __instance, PlayerController interactor, ref bool __result) { try { return InteractionProxy.BeforeCustomInteract(__instance, interactor, ref __result); } catch (Exception e) { CoopLog.Error("Sending an object's action to the host failed", e); return true; } } private static void Finalizer() { InteractionProxy.AfterCustomInteract(); } } [HarmonyPatch(typeof(UIGraveWindowData), "ExhumeBody")] internal static class PartnerExhumesOnHost { private static bool Prefix(UIGraveWindowData __instance) { try { return InteractionProxy.BeforeExhume(__instance); } catch (Exception e) { CoopLog.Error("Sending an exhumation to the host failed", e); return true; } } } internal static class ZombieBook { private static readonly FieldRef> CacheField = AccessTools.FieldRefAccess>("cache"); public static void OnZombieAdded(ZombieWgoData zombie) { ZombieSystemData zombieSystemData = MainGame.ZombieSystemData; if (zombieSystemData != null && !(((zombie != null) ? ((WgoData)zombie).UniqueId : null) == (SGuid)null)) { string uid = ((WgoData)zombie).UniqueId.Id; zombieSystemData.zombieDrops.RemoveAll((ZombieWgoData z) => ((z != null) ? ((WgoData)z).UniqueId : null) != (SGuid)null && ((WgoData)z).UniqueId.Id == uid); if (!zombieSystemData.zombieOnSceneWgoIds.Any((SGuid g) => g != (SGuid)null && g.Id == uid)) { zombieSystemData.zombieOnSceneWgoIds.Add(((WgoData)zombie).UniqueId); } CacheField.Invoke(zombieSystemData) = null; } } public static void OnZombieRemoved(ZombieWgoData zombie) { ZombieSystemData zombieSystemData = MainGame.ZombieSystemData; if (zombieSystemData != null && !(((zombie != null) ? ((WgoData)zombie).UniqueId : null) == (SGuid)null)) { string uid = ((WgoData)zombie).UniqueId.Id; zombieSystemData.zombieOnSceneWgoIds.RemoveAll((SGuid g) => g != (SGuid)null && g.Id == uid); if (!zombieSystemData.zombieDrops.Any((ZombieWgoData z) => ((z != null) ? ((WgoData)z).UniqueId : null) != (SGuid)null && ((WgoData)z).UniqueId.Id == uid)) { zombieSystemData.zombieDrops.Add(zombie); } CacheField.Invoke(zombieSystemData) = null; } } } [HarmonyPatch] internal static class PartnerZombieWorkRunsOnHost { private static IEnumerable TargetMethods() { Type[] array = new Type[13] { typeof(StationInteractionHandler), typeof(PorterStationInteractionHandler), typeof(GardenStationInteractionHandler), typeof(ConveyorTransporterStationInteractionHandler), typeof(PowerSourceInteractionHandler), typeof(ZombieCarrierInteractionHandler), typeof(ZombieClayInteractionHandler), typeof(ZombieMineInteractionHandler), typeof(ZombieSandInteractionHandler), typeof(ZombieSawmillInteractionHandler), typeof(ZombieInteractionHandler), typeof(CraftInteractionHandler), typeof(FightersContainerInteractionHandler) }; foreach (Type type in array) { MethodInfo methodInfo = AccessTools.Method(type, "Interact", new Type[1] { typeof(PlayerController) }, (Type[])null); if (methodInfo != null && methodInfo.DeclaringType == type) { yield return methodInfo; } } } private static bool Prefix(WGOInteractionHandlerBase __instance, PlayerController interactor, ref bool __result) { try { return InteractionProxy.BeforeInteract(__instance, interactor, ref __result); } catch (Exception e) { CoopLog.Error("Sending a zombie interaction to the host failed", e); return true; } } } [HarmonyPatch(typeof(PlayerInteractionComponent), "ResetInteractionState")] internal static class StandInSkipsInteractionReset { private static bool Prefix(PlayerInteractionComponent __instance) { return !PlayerSync.IsAvatarTransform(((Component)__instance).transform); } } [HarmonyPatch(typeof(PlayerController), "TryTeleportPlayerToAnyFreePlace")] internal static class StandInStaysPut { private static bool Prefix(PlayerController __instance) { return !PlayerSync.IsAvatarTransform(((Component)__instance).transform); } } [HarmonyPatch(typeof(WgoData), "TrySetWorker")] internal static class MirrorWorkerSet { private static void Postfix(WgoData __instance) { WorldSync.MarkStateDirty(__instance); } } [HarmonyPatch(typeof(WgoData), "ClearWorker")] internal static class MirrorWorkerCleared { private static void Postfix(WgoData __instance) { WorldSync.MarkStateDirty(__instance); } } [HarmonyPatch(typeof(WgoData), "OccupyDockPoint")] internal static class MirrorDockTaken { private static void Postfix(WgoData __instance, SGuid dockPointParent) { WorldSync.MarkStateDirty(__instance); WorldSync.MarkStateDirty(dockPointParent); } } [HarmonyPatch(typeof(WgoData), "UnOccupyDockPoint")] internal static class MirrorDockFreed { private static void Prefix(WgoData __instance, out SGuid __state) { __state = __instance.takenDockPointsParentSGuid; } private static void Postfix(WgoData __instance, SGuid __state) { WorldSync.MarkStateDirty(__instance); if (__state != (SGuid)null && !__state.IsEmpty) { WorldSync.MarkStateDirty(__state); } } } }