using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using LiteNetLib; using LiteNetLib.Layers; using LiteNetLib.Utils; using PsychoticLab; using SailwindCoop.Avatar; using SailwindCoop.Net; using SailwindCoop.Runtime; using SailwindCoop.Sync; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyCompany("SailwindCoop")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+b9182b1208195d9b9f018e19a846538a308e6d10")] [assembly: AssemblyProduct("SailwindCoop")] [assembly: AssemblyTitle("SailwindCoop")] [assembly: AssemblyVersion("1.0.0.0")] namespace SailwindCoop { [BepInPlugin("com.sailwind.coop", "Sailwind LAN Co-op", "0.1.3")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.sailwind.coop"; public const string Version = "0.1.3"; internal static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } internal static CoopConfig Cfg { get; private set; } internal static string AvatarBundlePath => Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), "avatar.bundle"); private void Awake() { //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_0045: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) Instance = this; Logger = ((BaseUnityPlugin)this).Logger; Cfg = new CoopConfig(((BaseUnityPlugin)this).Config); AvatarCatalog.Initialize(); Logger.LogInfo((object)"Sailwind LAN Co-op 0.1.3 loading..."); GameObject val = new GameObject("SailwindCoop"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent(); Logger.LogInfo((object)"Sailwind LAN Co-op ready. Use the in-game Co-op menu."); } } public sealed class CoopConfig { public readonly ConfigEntry ListenIp; public readonly ConfigEntry Port; public readonly ConfigEntry JoinIp; public readonly ConfigEntry PlayerName; public readonly ConfigEntry SnapshotHz; public readonly ConfigEntry InterpDelayMs; public readonly ConfigEntry AvatarVerticalOffset; public readonly ConfigEntry HostAvatarVerticalOffset; public readonly ConfigEntry MaxClients; public readonly ConfigEntry DisconnectTimeoutMs; public readonly ConfigEntry UpdateTimeMs; public readonly ConfigEntry PingIntervalMs; public readonly ConfigEntry ConnectAttempts; public readonly ConfigEntry ReconnectDelayMs; public readonly ConfigEntry CoopSaveSlot; public readonly ConfigEntry ForceHostSaveOnJoin; public readonly ConfigEntry PauseHostOnJoin; public readonly ConfigEntry EnableDebugPanel; public readonly ConfigEntry MenuKey; public CoopConfig(ConfigFile c) { Port = c.Bind("Network", "Port", 7777, "Host UDP port."); ListenIp = c.Bind("Network", "ListenIp", "0.0.0.0", "IP/interface the host listens on (0.0.0.0 = all interfaces). Applied when starting the host: a specific address accepts connections only on that interface."); JoinIp = c.Bind("Network", "JoinIp", "127.0.0.1", "Host IP for the client to join."); PlayerName = c.Bind("Network", "PlayerName", "Player", "Displayed player name."); SnapshotHz = c.Bind("Network", "SnapshotHz", 20, "State snapshot send rate (Hz), Stage 1+."); InterpDelayMs = c.Bind("Network", "InterpDelayMs", 100f, "Interpolation buffer delay (ms), Stage 1+."); AvatarVerticalOffset = c.Bind("Avatar", "VerticalOffset", -0.6f, "Vertical offset of the visual bundle model relative to the networked player position. Negative values move the model down."); HostAvatarVerticalOffset = c.Bind("Avatar", "HostVerticalOffset", -0.6f, "Vertical offset of the host visual bundle model. Separate because the host root pose in Sailwind is usually higher than the client pose."); MaxClients = c.Bind("Server", "MaxClients", 4, "Maximum number of clients connected to the host at once (1 = single guest only). Applied on incoming connections."); DisconnectTimeoutMs = c.Bind("Server", "DisconnectTimeoutMs", 5000, "Timeout (ms) without packets from a peer before it is considered disconnected."); UpdateTimeMs = c.Bind("Server", "UpdateTimeMs", 15, "Internal network manager update interval (ms). Lower = more frequent polling/sending, higher CPU load."); PingIntervalMs = c.Bind("Server", "PingIntervalMs", 1000, "Ping interval (ms) for latency estimation and connection keepalive."); ConnectAttempts = c.Bind("Client", "ConnectAttempts", 10, "How many times the client tries to reach the host before reporting a connection error."); ReconnectDelayMs = c.Bind("Client", "ReconnectDelayMs", 500, "Delay (ms) between client connection attempts."); CoopSaveSlot = c.Bind("Save", "CoopSaveSlot", 5, "Save slot (0..5) where the client writes the received host world and loads from it. WARNING: the local save in this slot on the client is overwritten. Join from the main menu."); ForceHostSaveOnJoin = c.Bind("Save", "ForceHostSaveOnJoin", true, "When a client joins, the host makes a fresh save so the client receives the current world (economy/objects/position). Disable to send the latest autosave without forcing a save."); PauseHostOnJoin = c.Bind("Save", "PauseHostOnJoin", true, "While the client loads the host world, the host world is paused (timeScale=0, like the settings menu) so items/anchor/moorings/waves match the snapshot on the client. The pause is lifted when the client reports loaded, disconnects, or after a 120 s timeout."); EnableDebugPanel = c.Bind("Debug", "EnableDebugPanel", false, "Developer/test panel for gold/spawn/reputation/world tools. Keep false for public builds."); MenuKey = c.Bind("UI", "MenuKey", (KeyCode)289, "Show/hide the co-op menu."); } } } namespace SailwindCoop.Sync { public sealed class AnchorSync { private readonly CoopNet _net; private readonly NetTransform _slave = new NetTransform(); private PlayerEmbarkerNew _emb; private Transform _cachedBoat; private Anchor _anchor; private Transform _anchorTr; private static FieldInfo _fBody; private static FieldInfo _fSet; private float _sendTimer; private Vector3 _lastRealPos; private long _lastRealTick; private bool _haveLast; private CoordFrame _lastFrame; private Rigidbody _anchorRb; private bool _prevKinematic; private RigidbodyInterpolation _prevInterp; private bool _slaved; private CoordFrame _curFrame; private bool _haveFrame; public float SnapshotHz = 12f; private const float DeployedLen = 0.5f; public bool HasAnchor => (Object)(object)_anchor != (Object)null; public bool ClientSet { get; private set; } public bool Slaving => _slaved; public string AnchorText { get { if ((Object)(object)_anchor == (Object)null) { return "no anchor"; } if (_net.Role == Role.Host) { if (!_anchor.IsSet()) { return "host: raised"; } return "host: dropped"; } return (_slaved ? "slaved" : "—") + (ClientSet ? " dropped" : " raised") + " [" + _curFrame.ToString() + "]"; } } public AnchorSync(CoopNet net) { _net = net; } public void Tick(float dt) { //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_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) //IL_015b: 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_016e: 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_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_00e3: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: 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_01b2: 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_0133: 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_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Host || _net.State != LinkState.Connected || !CoordSpace.Ready) { return; } RefreshAnchor(); if ((Object)(object)_anchor == (Object)null || (Object)(object)_anchorTr == (Object)null) { return; } Transform cachedBoat = _cachedBoat; if ((Object)(object)cachedBoat == (Object)null) { return; } float num = 1f / Mathf.Max(1f, SnapshotHz); _sendTimer += dt; if (_sendTimer < num) { return; } _sendTimer = 0f; long serverTick = _net.Clock.ServerTick; CoordFrame coordFrame = ((!_anchor.IsSet() && !(GetRopeLen() > 0.5f)) ? CoordFrame.Boat : CoordFrame.World); Vector3 vel = Vector3.zero; Vector3 val; Quaternion rot; if (coordFrame == CoordFrame.World) { val = CoordSpace.LocalToReal(_anchorTr.position); rot = _anchorTr.rotation; if (_haveLast && _lastFrame == CoordFrame.World) { float num2 = (float)(serverTick - _lastRealTick) / 1000f; if (num2 > 0.0001f) { vel = (val - _lastRealPos) / num2; } } _lastRealPos = val; _lastRealTick = serverTick; _haveLast = true; } else { val = cachedBoat.InverseTransformPoint(_anchorTr.position); rot = Quaternion.Inverse(cachedBoat.rotation) * _anchorTr.rotation; _haveLast = false; } _lastFrame = coordFrame; _net.Broadcast(new AnchorStateMsg { Tick = serverTick, Frame = coordFrame, Pos = val, Rot = rot, Vel = vel, Set = _anchor.IsSet() }, (DeliveryMethod)4); } public void OnAnchorState(AnchorStateMsg msg, NetPeer fromPeer) { //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_008b: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client) { return; } RefreshAnchor(); if ((Object)(object)_anchor == (Object)null || (Object)(object)_anchorTr == (Object)null) { return; } EnsureSlaved(); if (!_haveFrame || msg.Frame != _curFrame) { _curFrame = msg.Frame; _haveFrame = true; _slave.Clear(); ApplyFrameConverters(); } _slave.Push(msg.Tick, msg.Pos, msg.Rot, msg.Vel); ClientSet = msg.Set; if (!(_fSet != null)) { return; } try { _fSet.SetValue(_anchor, msg.Set); } catch { } } public void ApplyRemote() { if (_net.Role == Role.Client && _slaved && !((Object)(object)_anchorTr == (Object)null) && _slave.HasData && CoordSpace.Ready && !(Time.timeScale <= 0.0001f)) { _slave.Apply(_anchorTr, _net.Clock.ServerTick); } } private void EnsureSlaved() { //IL_0049: 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) if (!_slaved && !((Object)(object)_anchor == (Object)null)) { _anchorRb = GetAnchorBody(); if ((Object)(object)_anchorRb != (Object)null) { _prevKinematic = _anchorRb.isKinematic; _prevInterp = _anchorRb.interpolation; _anchorRb.isKinematic = true; _anchorRb.interpolation = (RigidbodyInterpolation)0; } _slaved = true; Plugin.Logger.LogInfo((object)("[AnchorSync] Client anchor in slave mode (rb=" + ((Object)(object)_anchorRb != (Object)null) + ")")); } } private void RestoreSlaved() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_anchorRb != (Object)null) { _anchorRb.isKinematic = _prevKinematic; _anchorRb.interpolation = _prevInterp; } _anchorRb = null; _slaved = false; _haveFrame = false; _slave.Clear(); } private void RefreshAnchor() { if ((Object)(object)_emb == (Object)null) { _emb = Object.FindObjectOfType(); } Transform val = (((Object)(object)_emb != (Object)null) ? _emb.debugOutCurrentBoat : null); if ((Object)(object)val == (Object)(object)_cachedBoat) { return; } RestoreSlaved(); _cachedBoat = val; _anchor = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInChildren(true) : null); _anchorTr = (((Object)(object)_anchor != (Object)null) ? ((Component)_anchor).transform : null); _haveLast = false; if ((Object)(object)_anchor != (Object)null) { if (_fBody == null) { _fBody = typeof(Anchor).GetField("body", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_fSet == null) { _fSet = typeof(Anchor).GetField("set", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } Plugin.Logger.LogInfo((object)("[AnchorSync] Anchor found on '" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "?") + "'")); } } private Rigidbody GetAnchorBody() { try { if (_fBody != null) { object? value = _fBody.GetValue(_anchor); Rigidbody val = (Rigidbody)((value is Rigidbody) ? value : null); if (val != null && (Object)(object)val != (Object)null) { return val; } } } catch { } if (!((Object)(object)_anchorTr != (Object)null)) { return null; } return ((Component)_anchorTr).GetComponent(); } private float GetRopeLen() { try { return _anchor.GetRopeLength(); } catch { return 0f; } } private void ApplyFrameConverters() { if (_curFrame == CoordFrame.Boat && (Object)(object)_cachedBoat != (Object)null) { Transform boat = _cachedBoat; _slave.ToWorldPos = (Vector3 p) => boat.TransformPoint(p); _slave.ToWorldRot = (Quaternion r) => boat.rotation * r; } else { _slave.ToWorldPos = CoordSpace.RealToLocal; _slave.ToWorldRot = (Quaternion r) => r; } } public void Clear() { RestoreSlaved(); _cachedBoat = null; _anchor = null; _anchorTr = null; _haveLast = false; _sendTimer = 0f; ClientSet = false; } } public sealed class BoatDamageSync { private readonly CoopNet _net; private PlayerEmbarkerNew _emb; private Transform _cachedBoat; private BoatDamage _damage; private BilgePump[] _pumps = Array.Empty(); private readonly Dictionary> _heldPumpsByActor = new Dictionary>(); private float _sendTimer; private string _lastPump = "—"; private long _lastPumpTick; private string _lastRepair = "—"; private long _lastRepairTick; private const float PumpInput = 50f; public float SnapshotHz = 4f; public static BoatDamageSync Instance { get; private set; } public string DamageText { get { if ((Object)(object)_damage == (Object)null) { return "no BoatDamage"; } string text = "—"; if (_lastPumpTick != 0L) { long num = _net.Clock.ServerTick - _lastPumpTick; if (num < 0) { num = 0L; } text = _lastPump + " " + num + "ms"; } string text2 = "—"; if (_lastRepairTick != 0L) { long num2 = _net.Clock.ServerTick - _lastRepairTick; if (num2 < 0) { num2 = 0L; } text2 = _lastRepair + " " + num2 + "ms"; } return "water=" + _damage.waterLevel.ToString("0.000") + " hull=" + _damage.hullDamage.ToString("0.000") + " oakum=" + _damage.oakum.ToString("0.0") + " pump " + ActivePumpCount() + "/" + _pumps.Length + " · " + text + " · " + text2; } } public BoatDamageSync(CoopNet net) { _net = net; Instance = this; } public void Tick(float dt) { if (_net.State == LinkState.Connected) { RefreshBoat(); if (_net.Role == Role.Host) { ApplyRemotePumps(dt); SendSnapshot(dt); } } } public void SetRemotePump(ushort index, bool down, uint actorNetId) { if (_net.Role != Role.Host) { return; } RefreshBoat(); if (index >= _pumps.Length) { Plugin.Logger.LogWarning((object)("[BoatDamageSync] Pump request #" + index + ": boat only has " + _pumps.Length)); return; } if (!_heldPumpsByActor.TryGetValue(actorNetId, out var value)) { value = new HashSet(); _heldPumpsByActor[actorNetId] = value; } if (down) { value.Add(index); } else { value.Remove(index); } if (value.Count == 0) { _heldPumpsByActor.Remove(actorNetId); } RememberPump((down ? "in down" : "in up") + " #" + index + " p" + actorNetId); Plugin.Logger.LogInfo((object)("[BoatDamageSync] Pump #" + index + " from player " + actorNetId + ": " + (down ? "held" : "released"))); } public void ClearRemoteActor(uint actorNetId) { if (_heldPumpsByActor.Remove(actorNetId)) { Plugin.Logger.LogInfo((object)("[BoatDamageSync] Cleared pump holds for player " + actorNetId)); } } public void OnDamageState(BoatDamageStateMsg msg, NetPeer fromPeer) { if (_net.Role == Role.Client) { RefreshBoat(); if (!((Object)(object)_damage == (Object)null)) { _damage.waterLevel = Mathf.Clamp01(msg.WaterLevel); _damage.hullDamage = Mathf.Clamp01(msg.HullDamage); _damage.oakum = Mathf.Max(0f, msg.Oakum); _damage.waterIntakeChunk = Mathf.Max(0f, msg.WaterIntakeChunk); _damage.sunk = msg.Sunk; } } } public void NotifyLocalDamageAction(DamageAction action, float amount) { if (_net.Role == Role.Client && _net.State == LinkState.Connected && !(amount <= 1E-05f)) { _net.Broadcast(new DamageRequestMsg { Action = action, Amount = amount }, (DeliveryMethod)2); RememberRepair("out " + action.ToString() + " +" + amount.ToString("0.000")); } } public void OnDamageRequest(DamageRequestMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Host) { return; } RefreshBoat(); if ((Object)(object)_damage == (Object)null) { return; } float num = Mathf.Max(0f, msg.Amount); if (num <= 0f) { return; } if (msg.Action == DamageAction.AddOakum) { float num2 = Mathf.Max(0f, _damage.hullDamage * _damage.waterUnitsCapacity - _damage.oakum); float num3 = Mathf.Min(num, num2); if (num3 > 0f) { BoatDamage damage = _damage; damage.oakum += num3; } RememberRepair("in oakum +" + num3.ToString("0.000")); } else if (msg.Action == DamageAction.BailWater) { float waterLevel = _damage.waterLevel; _damage.waterLevel = Mathf.Clamp01(_damage.waterLevel - num); RememberRepair("in water -" + (waterLevel - _damage.waterLevel).ToString("0.000")); } BroadcastSnapshot(); } private void SendSnapshot(float dt) { if (!((Object)(object)_damage == (Object)null)) { float num = 1f / Mathf.Max(0.5f, SnapshotHz); _sendTimer += dt; if (!(_sendTimer < num)) { _sendTimer = 0f; BroadcastSnapshot(); } } } private void BroadcastSnapshot() { if (!((Object)(object)_damage == (Object)null)) { _net.Broadcast(new BoatDamageStateMsg { Tick = _net.Clock.ServerTick, WaterLevel = _damage.waterLevel, HullDamage = _damage.hullDamage, Oakum = _damage.oakum, WaterIntakeChunk = _damage.waterIntakeChunk, Sunk = _damage.sunk }, (DeliveryMethod)4); } } private void ApplyRemotePumps(float dt) { if ((Object)(object)_damage == (Object)null || _damage.sunk || _heldPumpsByActor.Count == 0) { return; } bool flag = false; for (int i = 0; i < _pumps.Length; i++) { if (IsPumpHeld((ushort)i)) { BilgePump val = _pumps[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.damage == (Object)null)) { float waterLevel = val.damage.waterLevel; val.damage.waterLevel = Mathf.Clamp01(val.damage.waterLevel - dt * 50f * val.drainRate); RotatePumpVisual(val, dt); flag = flag || val.damage.waterLevel != waterLevel; } } } } private static void RotatePumpVisual(BilgePump pump, float dt) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)pump == (Object)null)) { float num = (((Object)(object)pump.damage != (Object)null && pump.damage.waterLevel > 0f) ? 0.75f : 2.25f); ((Component)pump).transform.Rotate(Vector3.forward, 50f * dt * 1.4f * pump.rotationSpeed * num, (Space)1); } } private bool IsPumpHeld(ushort index) { foreach (HashSet value in _heldPumpsByActor.Values) { if (value.Contains(index)) { return true; } } return false; } private int ActivePumpCount() { int num = 0; for (int i = 0; i < _pumps.Length; i++) { if (IsPumpHeld((ushort)i)) { num++; } } return num; } private void RefreshBoat() { if ((Object)(object)_emb == (Object)null) { _emb = Object.FindObjectOfType(); } Transform val = (((Object)(object)_emb != (Object)null) ? _emb.debugOutCurrentBoat : null); if (!((Object)(object)val == (Object)(object)_cachedBoat)) { _cachedBoat = val; _heldPumpsByActor.Clear(); _sendTimer = 0f; if ((Object)(object)val == (Object)null) { _damage = null; _pumps = Array.Empty(); return; } _damage = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInParent() ?? ((Component)val).GetComponentInChildren(true); _pumps = ((Component)val).GetComponentsInChildren(true); Plugin.Logger.LogInfo((object)("[BoatDamageSync] Boat changed: damage=" + ((Object)(object)_damage != (Object)null) + ", pumps=" + _pumps.Length + " ('" + ((Object)val).name + "')")); } } private void RememberPump(string text) { _lastPump = text; _lastPumpTick = _net.Clock.ServerTick; } private void RememberRepair(string text) { _lastRepair = text; _lastRepairTick = _net.Clock.ServerTick; } public void Clear() { _cachedBoat = null; _damage = null; _pumps = Array.Empty(); _heldPumpsByActor.Clear(); _sendTimer = 0f; _lastPump = "—"; _lastPumpTick = 0L; _lastRepair = "—"; _lastRepairTick = 0L; } } public static class BoatDamagePatches { private struct WaterBailState { public float Water; public float Amount; public float Health; } public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, typeof(HullDamageButton), "OnItemClick", new Type[1] { typeof(PickupableItem) }, "PreHullOakum", "PostHullOakum"); bool flag2 = TryPatch(harmony, typeof(BoatDamageWaterButton), "OnItemClick", new Type[1] { typeof(PickupableItem) }, "PreWaterBail", "PostWaterBail"); bool flag3 = TryPatch(harmony, typeof(ShipItemOakum), "OnAltActivate", Type.EmptyTypes, "PreOakumAlt", "PostOakumAlt"); Plugin.Logger.LogInfo((object)("[BoatDamagePatches] Damage patches: HullOakum=" + flag + ", WaterBail=" + flag2 + ", OakumAlt=" + flag3)); PatchHealth.Report("Damage", (flag ? 1 : 0) + (flag2 ? 1 : 0) + (flag3 ? 1 : 0), 3); } private static bool TryPatch(Harmony harmony, Type type, string method, Type[] args, string prefixName, string postfixName) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown try { MethodInfo method2 = type.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method2 == null) { Plugin.Logger.LogWarning((object)("[BoatDamagePatches] Not found " + type.Name + "." + method)); return false; } HarmonyMethod val = new HarmonyMethod(typeof(BoatDamagePatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val2 = new HarmonyMethod(typeof(BoatDamagePatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[BoatDamagePatches] " + type.Name + "." + method + ": " + ex.Message)); return false; } } private static void PreHullOakum(HullDamageButton __instance, out float __state) { __state = ReadOakum(GetHullDamage(__instance)); } private static void PostHullOakum(HullDamageButton __instance, float __state) { try { float num = ReadOakum(GetHullDamage(__instance)) - __state; if (num > 1E-05f) { BoatDamageSync.Instance?.NotifyLocalDamageAction(DamageAction.AddOakum, num); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[BoatDamagePatches] PostHullOakum: " + ex.Message)); } } private static void PreWaterBail(BoatDamageWaterButton __instance, PickupableItem __0, out WaterBailState __state) { __state = new WaterBailState { Water = ReadWater(GetWaterDamage(__instance)) }; ShipItemBottle val = (ShipItemBottle)(object)((__0 is ShipItemBottle) ? __0 : null); if ((Object)(object)val != (Object)null) { __state.Amount = ((ShipItem)val).amount; __state.Health = ((ShipItem)val).health; } } private static void PostWaterBail(BoatDamageWaterButton __instance, PickupableItem __0, WaterBailState __state) { try { float num = ReadWater(GetWaterDamage(__instance)); float num2 = __state.Water - num; ShipItemBottle val = (ShipItemBottle)(object)((__0 is ShipItemBottle) ? __0 : null); bool flag = (Object)(object)val != (Object)null && (Mathf.Abs(((ShipItem)val).amount - __state.Amount) > 0.0001f || Mathf.Abs(((ShipItem)val).health - __state.Health) > 0.0001f); if (num2 > 1E-05f) { BoatDamageSync.Instance?.NotifyLocalDamageAction(DamageAction.BailWater, num2); } if (flag) { ItemSync.Instance?.NotifyHeldItemStateChanged((ShipItem)(object)val, "bail-water"); } if (num2 > 1E-05f || flag) { Plugin.Logger.LogInfo((object)("[BoatDamagePatches] WaterBail item=" + (((Object)(object)__0 != (Object)null) ? ((object)__0).GetType().Name : "null") + " delta=" + num2.ToString("0.####") + " amount " + __state.Amount.ToString("0.##") + "->" + (((Object)(object)val != (Object)null) ? ((ShipItem)val).amount.ToString("0.##") : "?") + " health " + __state.Health.ToString("0.##") + "->" + (((Object)(object)val != (Object)null) ? ((ShipItem)val).health.ToString("0.##") : "?"))); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[BoatDamagePatches] PostWaterBail: " + ex.Message)); } } private static void PreOakumAlt(ShipItemOakum __instance, out float __state) { __state = ReadOakum(CurrentBoatDamage()); } private static void PostOakumAlt(ShipItemOakum __instance, float __state) { try { float num = ReadOakum(CurrentBoatDamage()) - __state; if (num > 1E-05f) { BoatDamageSync.Instance?.NotifyLocalDamageAction(DamageAction.AddOakum, num); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[BoatDamagePatches] PostOakumAlt: " + ex.Message)); } } private static BoatDamage GetHullDamage(HullDamageButton button) { HullDamageTexture val = (((Object)(object)button != (Object)null) ? ((Component)button).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.damage; } private static BoatDamage GetWaterDamage(BoatDamageWaterButton button) { BoatDamageWater val = (((Object)(object)button != (Object)null) ? ((Component)button).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.damage; } private static BoatDamage CurrentBoatDamage() { try { return ((Object)(object)GameState.currentBoat != (Object)null && (Object)(object)GameState.currentBoat.parent != (Object)null) ? ((Component)GameState.currentBoat.parent).GetComponent() : null; } catch { return null; } } private static float ReadOakum(BoatDamage damage) { if (!((Object)(object)damage != (Object)null)) { return 0f; } return damage.oakum; } private static float ReadWater(BoatDamage damage) { if (!((Object)(object)damage != (Object)null)) { return 0f; } return damage.waterLevel; } } public static class BoatLocator { public const ushort NoBoat = ushort.MaxValue; public static List FindBoats() { HashSet hashSet = new HashSet(); BoatEmbarkCollider[] array = Object.FindObjectsOfType(); foreach (BoatEmbarkCollider val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null) && !((Object)(object)((Component)val).transform.parent == (Object)null)) { Transform parent = ((Component)val).transform.parent; if (IsNetworkBoat(parent)) { hashSet.Add(parent); } } } List list = new List(hashSet); list.Sort(delegate(Transform a, Transform b) { int num = (IsPurchasable(a) ? 1 : 0); int num2 = (IsPurchasable(b) ? 1 : 0); return (num != num2) ? (num - num2) : string.CompareOrdinal(PathOf(a), PathOf(b)); }); return list; } private static bool IsPurchasable(Transform worldBoat) { if ((Object)(object)worldBoat == (Object)null) { return false; } return (Object)(object)((Component)(((Object)(object)worldBoat.parent != (Object)null) ? worldBoat.parent : worldBoat)).GetComponent("PurchasableBoat") != (Object)null; } private static bool IsNetworkBoat(Transform worldBoat) { if ((Object)(object)worldBoat == (Object)null) { return false; } Transform obj = (((Object)(object)worldBoat.parent != (Object)null) ? worldBoat.parent : worldBoat); SaveableObject component = ((Component)obj).GetComponent(); Component component2 = ((Component)obj).GetComponent("BoatProbes"); if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && !component.extraSetting) { return false; } return true; } public static Transform FindByIndex(ushort index) { if (index == ushort.MaxValue) { return null; } List list = FindBoats(); if (index >= list.Count) { return null; } return list[index]; } public static ushort IndexOf(Transform boat) { if ((Object)(object)boat == (Object)null) { return ushort.MaxValue; } List list = FindBoats(); for (int i = 0; i < list.Count; i++) { if ((Object)(object)list[i] == (Object)(object)boat) { return (ushort)i; } } return ushort.MaxValue; } public static string PathOf(Transform t) { if ((Object)(object)t == (Object)null) { return ""; } string text = ((Object)t).name; Transform parent = t.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } public sealed class BoatSync { private sealed class HostBoat { public ushort Index; public uint NetId; public Transform Boat; public Vector3 LastRealPos; public long LastRealTick; public bool HaveLast; } private sealed class ClientBoat { public ushort Index; public Transform Boat; public readonly NetTransform Net = new NetTransform(); public Rigidbody Rb; public bool PrevKinematic; public RigidbodyInterpolation PrevInterp; public Component PhysSwitcher; public bool PrevPaused; public bool PrevSwitcherEnabled; } private readonly CoopNet _net; private readonly Dictionary _hostBoats = new Dictionary(); private readonly Dictionary _clientBoats = new Dictionary(); private float _sendTimer; private float _refreshTimer; private uint _firstBoatNetId; private static Type _physSwitcherType; public float InterpDelayMs = 100f; public int SnapshotHz = 20; public bool IsSlaving => _clientBoats.Count > 0; public uint BoatNetId => _firstBoatNetId; public int BoatCount { get { if (_net.Role != Role.Host) { return _clientBoats.Count; } return _hostBoats.Count; } } public BoatSync(CoopNet net) { _net = net; } public void Tick(float dt) { //IL_00b8: 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_00c2: 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_00c9: 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_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0144: 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) //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_015d: 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_00ed: 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_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) if (_net.Role != Role.Host || _net.State != LinkState.Connected || !CoordSpace.Ready) { return; } RefreshHostBoats(dt); if (_hostBoats.Count == 0) { return; } float num = 1f / (float)Mathf.Max(1, SnapshotHz); _sendTimer += dt; if (_sendTimer < num) { return; } _sendTimer = 0f; long serverTick = _net.Clock.ServerTick; foreach (HostBoat value in _hostBoats.Values) { if ((Object)(object)value.Boat == (Object)null) { continue; } Vector3 val = CoordSpace.LocalToReal(value.Boat.position); Vector3 realVel = Vector3.zero; if (value.HaveLast) { float num2 = (float)(serverTick - value.LastRealTick) / 1000f; if (num2 > 0.0001f) { realVel = (val - value.LastRealPos) / num2; } } value.LastRealPos = val; value.LastRealTick = serverTick; value.HaveLast = true; _net.Broadcast(new BoatStateMsg { NetId = value.NetId, BoatIndex = value.Index, Tick = serverTick, RealPos = val, Rot = value.Boat.rotation, RealVel = realVel }, (DeliveryMethod)4); } } public void OnBoatState(BoatStateMsg msg, NetPeer fromPeer) { //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_0061: Unknown result type (might be due to invalid IL or missing references) if (_net.Role == Role.Client) { Transform val = BoatLocator.FindByIndex(msg.BoatIndex); if (!((Object)(object)val == (Object)null)) { ClientBoat clientBoat = EnsureClientBoat(msg.BoatIndex, msg.NetId, val); clientBoat.Net.InterpDelayMs = InterpDelayMs; clientBoat.Net.Push(msg.Tick, msg.RealPos, msg.Rot, msg.RealVel); } } } public void ApplyRemote() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client || !CoordSpace.Ready || Time.timeScale <= 0.0001f) { return; } foreach (ClientBoat value in _clientBoats.Values) { if (!((Object)(object)value.Boat == (Object)null) && value.Net.HasData) { if ((Object)(object)value.Rb != (Object)null && (int)value.Rb.interpolation != 0) { value.Rb.interpolation = (RigidbodyInterpolation)0; } value.Net.Apply(value.Boat, _net.Clock.ServerTick); } } } public Transform GetBoatByIndex(ushort index) { if (_net.Role == Role.Client && _clientBoats.TryGetValue(index, out var value) && (Object)(object)value.Boat != (Object)null) { return value.Boat; } if (_net.Role == Role.Host && _hostBoats.TryGetValue(index, out var value2) && (Object)(object)value2.Boat != (Object)null) { return value2.Boat; } return BoatLocator.FindByIndex(index); } private void RefreshHostBoats(float dt) { _refreshTimer += dt; if (_refreshTimer < 1f && _hostBoats.Count > 0) { return; } _refreshTimer = 0f; List list = BoatLocator.FindBoats(); HashSet hashSet = new HashSet(); for (int i = 0; i < list.Count && i <= 65534; i++) { Transform val = list[i]; if ((Object)(object)val == (Object)null) { continue; } ushort num = (ushort)i; hashSet.Add(num); if (!_hostBoats.TryGetValue(num, out var value)) { value = new HostBoat { Index = num, NetId = _net.Registry.AllocateId(), Boat = val }; _hostBoats[num] = value; _net.Registry.Register(value.NetId, NetObjKind.Boat, 0u, val); if (_firstBoatNetId == 0) { _firstBoatNetId = value.NetId; } Plugin.Logger.LogInfo((object)("[BoatSync] Boat #" + num + " registered NetId=" + value.NetId + " ('" + BoatLocator.PathOf(val) + "')")); } else if ((Object)(object)value.Boat != (Object)(object)val) { value.Boat = val; value.HaveLast = false; _net.Registry.Register(value.NetId, NetObjKind.Boat, 0u, val); Plugin.Logger.LogInfo((object)("[BoatSync] Boat #" + num + " rebound ('" + BoatLocator.PathOf(val) + "')")); } } List list2 = new List(); foreach (KeyValuePair hostBoat in _hostBoats) { if (!hashSet.Contains(hostBoat.Key)) { list2.Add(hostBoat.Key); } } foreach (ushort item in list2) { _net.Registry.Remove(_hostBoats[item].NetId); _hostBoats.Remove(item); } } private ClientBoat EnsureClientBoat(ushort index, uint netId, Transform boat) { //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) if (_clientBoats.TryGetValue(index, out var value)) { if ((Object)(object)value.Boat == (Object)(object)boat) { return value; } RestoreClientBoat(value); _clientBoats.Remove(index); } value = new ClientBoat { Index = index, Boat = boat }; value.Rb = ((Component)boat).GetComponent(); if ((Object)(object)value.Rb != (Object)null) { value.PrevKinematic = value.Rb.isKinematic; value.PrevInterp = value.Rb.interpolation; value.Rb.isKinematic = true; value.Rb.interpolation = (RigidbodyInterpolation)0; } TrySetPhysicsPaused(value, paused: true); TrySetSwitcherEnabled(value, slave: true); _clientBoats[index] = value; _net.Registry.Register(netId, NetObjKind.Boat, 0u, boat); if (_firstBoatNetId == 0) { _firstBoatNetId = netId; } Plugin.Logger.LogInfo((object)("[BoatSync] Client boat #" + index + " in slave mode: NetId=" + netId + " ('" + BoatLocator.PathOf(boat) + "'), rb=" + ((Object)(object)value.Rb != (Object)null) + ", physSwitcher=" + ((Object)(object)value.PhysSwitcher != (Object)null))); return value; } private void RestoreClientBoat(ClientBoat cb) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (cb != null) { if ((Object)(object)cb.Rb != (Object)null) { cb.Rb.isKinematic = cb.PrevKinematic; cb.Rb.interpolation = cb.PrevInterp; } TrySetSwitcherEnabled(cb, slave: false); TrySetPhysicsPaused(cb, paused: false, restore: true); cb.Net.Clear(); cb.Rb = null; cb.PhysSwitcher = null; cb.Boat = null; } } private void TrySetSwitcherEnabled(ClientBoat cb, bool slave) { try { if (cb == null || (Object)(object)cb.Boat == (Object)null) { return; } if ((Object)(object)cb.PhysSwitcher == (Object)null) { if (_physSwitcherType == null) { _physSwitcherType = Type.GetType("BoatPhysicsSwitcher, Assembly-CSharp"); } if (_physSwitcherType == null) { return; } cb.PhysSwitcher = ((Component)cb.Boat).GetComponentInChildren(_physSwitcherType); } Component physSwitcher = cb.PhysSwitcher; Behaviour val = (Behaviour)(object)((physSwitcher is Behaviour) ? physSwitcher : null); if (!((Object)(object)val == (Object)null)) { if (slave) { cb.PrevSwitcherEnabled = val.enabled; val.enabled = false; } else { val.enabled = cb.PrevSwitcherEnabled; } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[BoatSync] BoatPhysicsSwitcher enable-toggle failed: " + ex.Message)); } } private void TrySetPhysicsPaused(ClientBoat cb, bool paused, bool restore = false) { try { if (cb == null || (Object)(object)cb.Boat == (Object)null) { return; } if (_physSwitcherType == null) { _physSwitcherType = Type.GetType("BoatPhysicsSwitcher, Assembly-CSharp"); } if (_physSwitcherType == null) { return; } if ((Object)(object)cb.PhysSwitcher == (Object)null) { cb.PhysSwitcher = ((Component)cb.Boat).GetComponentInChildren(_physSwitcherType); } if ((Object)(object)cb.PhysSwitcher == (Object)null) { return; } FieldInfo field = _physSwitcherType.GetField("paused"); PropertyInfo propertyInfo = ((field == null) ? _physSwitcherType.GetProperty("paused") : null); if (!(field == null) || !(propertyInfo == null)) { if (!restore) { cb.PrevPaused = ((field != null) ? ((bool)field.GetValue(cb.PhysSwitcher)) : ((bool)propertyInfo.GetValue(cb.PhysSwitcher, null))); } bool flag = (restore ? cb.PrevPaused : paused); if (field != null) { field.SetValue(cb.PhysSwitcher, flag); } else { propertyInfo.SetValue(cb.PhysSwitcher, flag, null); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[BoatSync] BoatPhysicsSwitcher.paused unavailable: " + ex.Message)); } } public void Clear() { foreach (ClientBoat value in _clientBoats.Values) { RestoreClientBoat(value); } _clientBoats.Clear(); _hostBoats.Clear(); _firstBoatNetId = 0u; _sendTimer = 0f; _refreshTimer = 0f; } } public sealed class ControlsSync { private struct Node { public Transform T; public Rigidbody Rb; } private readonly CoopNet _net; private PlayerEmbarkerNew _emb; private Transform _cachedBoat; private RopeController[] _ropes = Array.Empty(); private GPButtonRopeWinch[] _winches = Array.Empty(); private Node[] _nodes = Array.Empty(); private GPButtonSteeringWheel[] _wheels = Array.Empty(); private float[] _steerLastSent = Array.Empty(); private float _steerTimer; private MethodInfo _miApplyRudder; private const float SteerEps = 0.01f; private RopeControllerSteeringWheel _steerRope; private RudderNew _rudderNew; private Rudder _rudderOld; private Quaternion[] _targetRots = Array.Empty(); private bool _haveTargets; private readonly List> _kinematicSaved = new List>(); private float[] _hostLen = Array.Empty(); private long[] _localUntil = Array.Empty(); private float[] _lastSentLen = Array.Empty(); private float _reqTimer; private const float LocalHoldMs = 600f; private const float LenEps = 1E-05f; private int _lastReqIndex = -1; private float _lastReqLength; private bool _lastReqHasWinchRotation; private bool _lastReqIncoming; private long _lastReqTick; private float _sendTimer; private bool _warnedMismatch; private GoPointer _gp; private FieldInfo _fSticky; public float ControlHz = 12f; public float RotSmoothing = 14f; private FieldInfo _fRudderNewAngle; private FieldInfo _fRudderOldAngle; public int RopeCount => _ropes.Length; public int NodeCount => _nodes.Length; public int WinchCount => _winches.Length; public string SteeringText { get { if (_wheels.Length == 0) { return "no wheel"; } string text = (((Object)(object)_steerRope != (Object)null) ? ((RopeController)_steerRope).currentLength.ToString("0.000") : "—"); string text2 = RudderAngleText(); return _wheels.Length + " pcs len=" + text + " angle=" + text2; } } public string LastControlRequestText { get { if (_lastReqIndex < 0) { return "—"; } long num = _net.Clock.ServerTick - _lastReqTick; if (num < 0) { num = 0L; } string text = (_lastReqIncoming ? "in" : "out"); string text2 = (_lastReqHasWinchRotation ? "+handle" : "no handle"); return text + " #" + _lastReqIndex + " len=" + _lastReqLength.ToString("0.00") + " " + text2 + " " + num + "ms"; } } public ControlsSync(CoopNet net) { _net = net; } private string RudderAngleText() { try { if ((Object)(object)_rudderNew != (Object)null) { if (_fRudderNewAngle == null) { _fRudderNewAngle = typeof(RudderNew).GetField("currentAngle", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_fRudderNewAngle != null) { return ((float)_fRudderNewAngle.GetValue(_rudderNew)).ToString("0.0"); } } if ((Object)(object)_rudderOld != (Object)null) { if (_fRudderOldAngle == null) { _fRudderOldAngle = typeof(Rudder).GetField("currentAngle", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_fRudderOldAngle != null) { return ((float)_fRudderOldAngle.GetValue(_rudderOld)).ToString("0.0"); } } } catch { } return "—"; } public void Tick(float dt) { //IL_0102: 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_0107: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Host || _net.State != LinkState.Connected) { return; } RefreshNodes(); if (_ropes.Length == 0 && _nodes.Length == 0) { return; } float num = 1f / Mathf.Max(1f, ControlHz); _sendTimer += dt; if (!(_sendTimer < num)) { _sendTimer = 0f; float[] array = new float[_ropes.Length]; for (int i = 0; i < _ropes.Length; i++) { array[i] = (((Object)(object)_ropes[i] != (Object)null) ? _ropes[i].currentLength : 0f); } Quaternion[] array2 = (Quaternion[])(object)new Quaternion[_nodes.Length]; for (int j = 0; j < _nodes.Length; j++) { array2[j] = (((Object)(object)_nodes[j].T != (Object)null) ? _nodes[j].T.localRotation : Quaternion.identity); } _net.Broadcast(new ControlStateMsg { Tick = _net.Clock.ServerTick, Lengths = array, Rotations = array2 }, (DeliveryMethod)4); } } public void OnControlState(ControlStateMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Client) { return; } RefreshNodes(); if (_ropes.Length != 0) { if (msg.Lengths.Length != _ropes.Length) { WarnMismatch("ropes", msg.Lengths.Length, _ropes.Length); } else { long serverTick = _net.Clock.ServerTick; for (int i = 0; i < _ropes.Length; i++) { RopeController val = _ropes[i]; if (!((Object)(object)val == (Object)null)) { _hostLen[i] = msg.Lengths[i]; if (serverTick >= _localUntil[i] && val.currentLength != msg.Lengths[i]) { val.currentLength = msg.Lengths[i]; val.changed = true; } } } } } if (_nodes.Length != 0) { if (msg.Rotations.Length != _nodes.Length) { WarnMismatch("nodes", msg.Rotations.Length, _nodes.Length); return; } EnsureKinematic(); _targetRots = msg.Rotations; _haveTargets = true; } } public void ApplyClient(float dt) { //IL_0080: 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) if (_net.Role != Role.Client) { return; } ForwardLocalRopeChanges(dt); GoPointerButton heldBtn = HeldButton(); ForwardSteering(dt, heldBtn); if (!_haveTargets || _nodes.Length == 0 || _targetRots.Length != _nodes.Length) { return; } float num = 1f - Mathf.Exp((0f - RotSmoothing) * dt); for (int i = 0; i < _nodes.Length; i++) { Transform t = _nodes[i].T; if (!((Object)(object)t == (Object)null)) { t.localRotation = Quaternion.Slerp(t.localRotation, _targetRots[i], num); } } } private void ForwardSteering(float dt, GoPointerButton heldBtn) { _steerTimer += dt; float num = 1f / Mathf.Max(1f, ControlHz); if (_steerTimer < num) { return; } _steerTimer = 0f; GPButtonSteeringWheel val = (GPButtonSteeringWheel)(object)((heldBtn is GPButtonSteeringWheel) ? heldBtn : null); if ((Object)(object)val == (Object)null) { return; } int num2 = Array.IndexOf(_wheels, val); if (num2 < 0) { return; } float currentInput = val.currentInput; if (num2 >= _steerLastSent.Length || !(Mathf.Abs(currentInput - _steerLastSent[num2]) < 0.01f)) { _net.Broadcast(new SteerRequestMsg { Index = (ushort)num2, Input = currentInput }, (DeliveryMethod)2); if (num2 < _steerLastSent.Length) { _steerLastSent[num2] = currentInput; } } } public void OnSteerRequest(SteerRequestMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Host) { return; } RefreshNodes(); int index = msg.Index; if (index < 0 || index >= _wheels.Length) { return; } GPButtonSteeringWheel val = _wheels[index]; if ((Object)(object)val == (Object)null) { return; } val.currentInput = msg.Input; try { if (_miApplyRudder == null) { _miApplyRudder = typeof(GPButtonSteeringWheel).GetMethod("ApplyRudderRotation", BindingFlags.Instance | BindingFlags.NonPublic); } _miApplyRudder?.Invoke(val, null); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ControlsSync] ApplyRudderRotation failed: " + ex.Message)); } } private GoPointerButton HeldButton() { try { if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } if ((Object)(object)_gp == (Object)null) { return null; } if (_fSticky == null) { _fSticky = typeof(GoPointer).GetField("stickyClickedButton", BindingFlags.Instance | BindingFlags.NonPublic); } return (GoPointerButton)((_fSticky != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private void ForwardLocalRopeChanges(float dt) { //IL_0148: 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) if (_ropes.Length == 0 || _hostLen.Length != _ropes.Length) { return; } long serverTick = _net.Clock.ServerTick; for (int i = 0; i < _ropes.Length; i++) { RopeController val = _ropes[i]; if (!((Object)(object)val == (Object)null) && Mathf.Abs(val.currentLength - _hostLen[i]) > 1E-05f) { _localUntil[i] = serverTick + 600; } } _reqTimer += dt; float num = 1f / Mathf.Max(1f, ControlHz); if (_reqTimer < num) { return; } _reqTimer = 0f; for (int j = 0; j < _ropes.Length; j++) { RopeController val2 = _ropes[j]; if (!((Object)(object)val2 == (Object)null) && serverTick < _localUntil[j] && Mathf.Abs(val2.currentLength - _lastSentLen[j]) > 1E-05f) { ControlRequestMsg controlRequestMsg = new ControlRequestMsg { Index = (ushort)j, Length = val2.currentLength }; GPButtonRopeWinch val3 = FindWinchForRope(val2); if ((Object)(object)val3 != (Object)null) { controlRequestMsg.HasWinchRotation = true; controlRequestMsg.WinchRotation = ((Component)val3).transform.localRotation; } _net.Broadcast(controlRequestMsg, (DeliveryMethod)2); RememberControlRequest(j, val2.currentLength, controlRequestMsg.HasWinchRotation, incoming: false); _lastSentLen[j] = val2.currentLength; } } } public void OnControlRequest(ControlRequestMsg msg, NetPeer fromPeer) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Host) { return; } RefreshNodes(); int index = msg.Index; if (index < 0 || index >= _ropes.Length) { return; } RopeController val = _ropes[index]; if ((Object)(object)val == (Object)null) { return; } RememberControlRequest(index, msg.Length, msg.HasWinchRotation, incoming: true); if (val.currentLength != msg.Length) { val.currentLength = msg.Length; val.changed = true; } if (msg.HasWinchRotation) { GPButtonRopeWinch val2 = FindWinchForRope(val); if ((Object)(object)val2 != (Object)null) { ((Component)val2).transform.localRotation = msg.WinchRotation; } } } private void RefreshNodes() { if ((Object)(object)_emb == (Object)null) { _emb = Object.FindObjectOfType(); } Transform val = (((Object)(object)_emb != (Object)null) ? _emb.debugOutCurrentBoat : null); if ((Object)(object)val == (Object)null) { val = BoatLocator.FindByIndex(0); } if ((Object)(object)val == (Object)(object)_cachedBoat) { return; } RestoreKinematic(); _cachedBoat = val; _haveTargets = false; _warnedMismatch = false; _steerRope = null; _rudderNew = null; _rudderOld = null; if ((Object)(object)val == (Object)null) { _ropes = Array.Empty(); _winches = Array.Empty(); _wheels = Array.Empty(); _nodes = Array.Empty(); _hostLen = Array.Empty(); _localUntil = Array.Empty(); _lastSentLen = Array.Empty(); return; } _ropes = ((Component)val).GetComponentsInChildren(true); _winches = ((Component)val).GetComponentsInChildren(true); _wheels = ((Component)val).GetComponentsInChildren(true); _steerLastSent = new float[_wheels.Length]; for (int i = 0; i < _wheels.Length; i++) { _steerLastSent[i] = (((Object)(object)_wheels[i] != (Object)null) ? _wheels[i].currentInput : 0f); } _steerRope = ((Component)val).GetComponentInChildren(true); _rudderNew = ((Component)val).GetComponentInChildren(true); _rudderOld = ((Component)val).GetComponentInChildren(true); _hostLen = new float[_ropes.Length]; _localUntil = new long[_ropes.Length]; _lastSentLen = new float[_ropes.Length]; for (int j = 0; j < _ropes.Length; j++) { float num = (((Object)(object)_ropes[j] != (Object)null) ? _ropes[j].currentLength : 0f); _hostLen[j] = num; _lastSentLen[j] = num; } List list = new List(); HingeJoint[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (HingeJoint val2 in componentsInChildren) { list.Add(new Node { T = ((Component)val2).transform, Rb = ((Component)val2).GetComponent() }); } _nodes = list.ToArray(); Plugin.Logger.LogInfo((object)("[ControlsSync] Boat changed: ropes=" + _ropes.Length + ", nodes=" + _nodes.Length + ", wheels=" + _wheels.Length + (((Object)(object)val != (Object)null) ? (" ('" + ((Object)val).name + "')") : ""))); } private void EnsureKinematic() { if (_kinematicSaved.Count > 0) { return; } Node[] nodes = _nodes; for (int i = 0; i < nodes.Length; i++) { Node node = nodes[i]; if (!((Object)(object)node.Rb == (Object)null)) { _kinematicSaved.Add(new KeyValuePair(node.Rb, node.Rb.isKinematic)); node.Rb.isKinematic = true; } } } private void RestoreKinematic() { foreach (KeyValuePair item in _kinematicSaved) { if ((Object)(object)item.Key != (Object)null) { item.Key.isKinematic = item.Value; } } _kinematicSaved.Clear(); } private void WarnMismatch(string what, int host, int client) { if (!_warnedMismatch) { _warnedMismatch = true; Plugin.Logger.LogWarning((object)("[ControlsSync] Count mismatch for " + what + ": host=" + host + ", client=" + client + " - this part is not applied")); } } private GPButtonRopeWinch FindWinchForRope(RopeController rope) { if ((Object)(object)rope == (Object)null) { return null; } for (int i = 0; i < _winches.Length; i++) { GPButtonRopeWinch val = _winches[i]; if ((Object)(object)val != (Object)null && (Object)(object)val.rope == (Object)(object)rope) { return val; } } return null; } private void RememberControlRequest(int index, float length, bool hasWinchRotation, bool incoming) { _lastReqIndex = index; _lastReqLength = length; _lastReqHasWinchRotation = hasWinchRotation; _lastReqIncoming = incoming; _lastReqTick = _net.Clock.ServerTick; } public void Clear() { RestoreKinematic(); _cachedBoat = null; _ropes = Array.Empty(); _winches = Array.Empty(); _wheels = Array.Empty(); _steerLastSent = Array.Empty(); _steerTimer = 0f; _steerRope = null; _rudderNew = null; _rudderOld = null; _nodes = Array.Empty(); _targetRots = Array.Empty(); _hostLen = Array.Empty(); _localUntil = Array.Empty(); _lastSentLen = Array.Empty(); _haveTargets = false; _sendTimer = 0f; _reqTimer = 0f; _lastReqIndex = -1; _lastReqLength = 0f; _lastReqHasWinchRotation = false; _lastReqIncoming = false; _lastReqTick = 0L; _warnedMismatch = false; } } public static class CoopProfile { public static string ProfilePath => Path.Combine(Application.persistentDataPath, "coop_profile.dat"); public static bool Exists() { try { return File.Exists(ProfilePath); } catch { return false; } } public static bool SaveFromGame() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown try { SaveContainer val = new SaveContainer(); FillCharacterFromGame(val); using (FileStream serializationStream = File.Create(ProfilePath)) { new BinaryFormatter().Serialize(serializationStream, val); } Plugin.Logger.LogInfo((object)("[CoopProfile] Character profile saved: " + ProfilePath)); return true; } catch (Exception ex) { Plugin.Logger.LogError((object)("[CoopProfile] Failed to save profile: " + ex)); return false; } } private static void FillCharacterFromGame(SaveContainer c) { c.playerGold = 0; Try("currency", delegate { c.playerCurrency = PlayerGold.currency; c.currentCurrency = GameState.currentCurrency; }); Try("reputation", delegate { c.playerReputation = PlayerReputation.GetSaveData(); }); Try("knownPrices", delegate { c.playerKnownPrices = GameState.playerKnownPrices; }); Try("tradeReceipts", delegate { c.tradeReceipts = TradeReceiptsUI.instance.GetData(); }); Try("quests", delegate { c.quests = Quests.instance.currentQuests; }); Try("needs", delegate { c.food = PlayerNeeds.food; c.foodDebt = PlayerNeeds.foodDebt; c.water = PlayerNeeds.water; c.sleep = PlayerNeeds.sleep; c.sleepDebt = PlayerNeeds.sleepDebt; c.vitamins = PlayerNeeds.vitamins; c.protein = PlayerNeeds.protein; c.alcohol = PlayerNeeds.alcohol; }); Try("tobacco", delegate { PlayerTobacco instance = PlayerTobacco.instance; c.tobaccoWhite = instance.white; c.tobaccoGreen = instance.green; c.tobaccoBlack = instance.black; c.tobaccoBrown = instance.brown; }); Try("missions", delegate { Mission[] missions = PlayerMissions.missions; c.savedMissions = (SaveMissionData[])(object)new SaveMissionData[missions.Length]; for (int i = 0; i < missions.Length; i++) { if (missions[i] != null) { c.savedMissions[i] = missions[i].PrepareSaveData(); } } }); Try("loggedMissions", delegate { c.loggedMissions = MissionLog.instance.GetLogSaveData(); }); Try("dayLogs", delegate { int num = ((PlayerGold.currency != null) ? PlayerGold.currency.Length : 0); c.currencyDayLogs = new DaySheet[num][]; for (int i = 0; i < num; i++) { c.currencyDayLogs[i] = DayLogs.instance.dayLogs[i].GetSaveData(); } }); Try("belt", delegate { List list = new List(); GPButtonInventorySlot[] inventorySlots = GPButtonInventorySlot.inventorySlots; if (inventorySlots != null) { GPButtonInventorySlot[] array = inventorySlots; foreach (GPButtonInventorySlot val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.currentItem == (Object)null)) { SaveablePrefab component = ((Component)val.currentItem).GetComponent(); if (!((Object)(object)component == (Object)null)) { SavePrefabData val2 = component.PrepareSaveData(); if (val2 != null) { list.Add(val2); } } } } } c.savedPrefabs = list; }); } private static bool IsPersonalBelt(SavePrefabData p) { if (p != null && p.itemParentObject <= 0 && p.inventorySlot >= 0) { return p.inventorySlot < 100; } return false; } public static void MergeInto(SaveContainer host) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown if (host == null) { return; } int num = StripPersonalBelt(host); if (!Exists()) { Plugin.Logger.LogInfo((object)("[CoopProfile] Profile missing - first session (host belt stripped: " + num + ", own belt empty)")); return; } try { SaveContainer val; using (FileStream serializationStream = File.Open(ProfilePath, FileMode.Open)) { val = (SaveContainer)new BinaryFormatter().Deserialize(serializationStream); } CopyCharacterFields(val, host); int num2 = InjectPersonalBelt(val, host); Plugin.Logger.LogInfo((object)("[CoopProfile] Profile applied (host belt stripped: " + num + ", own belt injected: " + num2 + ")")); } catch (Exception ex) { Plugin.Logger.LogError((object)("[CoopProfile] Failed to apply profile (using host character): " + ex)); } } private static int StripPersonalBelt(SaveContainer host) { if (host == null || host.savedPrefabs == null) { return 0; } return host.savedPrefabs.RemoveAll(IsPersonalBelt); } private static int InjectPersonalBelt(SaveContainer profile, SaveContainer host) { if (profile == null || profile.savedPrefabs == null || host == null) { return 0; } if (host.savedPrefabs == null) { host.savedPrefabs = new List(); } int num = MaxInstanceId(host) + 1000; int num2 = 0; foreach (SavePrefabData savedPrefab in profile.savedPrefabs) { if (IsPersonalBelt(savedPrefab)) { savedPrefab.instanceId = num++; host.savedPrefabs.Add(savedPrefab); num2++; } } return num2; } private static int MaxInstanceId(SaveContainer host) { int num = 0; if (host != null && host.savedPrefabs != null) { foreach (SavePrefabData savedPrefab in host.savedPrefabs) { if (savedPrefab != null && savedPrefab.instanceId > num) { num = savedPrefab.instanceId; } } } return num; } private static void CopyCharacterFields(SaveContainer src, SaveContainer dst) { dst.playerGold = src.playerGold; if (src.playerCurrency != null) { dst.playerCurrency = src.playerCurrency; } dst.currentCurrency = src.currentCurrency; if (src.playerReputation != null) { dst.playerReputation = src.playerReputation; } if (src.playerKnownPrices != null) { dst.playerKnownPrices = src.playerKnownPrices; } if (src.tradeReceipts != null) { dst.tradeReceipts = src.tradeReceipts; } if (src.quests != null) { dst.quests = src.quests; } dst.food = src.food; dst.foodDebt = src.foodDebt; dst.water = src.water; dst.sleep = src.sleep; dst.sleepDebt = src.sleepDebt; dst.vitamins = src.vitamins; dst.protein = src.protein; dst.alcohol = src.alcohol; dst.tobaccoWhite = src.tobaccoWhite; dst.tobaccoGreen = src.tobaccoGreen; dst.tobaccoBlack = src.tobaccoBlack; dst.tobaccoBrown = src.tobaccoBrown; if (src.savedMissions != null) { dst.savedMissions = src.savedMissions; } if (src.loggedMissions != null) { dst.loggedMissions = src.loggedMissions; } if (src.currencyDayLogs != null) { dst.currencyDayLogs = src.currencyDayLogs; } } private static void Try(string what, Action a) { try { a(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[CoopProfile] field '" + what + "' skipped: " + ex.Message)); } } } public static class CoordSpace { public static bool Ready => (Object)(object)FloatingOriginManager.instance != (Object)null; public static bool ShiftingThisFrame => FloatingOriginManager.ShiftingThisFrame; public static Vector3 LocalToReal(Vector3 localPos) { //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_000f: Unknown result type (might be due to invalid IL or missing references) FloatingOriginManager instance = FloatingOriginManager.instance; if (!((Object)(object)instance != (Object)null)) { return localPos; } return instance.ShiftingPosToRealPos(localPos); } public static Vector3 RealToLocal(Vector3 realPos) { //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_000f: Unknown result type (might be due to invalid IL or missing references) FloatingOriginManager instance = FloatingOriginManager.instance; if (!((Object)(object)instance != (Object)null)) { return realPos; } return instance.RealPosToShiftingPos(realPos); } } public sealed class EnvironmentSync { private readonly CoopNet _net; private float _sendTimer; private bool _windDisabled; private bool _wavesDisabled; private WavesInertia _waves; private float _waveTimeBase; private float _waveBaseLocal; private float _hostTimeScale; private bool _waveBaseValid; private float _waveClock; public static float WaveClock; public static volatile bool WaveClockActive; public float EnvHz = 4f; public float WaveClockError { get; private set; } public float HostTimeScale => _hostTimeScale; public bool WaveClockValid => _waveBaseValid; public EnvironmentSync(CoopNet net) { _net = net; } public void Tick(float dt) { //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_0094: 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_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_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) if (_net.Role == Role.Client) { TickWaveClock(); } else { if (_net.Role != Role.Host || _net.State != LinkState.Connected) { return; } float num = 1f / Mathf.Max(0.5f, EnvHz); _sendTimer += dt; if (!(_sendTimer < num)) { _sendTimer = 0f; EnvStateMsg envStateMsg = new EnvStateMsg { Tick = _net.Clock.ServerTick, Wind = Wind.currentWind, BaseWind = Wind.currentBaseWind, WindRot = Wind.windRotation, Day = GameState.day }; Sun sun = Sun.sun; if ((Object)(object)sun != (Object)null) { envStateMsg.GlobalTime = sun.globalTime; envStateMsg.LocalTime = sun.localTime; envStateMsg.Timescale = sun.timescale; } Moon instance = Moon.instance; if ((Object)(object)instance != (Object)null) { envStateMsg.MoonPhase = instance.currentPhase; } WavesInertia val = FindWaves(); if ((Object)(object)val != (Object)null) { envStateMsg.HasWaves = true; envStateMsg.WavesRot = ((Component)val).transform.rotation; envStateMsg.WavesInertia = val.currentInertia; envStateMsg.WavesMagnitude = val.currentMagnitude; } envStateMsg.WaveTime = Time.time; envStateMsg.HostTimeScale = Time.timeScale; _net.Broadcast(envStateMsg, (DeliveryMethod)4); } } } private WavesInertia FindWaves() { if ((Object)(object)_waves == (Object)null) { _waves = Object.FindObjectOfType(); } return _waves; } public void OnEnvState(EnvStateMsg msg, NetPeer fromPeer) { //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_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_004b: 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_00de: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client) { return; } Wind instance = Wind.instance; if ((Object)(object)instance != (Object)null && ((Behaviour)instance).enabled) { ((Behaviour)instance).enabled = false; _windDisabled = true; } Wind.currentWind = msg.Wind; Wind.currentBaseWind = msg.BaseWind; Wind.windRotation = msg.WindRot; Sun sun = Sun.sun; if ((Object)(object)sun != (Object)null) { sun.globalTime = msg.GlobalTime; sun.localTime = msg.LocalTime; sun.timescale = msg.Timescale; } GameState.day = msg.Day; Moon instance2 = Moon.instance; if ((Object)(object)instance2 != (Object)null) { instance2.currentPhase = msg.MoonPhase; } if (msg.HasWaves) { WavesInertia val = FindWaves(); if ((Object)(object)val != (Object)null) { if (((Behaviour)val).enabled) { ((Behaviour)val).enabled = false; _wavesDisabled = true; } val.LoadInertia(msg.WavesRot, msg.WavesInertia, msg.WavesMagnitude); } } _waveTimeBase = msg.WaveTime; _waveBaseLocal = Time.unscaledTime; _hostTimeScale = msg.HostTimeScale; _waveBaseValid = true; } private void TickWaveClock() { if (!_waveBaseValid || _net.State != LinkState.Connected) { WaveClockActive = false; WaveClockError = 0f; return; } float num = _waveTimeBase + (Time.unscaledTime - _waveBaseLocal) * _hostTimeScale; float num2 = num - _waveClock; if (Mathf.Abs(num2) > 1f) { _waveClock = num; } else { _waveClock += Time.unscaledDeltaTime * _hostTimeScale + num2 * 0.1f; } WaveClockError = num2; WaveClock = _waveClock; WaveClockActive = true; } public void Clear() { if (_windDisabled) { Wind instance = Wind.instance; if ((Object)(object)instance != (Object)null) { ((Behaviour)instance).enabled = true; } _windDisabled = false; } if (_wavesDisabled) { if ((Object)(object)_waves != (Object)null) { ((Behaviour)_waves).enabled = true; } _wavesDisabled = false; } _waves = null; _sendTimer = 0f; _waveBaseValid = false; WaveClockActive = false; WaveClockError = 0f; } } public static class OceanPatches { private const int GaussSeed = 727272; public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, "calcComplex", "PreCalcComplex"); bool flag2 = TryPatch(harmony, "InitWaveGenerator", "PreInitWaveGenerator"); Plugin.Logger.LogInfo((object)("[OceanPatches] Ocean patches: phase=" + flag + " table=" + flag2)); PatchHealth.Report("Ocean", (flag ? 1 : 0) + (flag2 ? 1 : 0), 2); } private static bool TryPatch(Harmony harmony, string method, string prefixName) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown try { MethodInfo method2 = typeof(Ocean).GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(OceanPatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[OceanPatches] " + method + ": " + ex.Message)); return false; } } private static void PreCalcComplex(ref float time) { try { if (EnvironmentSync.WaveClockActive) { time = EnvironmentSync.WaveClock; } } catch { } } private static void PreInitWaveGenerator(Ocean __instance, bool skip, ref bool useMyRandom) { try { if (skip) { return; } int num = __instance.width * __instance.height; if (num > 0) { if (__instance.gaussRandom1 == null || __instance.gaussRandom1.Length != num) { __instance.gaussRandom1 = new float[num]; } if (__instance.gaussRandom2 == null || __instance.gaussRandom2.Length != num) { __instance.gaussRandom2 = new float[num]; } Random rng = new Random(727272); for (int i = 0; i < num; i++) { __instance.gaussRandom1[i] = Gaussian(rng); __instance.gaussRandom2[i] = Gaussian(rng); } useMyRandom = true; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[OceanPatches] PreInitWaveGenerator: " + ex.Message)); } } private static float Gaussian(Random rng) { double num = rng.NextDouble(); double num2 = rng.NextDouble(); if (num < 0.01) { num = 0.01; } return (float)(Math.Sqrt(-2.0 * Math.Log(num)) * Math.Cos(Math.PI * 2.0 * num2)); } } public enum InteractPolicy { Shared, Item, Local, HostOnly } public static class InteractionPolicy { private static readonly HashSet HostOnly = new HashSet { "GPButtonBed", "GPButtonTavernSleep", "GPButtonOnsenEntrance", "ShipItemBed", "GPButtonAutosaveToggle" }; private static readonly HashSet Local = new HashSet { "GPButtonAntiAliasing", "GPButtonLightQuality", "GPButtonScreenResolution", "GPButtonResolutionUI", "GPButtonWindowMode", "GPButtonTargetFramerate", "GPButtonKeybinding", "GPButtonResetKeybindings", "GPButtonSliderVolume", "GPButtonSettingsCheckbo", "GPButtonInterface", "GPButtonExtraMenus", "GPButtonControlToggle", "GPButtonLogMode", "GPButtonDayLogDay", "GPButtonMapZoom", "StartMenuButton", "GPButtonInventorySlot", "MouseoverTextTrigger", "GPButtonBuyItem", "EconomyUIButton", "CurrencyExchangeUIButton", "CurrencySwitchButton", "TradeReceiptsUIButton", "CrateInventoryButton", "CargoCarrierButton", "CargoStorageUIButton", "GPButtonPortMissions", "GPButtonListedMission", "GPButtonSetMission", "GPButtonMissionListBack", "GPButtonMissionListPage", "GPButtonMissionListWorld", "ShipyardButton", "ShipyardDocuments", "GPButtonPurchaseBoat", "BoatLadder", "GPButtonRatlines" }; private static readonly HashSet SharedPickupable = new HashSet(); public static InteractPolicy Classify(GoPointerButton btn) { if ((Object)(object)btn == (Object)null) { return InteractPolicy.Local; } string name = ((object)btn).GetType().Name; if (HostOnly.Contains(name)) { return InteractPolicy.HostOnly; } if (Local.Contains(name)) { return InteractPolicy.Local; } if (SharedPickupable.Contains(name)) { return InteractPolicy.Shared; } if (btn is PickupableItem) { return InteractPolicy.Item; } return InteractPolicy.Shared; } } public sealed class InteractionSync { private readonly CoopNet _net; private PlayerEmbarkerNew _emb; private Transform _cachedBoat; private GoPointerButton[] _buttons = Array.Empty(); private readonly Dictionary _index = new Dictionary(); private GoPointer _hostPointer; private bool _replaying; private GoPointer _gp; private FieldInfo _fClicked; private FieldInfo _fHeldItem; private float _pushTimer; private float _pushAccumDt; private string _lastPush = "—"; private long _lastPushTick; private const float PushHz = 20f; private string _lastEvent = "—"; private long _lastEventTick; public static InteractionSync Instance { get; private set; } public string LastEventText { get { if (_lastEventTick == 0L) { return "—"; } long num = _net.Clock.ServerTick - _lastEventTick; if (num < 0) { num = 0L; } string text = "—"; if (_lastPushTick != 0L) { long num2 = _net.Clock.ServerTick - _lastPushTick; if (num2 < 0) { num2 = 0L; } text = _lastPush + " " + num2 + "ms"; } return _lastEvent + " " + num + "ms · push " + text; } } public int ButtonCount => _buttons.Length; public InteractionSync(CoopNet net) { _net = net; Instance = this; } public static bool ShouldBlockLocally(GoPointerButton btn) { InteractionSync instance = Instance; if (instance == null || (Object)(object)btn == (Object)null) { return false; } if (instance._net.Role != Role.Client || instance._net.State != LinkState.Connected) { return false; } if (InteractionPolicy.Classify(btn) != InteractPolicy.HostOnly) { return false; } instance.Remember("block(host-only) '" + ButtonLabel(btn) + "'"); return true; } public static bool ShouldBlockPushWhileHolding(GoPointerButton btn) { InteractionSync instance = Instance; if (instance == null || (Object)(object)btn == (Object)null) { return false; } if (instance._net.Role != Role.Client || instance._net.State != LinkState.Connected) { return false; } if (!IsPushButton(btn)) { return false; } if (!instance.HasHeldItem()) { return false; } instance.RememberPush("block held '" + ButtonLabel(btn) + "'"); return true; } public void Tick(float dt) { if (_net.State == LinkState.Connected) { RefreshButtons(); ForwardPushRequests(dt); } } private void RefreshButtons() { if ((Object)(object)_emb == (Object)null) { _emb = Object.FindObjectOfType(); } Transform val = (((Object)(object)_emb != (Object)null) ? _emb.debugOutCurrentBoat : null); if ((Object)(object)val == (Object)(object)_cachedBoat) { return; } _cachedBoat = val; _index.Clear(); if ((Object)(object)val == (Object)null) { _buttons = Array.Empty(); return; } _buttons = ((Component)val).GetComponentsInChildren(true); for (int i = 0; i < _buttons.Length; i++) { if ((Object)(object)_buttons[i] != (Object)null) { _index[_buttons[i]] = i; } } Plugin.Logger.LogInfo((object)("[InteractionSync] Boat changed: buttons=" + _buttons.Length + (((Object)(object)val != (Object)null) ? (" ('" + ((Object)val).name + "')") : ""))); } public void NotifyLocalInteract(GoPointerButton btn, InteractKind kind) { if (!_replaying && _net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)btn == (Object)null) && InteractionPolicy.Classify(btn) == InteractPolicy.Shared && !IsExcluded(btn, kind)) { RefreshButtons(); if (_index.TryGetValue(btn, out var value)) { _net.Broadcast(new ControlEventMsg { Index = (ushort)value, Kind = kind }, (DeliveryMethod)2); Remember("out " + kind.ToString() + " #" + value + " '" + ButtonLabel(btn) + "'"); } } } public void NotifyLocalHold(GoPointerButton btn, InteractKind kind, bool down) { if (!_replaying && _net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)btn == (Object)null) && HasHeldChannel(btn, kind) && InteractionPolicy.Classify(btn) == InteractPolicy.Shared) { RefreshButtons(); if (_index.TryGetValue(btn, out var value)) { _net.Broadcast(new HoldRequestMsg { Index = (ushort)value, Kind = kind, Down = down }, (DeliveryMethod)2); Remember("out hold " + (down ? "down" : "up") + " #" + value + " '" + ButtonLabel(btn) + "'"); } } } public void OnControlEvent(ControlEventMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Host) { return; } RefreshButtons(); int index = msg.Index; if (index < 0 || index >= _buttons.Length) { return; } GoPointerButton val = _buttons[index]; if ((Object)(object)val == (Object)null || InteractionPolicy.Classify(val) != InteractPolicy.Shared || IsExcluded(val, msg.Kind)) { return; } string text = ((msg.Kind == InteractKind.AltActivate) ? "OnAltActivate" : "OnActivate"); Remember("in " + msg.Kind.ToString() + " #" + index + " '" + ButtonLabel(val) + "'"); try { Type[] types = ((msg.Kind == InteractKind.ActivateNoArg) ? Type.EmptyTypes : new Type[1] { typeof(GoPointer) }); object[] parameters = ((msg.Kind == InteractKind.ActivateNoArg) ? null : new object[1] { HostPointer() }); MethodInfo method = ((object)val).GetType().GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); if (method == null) { Plugin.Logger.LogWarning((object)("[InteractionSync] '" + ((object)val).GetType().Name + "' missing " + text + ((msg.Kind == InteractKind.ActivateNoArg) ? "()" : "(GoPointer)"))); } else { _replaying = true; method.Invoke(val, parameters); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[InteractionSync] Replay error " + text + " on '" + ((object)val).GetType().Name + "': " + ex.Message)); } finally { _replaying = false; } } public void OnHoldRequest(HoldRequestMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Host) { return; } RefreshButtons(); int index = msg.Index; if (index < 0 || index >= _buttons.Length) { return; } GoPointerButton val = _buttons[index]; if (!((Object)(object)val == (Object)null) && InteractionPolicy.Classify(val) == InteractPolicy.Shared && HasHeldChannel(val, msg.Kind)) { uint actorNetId = _net.PlayerNetIdForPeer(fromPeer); if (val is BilgePump) { BoatDamageSync.Instance?.SetRemotePump(msg.Index, msg.Down, actorNetId); Remember("in hold " + (msg.Down ? "down" : "up") + " #" + index + " '" + ButtonLabel(val) + "'"); } } } public void OnPushRequest(PushRequestMsg msg, NetPeer fromPeer) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_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) if (_net.Role != Role.Host || !CoordSpace.Ready) { return; } RefreshButtons(); int index = msg.Index; if (index < 0 || index >= _buttons.Length) { return; } GoPointerButton val = _buttons[index]; if (!((Object)(object)val == (Object)null) && IsPushButton(val) && InteractionPolicy.Classify(val) == InteractPolicy.Shared) { Rigidbody val2 = PushTargetBody(val); if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = CoordSpace.RealToLocal(msg.RealPos); float num = Mathf.Clamp(msg.DeltaTime, 0.001f, 0.2f); val2.AddForceAtPosition(msg.Force * num, val3, (ForceMode)1); RememberPush("in #" + index + " '" + ButtonLabel(val) + "'"); } } } private static bool IsExcluded(GoPointerButton btn, InteractKind kind) { if (kind != InteractKind.Activate) { return false; } if (!(btn is GPButtonRopeWinch) && !(btn is GPButtonSteeringWheel) && !(btn is BilgePump)) { return btn is PickupableItem; } return true; } private static bool HasHeldChannel(GoPointerButton btn, InteractKind kind) { if (kind == InteractKind.Activate) { return btn is BilgePump; } return false; } private void ForwardPushRequests(float dt) { //IL_0124: 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_012b: 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_0132: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client || _net.State != LinkState.Connected || !CoordSpace.Ready) { return; } if (HasHeldItem()) { _pushAccumDt = 0f; _pushTimer = 0f; return; } _pushAccumDt += dt; _pushTimer += dt; float num = 0.05f; if (_pushTimer < num) { return; } _pushTimer = 0f; GoPointerButton val = ClickedButton(); if ((Object)(object)val == (Object)null || !IsPushButton(val)) { _pushAccumDt = 0f; return; } if (InteractionPolicy.Classify(val) != InteractPolicy.Shared) { _pushAccumDt = 0f; return; } RefreshButtons(); if (!_index.TryGetValue(val, out var value)) { _pushAccumDt = 0f; return; } if (!TryBuildPush(val, out var force, out var atPos)) { _pushAccumDt = 0f; return; } float deltaTime = Mathf.Clamp(_pushAccumDt, 0.001f, 0.2f); _pushAccumDt = 0f; _net.Broadcast(new PushRequestMsg { Index = (ushort)value, RealPos = CoordSpace.LocalToReal(atPos), Force = force, DeltaTime = deltaTime }, (DeliveryMethod)2); RememberPush("out #" + value + " '" + ButtonLabel(val) + "'"); } private GoPointerButton ClickedButton() { try { if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } if ((Object)(object)_gp == (Object)null) { return null; } if (_fClicked == null) { _fClicked = typeof(GoPointer).GetField("clickedButton", BindingFlags.Instance | BindingFlags.NonPublic); } return (GoPointerButton)((_fClicked != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private bool HasHeldItem() { try { if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } if ((Object)(object)_gp == (Object)null) { return false; } if (_fHeldItem == null) { _fHeldItem = typeof(GoPointer).GetField("heldItem", BindingFlags.Instance | BindingFlags.NonPublic); } return _fHeldItem != null && _fHeldItem.GetValue(_gp) != null; } catch { return false; } } private GoPointer HostPointer() { if ((Object)(object)_hostPointer == (Object)null) { _hostPointer = Object.FindObjectOfType(); } return _hostPointer; } private bool TryBuildPush(GoPointerButton btn, out Vector3 force, out Vector3 atPos) { //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_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_0072: 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_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_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) //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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: 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_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0178: 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_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_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: 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_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: 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_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027a: 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_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028b: 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) force = Vector3.zero; atPos = Vector3.zero; if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } if ((Object)(object)_gp == (Object)null) { return false; } Transform val = (((Object)(object)_gp.movement != (Object)null) ? ((Component)_gp.movement).transform : ((Component)_gp).transform); atPos = val.position; GPButtonSailPusher val2 = (GPButtonSailPusher)(object)((btn is GPButtonSailPusher) ? btn : null); if (val2 != null) { force = val2.pushForceMult * val.forward * 2.5f; return ((Vector3)(ref force)).sqrMagnitude > 1E-06f; } Vector3 velocity; if (btn is GPButtonBoatPushCol) { Rigidbody val3 = PushTargetBody(btn); if ((Object)(object)val3 == (Object)null) { return false; } velocity = val3.velocity; float num = Mathf.Max(1f, ((Vector3)(ref velocity)).magnitude); float field = GetField(btn, "pushForceMult", 3f); float field2 = GetField(btn, "upForceMult", 1f); float field3 = GetField(btn, "verticalOffset", -2f); float num2 = (PlayerSwimming.swimming ? 0f : 1f); force = (field * val3.mass * val.forward + field2 * val3.mass * Vector3.up) * num2 / num; atPos = val.position + Vector3.up * field3; return ((Vector3)(ref force)).sqrMagnitude > 1E-06f; } if (btn is DockPushCol) { Rigidbody val4 = (((Object)(object)GameState.currentBoat != (Object)null && (Object)(object)GameState.currentBoat.parent != (Object)null) ? ((Component)GameState.currentBoat.parent).GetComponent() : null); if ((Object)(object)val4 == (Object)null) { return false; } velocity = val4.velocity; float num3 = Mathf.Max(1f, ((Vector3)(ref velocity)).magnitude); float field4 = GetField(btn, "pushForceMult", -0.55f); float field5 = GetField(btn, "upForceMult", 0f); float field6 = GetField(btn, "verticalOffset", 0f); force = (field4 * val4.mass * val.forward + field5 * val4.mass * Vector3.up) / num3; atPos = val.position + Vector3.up * field6; return ((Vector3)(ref force)).sqrMagnitude > 1E-06f; } return false; } private static bool IsPushButton(GoPointerButton btn) { if (!(btn is GPButtonBoatPushCol) && !(btn is DockPushCol)) { return btn is GPButtonSailPusher; } return true; } private static Rigidbody PushTargetBody(GoPointerButton btn) { if ((Object)(object)btn == (Object)null) { return null; } if (btn is GPButtonSailPusher) { Rigidbody field = GetField(btn, "body"); if (!((Object)(object)field != (Object)null)) { if (!((Object)(object)((Component)btn).transform.parent != (Object)null)) { return null; } return ((Component)((Component)btn).transform.parent).GetComponent(); } return field; } if (btn is GPButtonBoatPushCol) { Transform transform = ((Component)btn).transform; if ((Object)(object)transform.parent != (Object)null && (Object)(object)transform.parent.parent != (Object)null) { Rigidbody component = ((Component)transform.parent.parent).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } if ((Object)(object)transform.parent.parent.parent != (Object)null) { return ((Component)transform.parent.parent.parent).GetComponent(); } } return null; } if (btn is DockPushCol) { if (!((Object)(object)GameState.currentBoat != (Object)null) || !((Object)(object)GameState.currentBoat.parent != (Object)null)) { return null; } return ((Component)GameState.currentBoat.parent).GetComponent(); } return null; } private static float GetField(object obj, string name, float fallback) { try { FieldInfo field = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (field != null) ? ((float)field.GetValue(obj)) : fallback; } catch { return fallback; } } private static T GetField(object obj, string name) where T : class { try { FieldInfo field = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (field != null) ? (field.GetValue(obj) as T) : null; } catch { return null; } } private static string ButtonLabel(GoPointerButton btn) { if ((Object)(object)btn == (Object)null) { return "—"; } if (!string.IsNullOrEmpty(btn.lookText)) { return btn.lookText; } return ((object)btn).GetType().Name; } private void Remember(string text) { _lastEvent = text; _lastEventTick = _net.Clock.ServerTick; } private void RememberPush(string text) { _lastPush = text; _lastPushTick = _net.Clock.ServerTick; } public void Clear() { _cachedBoat = null; _buttons = Array.Empty(); _index.Clear(); _hostPointer = null; _replaying = false; _gp = null; _fClicked = null; _pushTimer = 0f; _pushAccumDt = 0f; _lastPush = "—"; _lastPushTick = 0L; _lastEvent = "—"; _lastEventTick = 0L; } } public static class InteractionPatches { public static void Apply(Harmony harmony) { int num = PatchAll(harmony, "OnActivate", "PostActivateNoArg", Type.EmptyTypes); int num2 = PatchAll(harmony, "OnActivate", "PostActivate"); int num3 = PatchAll(harmony, "OnAltActivate", "PostAltActivate"); int num4 = PatchAll(harmony, "OnUnactivate", "PostUnactivate", null, withBlockPrefix: false); int num5 = 0; if (PatchPushFixedUpdate(harmony, typeof(GPButtonBoatPushCol))) { num5++; } if (PatchPushFixedUpdate(harmony, typeof(DockPushCol))) { num5++; } if (PatchPushFixedUpdate(harmony, typeof(GPButtonSailPusher))) { num5++; } int ok = ((num > 0) ? 1 : 0) + ((num2 > 0) ? 1 : 0) + ((num3 > 0) ? 1 : 0) + ((num4 > 0) ? 1 : 0) + num5; PatchHealth.Report("Interactions", ok, 7, "activate=" + num2 + ", alt=" + num3 + ", unactivate=" + num4 + ", push=" + num5 + "/3"); } private static bool PatchPushFixedUpdate(Harmony harmony, Type type) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown try { MethodInfo method = type.GetMethod("ExtraFixedUpdate", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { Plugin.Logger.LogWarning((object)("[InteractionPatches] " + type.Name + " missing ExtraFixedUpdate")); return false; } HarmonyMethod val = new HarmonyMethod(typeof(InteractionPatches).GetMethod("PrePushFixedUpdate", BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Logger.LogInfo((object)("[InteractionPatches] " + type.Name + ".ExtraFixedUpdate: push-block patched")); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[InteractionPatches] Failed to patch " + type.Name + ".ExtraFixedUpdate: " + ex.Message)); return false; } } private static int PatchAll(Harmony harmony, string gameMethod, string postfixName, Type[] args = null, bool withBlockPrefix = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (args == null) { args = new Type[1] { typeof(GoPointer) }; } HarmonyMethod val = new HarmonyMethod(typeof(InteractionPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val2 = ((!withBlockPrefix) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(InteractionPatches).GetMethod("PreBlock", BindingFlags.Static | BindingFlags.NonPublic))); int num = 0; Type typeFromHandle = typeof(GoPointerButton); Type[] types = typeFromHandle.Assembly.GetTypes(); foreach (Type type in types) { if (!typeFromHandle.IsAssignableFrom(type)) { continue; } MethodInfo method; try { method = type.GetMethod(gameMethod, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); } catch { continue; } if (!(method == null) && !method.IsAbstract) { try { harmony.Patch((MethodBase)method, val2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[InteractionPatches] Failed to patch " + type.Name + "." + gameMethod + ": " + ex.Message)); } } } string text = ((args.Length == 0) ? "()" : "(GoPointer)"); Plugin.Logger.LogInfo((object)("[InteractionPatches] " + gameMethod + text + ": patched " + num + " methods")); return num; } private static void PostActivateNoArg(GoPointerButton __instance) { InteractionSync.Instance?.NotifyLocalInteract(__instance, InteractKind.ActivateNoArg); } private static void PostActivate(GoPointerButton __instance) { InteractionSync.Instance?.NotifyLocalHold(__instance, InteractKind.Activate, down: true); InteractionSync.Instance?.NotifyLocalInteract(__instance, InteractKind.Activate); } private static void PostAltActivate(GoPointerButton __instance) { InteractionSync.Instance?.NotifyLocalInteract(__instance, InteractKind.AltActivate); } private static void PostUnactivate(GoPointerButton __instance) { InteractionSync.Instance?.NotifyLocalHold(__instance, InteractKind.Activate, down: false); } private static bool PreBlock(GoPointerButton __instance) { return !InteractionSync.ShouldBlockLocally(__instance); } private static bool PrePushFixedUpdate(GoPointerButton __instance) { return !InteractionSync.ShouldBlockPushWhileHolding(__instance); } } public static class ItemPatches { private sealed class DropState { public PickupableItem Item; public bool SurfacePlaced; } private struct BottleClickState { public bool HasTarget; public float TargetAmount; public float TargetHealth; public bool HasHeld; public float HeldAmount; public float HeldHealth; } private sealed class MarketSpawnState { public int PrefabIndex; public Vector3 Pos; public HashSet ExistingIds; } private static FieldInfo _fHeldItem; private static FieldInfo _fOarIsRowing; private static FieldInfo _fPointedAtButton; private static FieldInfo _fCurrentThrowPower; private static float _rodPreClickHealth; private static FieldInfo _fWarehouseGoodsInArea; private static MethodInfo _mWarehouseIsGoodValid; public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, typeof(GoPointer), "PickUpItem", new Type[1] { typeof(PickupableItem) }, null, "PostPickup"); bool flag2 = TryPatch(harmony, typeof(GoPointer), "DropItem", Type.EmptyTypes, "PreDrop", "PostDrop"); bool flag3 = TryPatch(harmony, typeof(ShipItemBottle), "OnItemClick", new Type[1] { typeof(PickupableItem) }, "PreBottleItemClick", "PostBottleItemClick"); bool flag4 = TryPatch(harmony, typeof(ShipItemOar), "OnAltHeld", Type.EmptyTypes, null, "PostOarAltHeld"); bool flag5 = TryPatch(harmony, typeof(ShipItemFood), "EatFood", Type.EmptyTypes, "PreEatFood"); bool flag6 = TryPatch(harmony, typeof(ShipItemHammer), "NailItem", new Type[1] { typeof(ShipItem) }, null, "PostNailItem"); bool flag7 = TryPatch(harmony, typeof(ShipItemHammer), "OnAltActivate", Type.EmptyTypes, null, "PostHammerAltActivate"); bool flag8 = TryPatch(harmony, typeof(FishingRodFish), "CollectFish", Type.EmptyTypes, null, "PostCollectFish"); bool flag9 = TryPatch(harmony, typeof(ShipItemFishingRod), "DetachHook", Type.EmptyTypes, null, "PostDetachHook"); bool flag10 = TryPatch(harmony, typeof(ShipItemFishingRod), "OnItemClick", new Type[1] { typeof(PickupableItem) }, "PreRodItemClick", "PostRodItemClick"); bool flag11 = TryPatch(harmony, typeof(ShipItemLampHook), "OnItemClick", new Type[1] { typeof(PickupableItem) }, null, "PostLampHookItemClick"); bool flag12 = TryPatch(harmony, typeof(CrateInventory), "InsertItem", new Type[1] { typeof(ShipItem) }, null, "PostCrateInsert"); bool flag13 = TryPatch(harmony, typeof(CrateInventory), "WithdrawItem", new Type[1] { typeof(ShipItem) }, null, "PostCrateWithdraw"); bool flag14 = TryPatch(harmony, typeof(ShipItemCrate), "UnsealCrate", Type.EmptyTypes, "PreUnseal"); bool flag15 = TryPatch(harmony, typeof(CargoCarrier), "InsertItem", new Type[1] { typeof(ShipItem) }, null, "PostCargoInsert"); bool flag16 = TryPatch(harmony, typeof(CargoCarrier), "WithdrawItem", new Type[2] { typeof(GoPointer), typeof(int) }, "PreCargoWithdraw", "PostCargoWithdraw"); bool flag17 = TryPatch(harmony, typeof(GPButtonInventorySlot), "InsertItem", new Type[1] { typeof(ShipItem) }, null, "PostInventoryInsert"); bool flag18 = TryPatch(harmony, typeof(GPButtonInventorySlot), "WithdrawItem", Type.EmptyTypes, "PreInventoryWithdraw", "PostInventoryWithdraw"); bool flag19 = TryPatch(harmony, typeof(IslandMarket), "SpawnGood", new Type[1] { typeof(GameObject) }, "PreMarketSpawnGood", "PostMarketSpawnGood"); bool flag20 = TryPatch(harmony, typeof(IslandMarketWarehouseArea), "SellGood", new Type[1] { typeof(int) }, "PreWarehouseSellGood"); Plugin.Logger.LogInfo((object)("[ItemPatches] Item patches: pickup=" + flag + ", drop=" + flag2 + ", bottleClick=" + flag3 + ", oarHeld=" + flag4 + ", eat=" + flag5 + ", nail=" + flag6 + ", unnail=" + flag7 + ", fish=" + flag8 + ", rodDetach=" + flag9 + ", rodAttach=" + flag10 + ", lampHook=" + flag11 + ", crateIn=" + flag12 + ", crateOut=" + flag13 + ", unseal=" + flag14 + ", cargoIn=" + flag15 + ", cargoOut=" + flag16 + ", invIn=" + flag17 + ", invOut=" + flag18 + ", marketBuy=" + flag19 + ", marketSell=" + flag20)); int num = PatchShipItem(harmony, "OnAltHeld", "PostAltHeld"); int num2 = PatchShipItem(harmony, "OnAltActivate", "PostAltActivate"); bool flag21 = TryPatch(harmony, typeof(ShipItemBottle), "Drink", Type.EmptyTypes, null, "PostBottleDrink"); bool flag22 = TryPatch(harmony, typeof(ShipItemFoldable), "OnAltActivate", Type.EmptyTypes, null, "PostFoldableAltActivate"); bool flag23 = TryPatch(harmony, typeof(ShipItemBroom), "OnAltActivate", Type.EmptyTypes, null, "PostBroomAltActivate"); Plugin.Logger.LogInfo((object)("[ItemPatches] Held alt-actions: OnAltHeld=" + num + ", OnAltActivate=" + num2)); Plugin.Logger.LogInfo((object)("[ItemPatches] Extra item patches: surfacePlace=DropItem, bottleDrink=" + flag21 + ", foldable=" + flag22 + ", broom=" + flag23)); int num3 = 0; bool[] array = new bool[20] { flag, flag2, flag3, flag4, flag5, flag6, flag7, flag8, flag9, flag10, flag11, flag12, flag13, flag14, flag15, flag16, flag17, flag18, flag19, flag20 }; for (int i = 0; i < array.Length; i++) { if (array[i]) { num3++; } } if (num > 0) { num3++; } if (num2 > 0) { num3++; } if (flag21) { num3++; } if (flag22) { num3++; } if (flag23) { num3++; } PatchHealth.Report("Items", num3, 24, num3 + "/24, held=" + num + ", alt=" + num2); } private static int PatchShipItem(Harmony harmony, string gameMethod, string postfixName) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown HarmonyMethod val = new HarmonyMethod(typeof(ItemPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); Type[] types = new Type[1] { typeof(GoPointer) }; int num = 0; Type typeFromHandle = typeof(ShipItem); Type[] types2 = typeFromHandle.Assembly.GetTypes(); foreach (Type type in types2) { if (!typeFromHandle.IsAssignableFrom(type)) { continue; } MethodInfo method; try { method = type.GetMethod(gameMethod, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); } catch { continue; } if (!(method == null) && !method.IsAbstract) { try { harmony.Patch((MethodBase)method, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] Failed to patch " + type.Name + "." + gameMethod + ": " + ex.Message)); } } } return num; } private static bool TryPatch(Harmony harmony, Type type, string method, Type[] args, string prefixName = null, string postfixName = null) { //IL_0031: 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) try { MethodInfo method2 = type.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method2 == null) { return false; } HarmonyMethod val = ((prefixName == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ItemPatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic))); HarmonyMethod val2 = ((postfixName == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ItemPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic))); harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] " + method + ": " + ex.Message)); return false; } } private static void PostPickup(GoPointer __instance, PickupableItem item) { try { ItemSync.Instance?.NotifyPickup(__instance, item); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostPickup: " + ex.Message)); } } private static void PreDrop(GoPointer __instance, ref DropState __state) { try { if (_fHeldItem == null) { _fHeldItem = typeof(GoPointer).GetField("heldItem", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fPointedAtButton == null) { _fPointedAtButton = typeof(GoPointer).GetField("pointedAtButton", BindingFlags.Instance | BindingFlags.NonPublic); } PickupableItem val = (PickupableItem)((_fHeldItem != null) ? /*isinst with value type is only supported in some contexts*/: null); GoPointerButton target = (GoPointerButton)((_fPointedAtButton != null) ? /*isinst with value type is only supported in some contexts*/: null); __state = new DropState { Item = val, SurfacePlaced = IsSurfacePlacement(target, val) }; } catch { __state = new DropState(); } } private static void PostDrop(GoPointer __instance, DropState __state) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) try { ItemSync.Instance?.NotifyDrop(__instance, __state?.Item, ComputeThrowVelocity(__instance), __state?.SurfacePlaced ?? false); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostDrop: " + ex.Message)); } } private static bool IsSurfacePlacement(GoPointerButton target, PickupableItem held) { try { if ((Object)(object)target == (Object)null || (Object)(object)held == (Object)null || !target.allowPlacingItems) { return false; } if (target is GPButtonInventorySlot) { return false; } if (target is CrateInventoryButton) { return false; } if (target is ShipItemLampHook) { return false; } return held is ShipItem; } catch { return false; } } private static Vector3 ComputeThrowVelocity(GoPointer pointer) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_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_00b7: 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_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_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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)pointer == (Object)null) { return Vector3.zero; } if (_fCurrentThrowPower == null) { _fCurrentThrowPower = typeof(GoPointer).GetField("currentThrowPower", BindingFlags.Instance | BindingFlags.NonPublic); } float num = ((_fCurrentThrowPower != null) ? ((float)_fCurrentThrowPower.GetValue(pointer)) : 0f); if (num <= pointer.throwDelay) { return Vector3.zero; } float num2 = Mathf.Min(num - pointer.throwDelay, 1f); return ((Component)pointer).transform.forward * pointer.throwForce * num2 * Time.fixedDeltaTime; } catch { return Vector3.zero; } } private static bool PreBottleItemClick(ShipItemBottle __instance, PickupableItem __0, out BottleClickState __state) { __state = default(BottleClickState); try { if ((Object)(object)__instance != (Object)null) { __state.HasTarget = true; __state.TargetAmount = ((ShipItem)__instance).amount; __state.TargetHealth = ((ShipItem)__instance).health; } ShipItemBottle val = BottleOf(__0); if ((Object)(object)val != (Object)null) { if ((Object)(object)__instance != (Object)null && val.GetRemainingCapacity() <= 0.0001f && val.GetCapacity() <= __instance.GetCapacity()) { return false; } __state.HasHeld = true; __state.HeldAmount = ((ShipItem)val).amount; __state.HeldHealth = ((ShipItem)val).health; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PreBottleItemClick: " + ex.Message)); } return true; } private static void PostBottleItemClick(ShipItemBottle __instance, PickupableItem __0, BottleClickState __state) { try { ItemSync instance = ItemSync.Instance; if (instance != null) { ShipItemBottle val = BottleOf(__0); bool flag = __state.HasTarget && (Object)(object)__instance != (Object)null && (Mathf.Abs(((ShipItem)__instance).amount - __state.TargetAmount) > 0.0001f || Mathf.Abs(((ShipItem)__instance).health - __state.TargetHealth) > 0.0001f); bool flag2 = __state.HasHeld && (Object)(object)val != (Object)null && (Mathf.Abs(((ShipItem)val).amount - __state.HeldAmount) > 0.0001f || Mathf.Abs(((ShipItem)val).health - __state.HeldHealth) > 0.0001f); if (flag) { instance.NotifyItemStateChanged((ShipItem)(object)__instance, "bottle-click-target"); } if (flag2 && val != __instance) { instance.NotifyItemStateChanged((ShipItem)(object)val, "bottle-click-held"); } if (flag || flag2) { Plugin.Logger.LogInfo((object)("[ItemPatches] BottleClick sync target=" + (flag ? ((ShipItem)__instance).name : "-") + " held=" + ((flag2 && (Object)(object)val != (Object)null) ? ((ShipItem)val).name : "-"))); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostBottleItemClick: " + ex.Message)); } } private static void PostAltHeld(GoPointerButton __instance) { try { if (!(__instance is ShipItemOar)) { ItemSync.Instance?.NotifyAltHeld((ShipItem)(object)((__instance is ShipItem) ? __instance : null)); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostAltHeld: " + ex.Message)); } } private static void PostOarAltHeld(ShipItemOar __instance) { try { if (OarIsRowing(__instance)) { ItemSync.Instance?.NotifyAltHeld((ShipItem)(object)__instance); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostOarAltHeld: " + ex.Message)); } } private static bool OarIsRowing(ShipItemOar oar) { try { if ((Object)(object)oar == (Object)null) { return false; } if (_fOarIsRowing == null) { _fOarIsRowing = typeof(ShipItemOar).GetField("isRowing", BindingFlags.Instance | BindingFlags.NonPublic); } return _fOarIsRowing != null && (bool)_fOarIsRowing.GetValue(oar); } catch { return false; } } private static void PostAltActivate(GoPointerButton __instance) { try { ItemSync.Instance?.NotifyAltActivate((ShipItem)(object)((__instance is ShipItem) ? __instance : null)); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostAltActivate: " + ex.Message)); } } private static void PreEatFood(ShipItemFood __instance) { try { if (!((Object)(object)__instance == (Object)null) && (!((Object)(object)PlayerNeeds.instance != (Object)null) || !(PlayerNeeds.instance.eatCooldown > 0f))) { ItemSync.Instance?.NotifyConsume((ShipItem)(object)__instance); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PreEatFood: " + ex.Message)); } } private static void PostNailItem(ShipItem __0) { try { if ((Object)(object)__0 != (Object)null) { ItemSync.Instance?.OnLocalNail(__0); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostNailItem: " + ex.Message)); } } private static void PostHammerAltActivate(ShipItemHammer __instance) { try { ShipItem val = (((Object)(object)__instance != (Object)null && (Object)(object)((PickupableItem)__instance).held != (Object)null) ? ((PickupableItem)__instance).held.GetPointedAtItem() : null); if ((Object)(object)val != (Object)null) { ItemSync.Instance?.OnLocalNail(val); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostHammerAltActivate: " + ex.Message)); } } private static void PostDetachHook(ShipItemFishingRod __instance) { try { if ((Object)(object)__instance != (Object)null) { ItemSync.Instance?.OnLocalRodHook(__instance, attached: false, null); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostDetachHook: " + ex.Message)); } } private static void PreRodItemClick(ShipItemFishingRod __instance) { try { _rodPreClickHealth = (((Object)(object)__instance != (Object)null) ? ((ShipItem)__instance).health : 1f); } catch { _rodPreClickHealth = 1f; } } private static void PostRodItemClick(ShipItemFishingRod __instance, PickupableItem __0) { try { if (!((Object)(object)__instance == (Object)null) && !(_rodPreClickHealth > 0f) && !(((ShipItem)__instance).health <= 0f)) { ShipItem consumedHook = (((Object)(object)__0 != (Object)null) ? ((Component)__0).GetComponent() : null); ItemSync.Instance?.OnLocalRodHook(__instance, attached: true, consumedHook); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostRodItemClick: " + ex.Message)); } } private static void PostLampHookItemClick(ShipItemLampHook __instance, PickupableItem __0, bool __result) { try { if (__result && !((Object)(object)__0 == (Object)null) && !((Object)(object)((Component)__0).GetComponent() == (Object)null)) { ItemSync.Instance?.NotifyLampHook(__instance, __0); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostLampHookItemClick: " + ex.Message)); } } private static void PostBottleDrink(ShipItemBottle __instance) { try { ItemSync.Instance?.NotifyItemStateChanged((ShipItem)(object)__instance, "bottle-drink"); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostBottleDrink: " + ex.Message)); } } private static void PostFoldableAltActivate(ShipItemFoldable __instance) { try { ItemSync.Instance?.NotifyItemStateChanged((ShipItem)(object)__instance, "foldable-alt"); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostFoldableAltActivate: " + ex.Message)); } } private static void PostBroomAltActivate(ShipItemBroom __instance) { try { ItemSync.Instance?.NotifyBroomActivated(__instance); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostBroomAltActivate: " + ex.Message)); } } private static ShipItemBottle BottleOf(PickupableItem item) { try { return ((Object)(object)item != (Object)null) ? ((Component)item).GetComponent() : null; } catch { return null; } } private static void PostCollectFish(ShipItem __result) { try { if ((Object)(object)__result != (Object)null) { ItemSync.Instance?.NotifyClientAuthored(__result); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostCollectFish: " + ex.Message)); } } private static void PreMarketSpawnGood(IslandMarket __instance, GameObject goodPrefab, out MarketSpawnState __state) { //IL_0029: 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_002e: Unknown result type (might be due to invalid IL or missing references) __state = new MarketSpawnState { PrefabIndex = PatchPrefabIndex(goodPrefab), Pos = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).transform.position : Vector3.zero), ExistingIds = new HashSet() }; try { ShipItem[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { int num = PatchInstanceId(array[i]); if (num > 0) { __state.ExistingIds.Add(num); } } } catch { } } private static void PostMarketSpawnGood(MarketSpawnState __state) { //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_00a6: Unknown result type (might be due to invalid IL or missing references) try { if (__state == null || __state.PrefabIndex <= 0) { return; } ShipItem val = null; float num = 25f; ShipItem[] array = Object.FindObjectsOfType(); foreach (ShipItem val2 in array) { if ((Object)(object)val2 == (Object)null || !val2.sold) { continue; } int num2 = PatchInstanceId(val2); if (num2 <= 0 || __state.ExistingIds.Contains(num2) || PatchPrefabIndex(((Component)val2).gameObject) != __state.PrefabIndex) { continue; } Good component = ((Component)val2).GetComponent(); if (!((Object)(object)component == (Object)null) && component.GetMissionIndex() == -1) { Vector3 val3 = ((Component)val2).transform.position - __state.Pos; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; val = val2; } } } if ((Object)(object)val != (Object)null) { ItemSync.Instance?.NotifyClientAuthored(val); Plugin.Logger.LogInfo((object)("[ItemPatches] Market buy sync prefab=" + __state.PrefabIndex + " id=" + PatchInstanceId(val) + " '" + val.name + "'")); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostMarketSpawnGood: " + ex.Message)); } } private static void PreWarehouseSellGood(IslandMarketWarehouseArea __instance, int goodIndex) { try { Good val = FindWarehouseGood(__instance, goodIndex); if (!((Object)(object)val == (Object)null)) { ShipItem component = ((Component)val).GetComponent(); SaveablePrefab component2 = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null) { ItemSync.Instance?.NotifySold(component2.instanceId, component2.prefabIndex); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PreWarehouseSellGood: " + ex.Message)); } } private static Good FindWarehouseGood(IslandMarketWarehouseArea area, int goodIndex) { if ((Object)(object)area == (Object)null) { return null; } if (_fWarehouseGoodsInArea == null) { _fWarehouseGoodsInArea = typeof(IslandMarketWarehouseArea).GetField("goodsInArea", BindingFlags.Instance | BindingFlags.NonPublic); } if (_mWarehouseIsGoodValid == null) { _mWarehouseIsGoodValid = typeof(IslandMarketWarehouseArea).GetMethod("IsGoodValid", BindingFlags.Instance | BindingFlags.NonPublic); } IEnumerable enumerable = ((_fWarehouseGoodsInArea != null) ? (_fWarehouseGoodsInArea.GetValue(area) as IEnumerable) : null); if (enumerable == null) { return null; } foreach (object item in enumerable) { Good val = (Good)((item is Good) ? item : null); if (!((Object)(object)val == (Object)null) && PatchGoodIndex(val) == goodIndex) { bool flag = true; if (_mWarehouseIsGoodValid != null) { flag = (bool)_mWarehouseIsGoodValid.Invoke(area, new object[1] { val }); } if (flag) { return val; } } } return null; } private static int PatchPrefabIndex(GameObject go) { SaveablePrefab val = (((Object)(object)go != (Object)null) ? go.GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return 0; } return val.prefabIndex; } private static int PatchInstanceId(ShipItem item) { SaveablePrefab val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return 0; } return val.instanceId; } private static int PatchGoodIndex(Good good) { SaveablePrefab val = (((Object)(object)good != (Object)null) ? ((Component)good).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return -1; } return PrefabsDirectory.ItemToGoodIndex(val.prefabIndex); } private static void PostCrateInsert(ShipItem __0) { try { if (!ItemSync.ApplyingCrate) { ItemSync.Instance?.OnLocalCrate(__0); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostCrateInsert: " + ex.Message)); } } private static void PostCrateWithdraw(ShipItem __0) { try { if (!ItemSync.ApplyingCrate) { ItemSync.Instance?.OnLocalCrate(__0); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostCrateWithdraw: " + ex.Message)); } } private static bool PreUnseal(ShipItemCrate __instance) { try { ItemSync instance = ItemSync.Instance; if (instance == null) { return true; } return !instance.ForwardUnseal(__instance); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PreUnseal: " + ex.Message)); return true; } } private static void PostCargoInsert(ShipItem __0) { try { if (!ItemSync.ApplyingCargo) { ItemSync.Instance?.OnLocalCargo(__0); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostCargoInsert: " + ex.Message)); } } private static void PreCargoWithdraw(CargoCarrier __instance, int __1, out ShipItem __state) { __state = null; try { if ((Object)(object)__instance != (Object)null && __instance.cargo != null && __1 >= 0 && __1 < __instance.cargo.Count) { __state = __instance.cargo[__1]; } } catch { __state = null; } } private static void PostCargoWithdraw(ShipItem __state) { try { if ((Object)(object)__state != (Object)null && !ItemSync.ApplyingCargo) { ItemSync.Instance?.OnLocalCargo(__state); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostCargoWithdraw: " + ex.Message)); } } private static void PostInventoryInsert(ShipItem __0) { try { ItemSync.Instance?.OnLocalInventory(__0); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostInventoryInsert: " + ex.Message)); } } private static void PreInventoryWithdraw(GPButtonInventorySlot __instance, out ShipItem __state) { __state = null; try { __state = (((Object)(object)__instance != (Object)null) ? __instance.currentItem : null); } catch { __state = null; } } private static void PostInventoryWithdraw(ShipItem __state) { try { if ((Object)(object)__state != (Object)null) { ItemSync.Instance?.OnLocalInventory(__state); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemPatches] PostInventoryWithdraw: " + ex.Message)); } } } public sealed class ItemSync { private sealed class ItemEntry { public ushort Index; public int InstanceId; public int PrefabIndex; public uint NetId; public ShipItem Item; public readonly NetTransform Net = new NetTransform(); public uint HolderNetId; public Vector3 LastPos; public long LastTick; public bool HaveLast; public CoordFrame LastFrame; public ushort LastBoatIndex; public bool WasActive; public int InventorySlot = -1; public bool DropWithoutProxyVelocity; public bool ForceWorldPoseUntilDrop; } private sealed class PendingDynamicRelease { public ShipItem Item; public CoordFrame Frame; public Vector3 Vel; public float ReleaseAt; public string Reason; } private sealed class HiddenVisualState { public readonly List Disabled = new List(); public Vector3 RootScale = Vector3.one; public readonly List ChildScales = new List(); } private sealed class RodRemote { public Vector3 RealPos; public float Limit; public float Bend; public float LastTime; public bool Kinematic; } private readonly CoopNet _net; private readonly List _items = new List(); private readonly Dictionary _byItem = new Dictionary(); private readonly Dictionary _byInstanceId = new Dictionary(); private readonly Dictionary _localHeld = new Dictionary(); private readonly List _poseScratch = new List(); private readonly List _pendingDynamic = new List(); private readonly HashSet _suppressNextDrop = new HashSet(); private readonly HashSet _localClaimed = new HashSet(); private ShipItem _pendingHeldItem; private GoPointer _gp; private FieldInfo _fHeldItem; private static FieldInfo _fBoatCachedItems; private static FieldInfo _fShipItemCurrentBoatCollider; private static FieldInfo _fShipItemCurrentlyStayedEmbarkCol; private static FieldInfo _fItemRigidbodyOnBoat; private static FieldInfo _fColCheckerCollidedCols; private static MethodInfo _mShipItemExitBoat; private float _refreshTimer; private float _sendTimer; private float _heldPoseTimer; private float _extraTimer; private float _altHeldTimer; private bool _baselineReady; private int _baselineCount = -1; private float _baselineChangedAt; private string _last = "—"; private long _lastEventTick; public const float SettleSeconds = 4f; private readonly HashSet _hostIds = new HashSet(); public const float MatchRadius = 0.5f; private readonly List _pendingClientItems = new List(); public float SnapshotHz = 5f; public float HeldPoseHz = 15f; public float ExtraStateHz = 2f; public float AltHeldHz = 15f; private static readonly Dictionary _hiddenVisuals = new Dictionary(); private static FieldInfo _fPointerBigItemLocalPos; private static FieldInfo _fPointerDecolLocalPos; private static FieldInfo _fPointerBigItemLocalRot; private static readonly MethodInfo RodUpdateHookMethod = typeof(ShipItemFishingRod).GetMethod("UpdateHook", BindingFlags.Instance | BindingFlags.NonPublic); internal static bool ApplyingCrate; internal static bool ApplyingCargo; private static GoPointer _visualPointer; private const float MaxDropSpeed = 15f; private const float SettleDepenetration = 1f; private static readonly Dictionary ExtraFields = new Dictionary { { "ShipItemStove", new string[1] { "currentHeat" } }, { "ShopStove", new string[1] { "currentHeat" } }, { "CookableFood", new string[2] { "currentHeat", "foodState" } }, { "CookableFoodSoup", new string[1] { "currentHeat" } }, { "CookableFoodKettle", new string[1] { "currentHeat" } }, { "ShipItemFood", new string[1] { "foodState" } }, { "ShipItemKettle", new string[3] { "currentWater", "currentTeaAmount", "currentTeaType" } }, { "ShipItemSoup", new string[4] { "currentWater", "currentEnergy", "currentSpoiled", "currentSalted" } }, { "ShipItemBottle", new string[1] { "capacity" } }, { "StoveFuel", new string[2] { "lit", "inserted" } }, { "ShipItemStoveFuel", new string[1] { "lit" } } }; private static readonly Dictionary _fieldCache = new Dictionary(); private const float RodStateHz = 10f; private const float RodRemoteTimeout = 1.5f; private readonly Dictionary _rodRemote = new Dictionary(); private readonly List _rodDone = new List(); private float _rodSendTimer; private static readonly FieldInfo RodBobberJointField = typeof(ShipItemFishingRod).GetField("bobberJoint", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo RodTargetLengthField = typeof(ShipItemFishingRod).GetField("currentTargetLength", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo RodBendField = typeof(ShipItemFishingRod).GetField("currentRodBend", BindingFlags.Instance | BindingFlags.NonPublic); public static ItemSync Instance { get; private set; } public int ItemCount => _items.Count; public int HeldCount => _localHeld.Count; public string ItemText { get { string text = "—"; if (_lastEventTick != 0L) { long num = _net.Clock.ServerTick - _lastEventTick; if (num < 0) { num = 0L; } text = _last + " " + num + "ms"; } return _items.Count + " pcs, held " + _localHeld.Count + " · " + text; } } public ItemSync(CoopNet net) { _net = net; Instance = this; } public void Tick(float dt) { if (_net.State != LinkState.Connected) { return; } RefreshItems(dt); SendLocalHeldPose(dt); TickRods(dt); if (_net.Role != Role.Host) { return; } ProcessPendingDynamic(); SendExtraState(dt); float num = 1f / Mathf.Max(1f, HeldPoseHz); _sendTimer += dt; if (_sendTimer < num) { return; } _sendTimer = 0f; long serverTick = _net.Clock.ServerTick; foreach (ItemEntry item in _items) { if (!((Object)(object)item.Item == (Object)null) && ShouldReplicate(item.Item)) { bool num2 = (Object)(object)((PickupableItem)item.Item).held != (Object)null; bool flag = item.HolderNetId == 0 && IsMoving(item.Item); if (num2 || flag) { _net.Broadcast(BuildState(item, serverTick), (DeliveryMethod)4); item.WasActive = true; } else if (item.WasActive && item.HolderNetId == 0) { _net.Broadcast(BuildState(item, serverTick), (DeliveryMethod)2); item.WasActive = false; } } } } private void ProcessPendingDynamic() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (_pendingDynamic.Count == 0) { return; } for (int num = _pendingDynamic.Count - 1; num >= 0; num--) { PendingDynamicRelease pendingDynamicRelease = _pendingDynamic[num]; if (pendingDynamicRelease == null || (Object)(object)pendingDynamicRelease.Item == (Object)null) { _pendingDynamic.RemoveAt(num); } else if (!(Time.time < pendingDynamicRelease.ReleaseAt)) { EnterFreeDynamic(pendingDynamicRelease.Item, pendingDynamicRelease.Frame, pendingDynamicRelease.Vel, pendingDynamicRelease.Reason); _pendingDynamic.RemoveAt(num); } } } private static bool IsMoving(ShipItem item) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) try { Rigidbody val = (((Object)(object)item != (Object)null && (Object)(object)item.GetItemRigidbody() != (Object)null) ? item.GetItemRigidbody().GetBody() : null); int result; if ((Object)(object)val != (Object)null) { Vector3 velocity = val.velocity; result = ((((Vector3)(ref velocity)).sqrMagnitude > 0.0025f) ? 1 : 0); } else { result = 0; } return (byte)result != 0; } catch { return false; } } public void ApplyRemote() { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client || !CoordSpace.Ready || Time.timeScale <= 0.0001f) { return; } foreach (ItemEntry item in _items) { if (!((Object)(object)item.Item == (Object)null) && item.HolderNetId != _net.MyNetId && item.Net.HasData) { bool held = item.HolderNetId != 0; PrepareForRemotePose(item.Item, held); SetPuppet(item.Item, puppet: true); item.Net.Apply(((Component)item.Item).transform, _net.Clock.ServerTick); MoveProxyToItem(item.Item, kinematic: true, Vector3.zero); } } } private static void SetPuppet(ShipItem item, bool puppet) { //IL_00ea: 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) try { ItemRigidbody val = (((Object)(object)item != (Object)null) ? item.GetItemRigidbody() : null); bool flag = puppet && (Object)(object)item != (Object)null && item.wallAttachment && (Object)(object)((PickupableItem)item).held != (Object)null; if ((Object)(object)val != (Object)null && flag && !((Behaviour)val).enabled) { ((Behaviour)val).enabled = true; } else if ((Object)(object)val != (Object)null && !flag && ((Behaviour)val).enabled == puppet) { ((Behaviour)val).enabled = !puppet; } if ((Object)(object)val != (Object)null) { val.ToggleCollider(!puppet); Collider[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (Collider val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null) { val2.enabled = !puppet; } } } Rigidbody val3 = (((Object)(object)val != (Object)null) ? val.GetBody() : null); if ((Object)(object)val3 != (Object)null) { if (val3.isKinematic != puppet) { val3.isKinematic = puppet; if (puppet) { val3.velocity = Vector3.zero; val3.angularVelocity = Vector3.zero; } } if (val3.detectCollisions == puppet) { val3.detectCollisions = !puppet; } } if (puppet && (Object)(object)item != (Object)null && (Object)(object)((PickupableItem)item).colChecker != (Object)null) { ((PickupableItem)item).colChecker.collisions = 0; ((PickupableItem)item).colChecker.allowObstructedDropping = true; if (_fColCheckerCollidedCols == null) { _fColCheckerCollidedCols = typeof(PickupableItemCollisionChecker).GetField("collidedCols", BindingFlags.Instance | BindingFlags.NonPublic); } ((_fColCheckerCollidedCols != null) ? (_fColCheckerCollidedCols.GetValue(((PickupableItem)item).colChecker) as IList) : null)?.Clear(); } } catch { } } public void NotifyPickup(GoPointer pointer, PickupableItem pickup) { if (_net.Role != Role.Client || _net.State != LinkState.Connected) { return; } ShipItem val = (ShipItem)(object)((pickup is ShipItem) ? pickup : null); if ((Object)(object)val == (Object)null) { return; } RefreshItems(force: true); if (_byItem.TryGetValue(val, out var value)) { PrepareLocalPickupPose(pointer, val); SetPuppet(val, puppet: true); value.HolderNetId = _net.MyNetId; value.Net.Clear(); _localHeld[val] = _net.MyNetId; if (!_hostIds.Contains(value.InstanceId)) { Remember("local pickup (player-local, waiting for authoring) '" + val.name + "'"); return; } SendRequest(value, ItemAction.Pickup, reliable: true); Remember("out pickup #" + value.Index + " '" + val.name + "'"); } } public void NotifyDrop(GoPointer pointer, PickupableItem pickup, Vector3 throwVelocity, bool surfacePlaced = false) { //IL_0250: 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_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0248: 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_0106: 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_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0271: 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_0121: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_014f: 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_0155: Unknown result type (might be due to invalid IL or missing references) if (_net.State != LinkState.Connected) { return; } ShipItem val = (ShipItem)(object)((pickup is ShipItem) ? pickup : null); if ((Object)(object)val == (Object)null) { return; } if (_suppressNextDrop.Remove(val)) { Remember("local drop suppressed '" + val.name + "'"); return; } RefreshItems(force: true); if (!_byItem.TryGetValue(val, out var value)) { return; } int num = PersonalInventorySlotOf(val); if (num >= 0) { ClaimItemToBelt(val, num); return; } _localHeld.Remove(val); value.InventorySlot = -1; if (surfacePlaced) { value.DropWithoutProxyVelocity = true; } if (_net.Role == Role.Client) { RestoreLocalInventoryVisual(val, inInventory: false); bool flag = IsProxyAttached(val); if (flag) { MoveItemToProxy(val); } if ((Object)(object)LocalPlayerBoat() == (Object)null) { EnsureWorldParentState(val); } if (!flag) { MoveProxyToItem(val, kinematic: true, Vector3.zero); } ItemRequestMsg itemRequestMsg = BuildRequest(value, ItemAction.Drop, _net.Clock.ServerTick); itemRequestMsg.Attached = flag; if (flag) { itemRequestMsg.Vel = Vector3.zero; } else if (value.DropWithoutProxyVelocity) { itemRequestMsg.Vel = Vector3.zero; itemRequestMsg.CargoIndex = -2; } else if (((Vector3)(ref throwVelocity)).sqrMagnitude > 0.0001f) { itemRequestMsg.Vel = WorldToFrameAxes(itemRequestMsg.Frame, itemRequestMsg.BoatIndex, throwVelocity); } value.DropWithoutProxyVelocity = false; value.ForceWorldPoseUntilDrop = false; _net.Broadcast(itemRequestMsg, (DeliveryMethod)2); value.HolderNetId = _net.MyNetId; value.Net.Clear(); SetPuppet(val, puppet: true); Remember("out drop #" + value.Index + " '" + val.name + "' " + PoseLabel(itemRequestMsg.Frame, itemRequestMsg.BoatIndex, itemRequestMsg.Pos)); } else { if (_net.Role != Role.Host) { return; } value.HolderNetId = 0u; bool num2 = IsProxyAttached(val); if (num2) { MoveItemToProxy(val); } ItemStateMsg itemStateMsg = BuildState(value, _net.Clock.ServerTick); itemStateMsg.HolderNetId = 0u; if (num2) { itemStateMsg.Vel = Vector3.zero; } else { Vector3 val2 = RealItemVelocity(val); if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f) { itemStateMsg.Vel = ((itemStateMsg.Frame == CoordFrame.Boat) ? ProxyToBoatAxes(val, val2) : val2); } else if (((Vector3)(ref throwVelocity)).sqrMagnitude > 0.0001f) { itemStateMsg.Vel = WorldToFrameAxes(itemStateMsg.Frame, itemStateMsg.BoatIndex, throwVelocity); } } _net.Broadcast(itemStateMsg, (DeliveryMethod)2); Remember("out drop(host) #" + value.Index + " '" + val.name + "'"); } } public void OnItemRequest(ItemRequestMsg msg, NetPeer fromPeer) { //IL_0b16: 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_05f3: Unknown result type (might be due to invalid IL or missing references) //IL_05f9: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_07b6: Unknown result type (might be due to invalid IL or missing references) //IL_07bc: Unknown result type (might be due to invalid IL or missing references) //IL_07c1: Unknown result type (might be due to invalid IL or missing references) //IL_0a91: Unknown result type (might be due to invalid IL or missing references) //IL_0a97: Unknown result type (might be due to invalid IL or missing references) //IL_0a9c: Unknown result type (might be due to invalid IL or missing references) //IL_0d04: Unknown result type (might be due to invalid IL or missing references) //IL_0d0a: Unknown result type (might be due to invalid IL or missing references) //IL_0d10: Unknown result type (might be due to invalid IL or missing references) //IL_0e51: Unknown result type (might be due to invalid IL or missing references) //IL_0e56: Unknown result type (might be due to invalid IL or missing references) //IL_0e0d: Unknown result type (might be due to invalid IL or missing references) //IL_0ddf: Unknown result type (might be due to invalid IL or missing references) //IL_0ed6: Unknown result type (might be due to invalid IL or missing references) if (_net.Role == Role.Client) { if (msg.Action == ItemAction.AltActivate) { RefreshItems(force: false); ItemEntry itemEntry = ResolveClient(msg.InstanceId, msg.PrefabIndex, msg.Frame, msg.BoatIndex, msg.Pos, msg.Amount, msg.Health, msg.Sold, msg.Nailed, allowSpawn: false); if (itemEntry != null && itemEntry.Item is ShipItemBroom) { PulseBroom(itemEntry.Item); } } } else { if (_net.Role != Role.Host) { return; } RefreshItems(force: true); if (msg.InstanceId == 0) { SendManifest(); Remember("manifest on request"); return; } ItemEntry itemEntry2 = HostLookup(msg.InstanceId, msg.PrefabIndex); if (itemEntry2 == null || (Object)(object)itemEntry2.Item == (Object)null) { Remember("reject req id=" + msg.InstanceId + " prefab=" + msg.PrefabIndex); return; } uint num = _net.PlayerNetIdForPeer(fromPeer); if (num == 0) { return; } if (msg.Action == ItemAction.Consume) { ItemEntry itemEntry3 = HostLookup(msg.InstanceId, msg.PrefabIndex); if (itemEntry3 != null && (Object)(object)itemEntry3.Item != (Object)null) { try { itemEntry3.Item.DestroyItem(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Consume destroy: " + ex.Message)); } RefreshItems(force: true); Remember("in consume #" + itemEntry3.Index + " actor=" + num); } else { Remember("reject consume id=" + msg.InstanceId); } return; } if (msg.Action == ItemAction.RodHook) { ItemEntry itemEntry4 = HostLookup(msg.InstanceId, msg.PrefabIndex); ShipItemFishingRod val = (ShipItemFishingRod)((itemEntry4 != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val != (Object)null) { ((ShipItem)val).health = msg.Health; InvokeRodUpdateHook(val); _net.Broadcast(BuildState(itemEntry4, _net.Clock.ServerTick), (DeliveryMethod)2); Remember("in rod-hook #" + itemEntry4.Index + " health=" + msg.Health + " actor=" + num); } else { Remember("reject rod-hook id=" + msg.InstanceId); } return; } if (msg.Action == ItemAction.Nail) { ItemEntry itemEntry5 = HostLookup(msg.InstanceId, msg.PrefabIndex); if (itemEntry5 != null && (Object)(object)itemEntry5.Item != (Object)null) { itemEntry5.Item.nailed = msg.Nailed; _net.Broadcast(BuildState(itemEntry5, _net.Clock.ServerTick), (DeliveryMethod)2); Remember("in nail #" + itemEntry5.Index + "=" + msg.Nailed + " actor=" + num); } else { Remember("reject nail id=" + msg.InstanceId); } return; } if (msg.Action == ItemAction.Crate) { ItemEntry itemEntry6 = HostLookup(msg.InstanceId, msg.PrefabIndex); if (itemEntry6 != null && (Object)(object)itemEntry6.Item != (Object)null) { ApplyCrateMembership(itemEntry6.Item, msg.CrateId); _net.Broadcast(BuildState(itemEntry6, _net.Clock.ServerTick), (DeliveryMethod)2); Remember("in crate #" + itemEntry6.Index + "->" + msg.CrateId + " actor=" + num); } else { Remember("reject crate id=" + msg.InstanceId); } return; } if (msg.Action == ItemAction.Unseal) { ItemEntry itemEntry7 = HostLookup(msg.InstanceId, msg.PrefabIndex); ShipItemCrate val2 = (ShipItemCrate)((itemEntry7 != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val2 != (Object)null) { try { val2.UnsealCrate(); } catch (Exception ex2) { Plugin.Logger.LogWarning((object)("[ItemSync] UnsealCrate: " + ex2.Message)); } if (itemEntry7 != null) { _net.Broadcast(BuildState(itemEntry7, _net.Clock.ServerTick), (DeliveryMethod)2); } Remember("in unseal crate #" + ((itemEntry7 != null) ? itemEntry7.Index.ToString() : "?") + " actor=" + num); } else { Remember("reject unseal id=" + msg.InstanceId); } return; } if (msg.Action == ItemAction.Cargo) { ItemEntry itemEntry8 = HostLookup(msg.InstanceId, msg.PrefabIndex); if (itemEntry8 != null && (Object)(object)itemEntry8.Item != (Object)null) { ApplyCargoMembership(itemEntry8.Item, msg.CargoPort); if (msg.CargoPort >= 0) { itemEntry8.HolderNetId = 0u; } else { itemEntry8.HolderNetId = num; _localHeld[itemEntry8.Item] = num; EnterRemoteHeldVisual(itemEntry8.Item, "host cargo out #" + itemEntry8.Index); ApplyWirePose(itemEntry8.Item, msg.Frame, msg.BoatIndex, msg.Pos, msg.Rot, Vector3.zero, itemEntry8.Item.amount, itemEntry8.Item.health, itemEntry8.Item.sold, itemEntry8.Item.nailed, held: true); } _net.Broadcast(BuildState(itemEntry8, _net.Clock.ServerTick), (DeliveryMethod)2); Remember("in cargo #" + itemEntry8.Index + "->" + msg.CargoPort + " actor=" + num); } else { Remember("reject cargo id=" + msg.InstanceId); } return; } if (msg.Action == ItemAction.Inventory) { ItemEntry itemEntry9 = HostLookup(msg.InstanceId, msg.PrefabIndex); if (itemEntry9 != null && (Object)(object)itemEntry9.Item != (Object)null) { itemEntry9.InventorySlot = msg.InventorySlot; if (msg.InventorySlot >= 0) { itemEntry9.HolderNetId = num; _localHeld[itemEntry9.Item] = num; EnterRemoteInventoryHidden(itemEntry9.Item, "host inventory in #" + itemEntry9.Index); } else { itemEntry9.InventorySlot = -1; itemEntry9.HolderNetId = num; _localHeld[itemEntry9.Item] = num; EnterRemoteHeldVisual(itemEntry9.Item, "host inventory out #" + itemEntry9.Index); ApplyWirePose(itemEntry9.Item, msg.Frame, msg.BoatIndex, msg.Pos, msg.Rot, Vector3.zero, itemEntry9.Item.amount, itemEntry9.Item.health, itemEntry9.Item.sold, itemEntry9.Item.nailed, held: true); } ItemStateMsg itemStateMsg = BuildState(itemEntry9, _net.Clock.ServerTick); itemStateMsg.InventorySlot = itemEntry9.InventorySlot; _net.Broadcast(itemStateMsg, (DeliveryMethod)2); Remember("in inventory #" + itemEntry9.Index + " slot=" + msg.InventorySlot + " actor=" + num); } else { Remember("reject inventory id=" + msg.InstanceId); } return; } if (msg.Action == ItemAction.AltHeld || msg.Action == ItemAction.AltActivate) { if (msg.Action == ItemAction.AltHeld && TryApplyOarRow(itemEntry2, msg, num)) { ItemStateMsg msg2 = BuildState(itemEntry2, _net.Clock.ServerTick); _net.Broadcast(msg2, (DeliveryMethod)4); Remember("in oar-row #" + itemEntry2.Index + " actor=" + num); return; } ReplayHeldAction(itemEntry2, msg.Action, num); if (msg.Action == ItemAction.AltActivate && itemEntry2.Item is ShipItemBroom) { PulseBroom(itemEntry2.Item); _net.RelayExcept(msg, fromPeer, (DeliveryMethod)2); } ItemStateMsg msg3 = BuildState(itemEntry2, _net.Clock.ServerTick); _net.Broadcast(msg3, (DeliveryMethod)4); Remember("in " + msg.Action.ToString() + " #" + itemEntry2.Index + " actor=" + num); return; } if (msg.Action == ItemAction.LampHook) { ItemEntry itemEntry10 = HostLookup(msg.CrateId, msg.CargoIndex); ShipItemLampHook val3 = (ShipItemLampHook)((itemEntry10 != null) ? /*isinst with value type is only supported in some contexts*/: /*isinst with value type is only supported in some contexts*/); if ((Object)(object)val3 != (Object)null && (Object)(object)itemEntry2.Item != (Object)null && (Object)(object)((Component)itemEntry2.Item).GetComponent() != (Object)null) { itemEntry2.InventorySlot = -1; itemEntry2.HolderNetId = 0u; _localHeld.Remove(itemEntry2.Item); SetProxyAttached(itemEntry2.Item, value: false); ApplyWirePose(itemEntry2.Item, msg.Frame, msg.BoatIndex, msg.Pos, msg.Rot, Vector3.zero, itemEntry2.Item.amount, itemEntry2.Item.health, itemEntry2.Item.sold, itemEntry2.Item.nailed, held: false); try { ((GoPointerButton)val3).OnItemClick((PickupableItem)(object)itemEntry2.Item); } catch (Exception ex3) { Plugin.Logger.LogWarning((object)("[ItemSync] LampHook replay: " + ex3.Message)); } SnapHangableToHook(itemEntry2.Item, val3); MoveProxyToItem(itemEntry2.Item, kinematic: true, Vector3.zero); SetProxyAttached(itemEntry2.Item, value: true); ItemStateMsg itemStateMsg2 = BuildState(itemEntry2, _net.Clock.ServerTick); itemStateMsg2.HolderNetId = 0u; itemStateMsg2.Attached = true; _net.Broadcast(itemStateMsg2, (DeliveryMethod)2); Remember("in lamp-hook #" + itemEntry2.Index + " -> hook=" + msg.CrateId + " actor=" + num); } else { Remember("reject lamp-hook item=" + msg.InstanceId + " hook=" + msg.CrateId); } return; } if (msg.Action == ItemAction.Pickup) { itemEntry2.InventorySlot = -1; itemEntry2.HolderNetId = num; _localHeld[itemEntry2.Item] = num; SetProxyAttached(itemEntry2.Item, value: false); DisconnectHangable(itemEntry2.Item); ApplyCrateMembership(itemEntry2.Item, msg.CrateId); ApplyCargoMembership(itemEntry2.Item, msg.CargoPort); } else if (msg.Action == ItemAction.Drop) { itemEntry2.InventorySlot = -1; itemEntry2.HolderNetId = 0u; _localHeld.Remove(itemEntry2.Item); ((PickupableItem)itemEntry2.Item).held = null; } else { if (msg.Action != ItemAction.State && itemEntry2.HolderNetId != num) { return; } if (itemEntry2.HolderNetId == num) { SetPuppet(itemEntry2.Item, puppet: true); } } bool flag = msg.Action == ItemAction.State; bool flag2 = msg.Action == ItemAction.Drop && msg.Frame == CoordFrame.World; bool flag3 = msg.Action == ItemAction.Drop && msg.CargoIndex == -2; bool flag4 = msg.Action == ItemAction.Drop && msg.Attached; ApplyWirePose(itemEntry2.Item, msg.Frame, msg.BoatIndex, msg.Pos, msg.Rot, msg.Vel, flag ? msg.Amount : itemEntry2.Item.amount, flag ? msg.Health : itemEntry2.Item.health, itemEntry2.Item.sold, itemEntry2.Item.nailed, itemEntry2.HolderNetId != 0 || flag2); if (msg.Action == ItemAction.Pickup) { EnterRemoteHeldVisual(itemEntry2.Item, "host pickup #" + itemEntry2.Index); } else if (msg.Action == ItemAction.Drop) { if (flag4) { EnterAttachedStatic(itemEntry2.Item, msg.Frame, "host place #" + itemEntry2.Index); } else if (flag3) { ScheduleFreeDynamic(itemEntry2.Item, msg.Frame, msg.Vel, "host delayed drop #" + itemEntry2.Index); } else { EnterFreeDynamic(itemEntry2.Item, msg.Frame, msg.Vel, "host drop #" + itemEntry2.Index); } } ItemStateMsg itemStateMsg3 = BuildState(itemEntry2, _net.Clock.ServerTick); if (msg.Action == ItemAction.Drop) { itemStateMsg3.Vel = msg.Vel; } _net.Broadcast(itemStateMsg3, (DeliveryMethod)((msg.Action == ItemAction.Pose) ? 4 : 2)); Remember("in " + msg.Action.ToString() + " #" + itemEntry2.Index + " actor=" + num + " " + PoseLabel(msg.Frame, msg.BoatIndex, msg.Pos)); } } public void OnItemState(ItemStateMsg msg, NetPeer fromPeer) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (_net.Role != Role.Client) { return; } RefreshItems(force: true); ItemEntry itemEntry = ResolveClient(msg.InstanceId, msg.PrefabIndex, msg.Frame, msg.BoatIndex, msg.Pos, msg.Amount, msg.Health, msg.Sold, msg.Nailed, msg.HolderNetId == 0); if (itemEntry == null || (Object)(object)itemEntry.Item == (Object)null) { Remember("missing item id=" + msg.InstanceId + " prefab=" + msg.PrefabIndex); return; } uint holderNetId = itemEntry.HolderNetId; itemEntry.HolderNetId = msg.HolderNetId; itemEntry.InventorySlot = msg.InventorySlot; ApplyScalarState(itemEntry.Item, msg.Amount, msg.Health, msg.Sold, msg.Nailed); if (msg.HolderNetId != _net.MyNetId) { ApplyCrateMembership(itemEntry.Item, msg.CrateId); ApplyCargoMembership(itemEntry.Item, msg.CargoPort); } if (msg.HolderNetId != _net.MyNetId) { SetProxyAttached(itemEntry.Item, msg.Attached); } if (msg.HolderNetId == _net.MyNetId) { SetRemoteInventoryVisual(itemEntry.Item, hidden: false); Remember("echo #" + itemEntry.Index); return; } SetRemoteInventoryVisual(itemEntry.Item, msg.InventorySlot >= 0); ConfigureNetFrame(itemEntry, msg.Frame, msg.BoatIndex); if (holderNetId == _net.MyNetId && msg.HolderNetId == 0) { itemEntry.Net.Clear(); } itemEntry.Net.Push(msg.Tick, msg.Pos, msg.Rot, msg.Vel); Remember("in " + ((msg.HolderNetId != 0) ? ("held " + msg.HolderNetId) : "free") + " #" + itemEntry.Index); } public void ClearRemoteActor(uint actorNetId) { if (actorNetId == 0) { return; } int num = 0; foreach (ItemEntry item in _items) { if (item != null && !((Object)(object)item.Item == (Object)null) && item.HolderNetId == actorNetId) { item.HolderNetId = 0u; item.InventorySlot = -1; _localHeld.Remove(item.Item); RestoreDisconnectedItem(item.Item); if (_net.Role == Role.Host && _net.State == LinkState.Connected) { _net.Broadcast(BuildState(item, _net.Clock.ServerTick), (DeliveryMethod)2); } num++; } } if (num > 0) { Remember("released actor " + actorNetId + " items=" + num); Plugin.Logger.LogInfo((object)("[ItemSync] Released " + num + " item(s) held by player " + actorNetId)); } } private void SendLocalHeldPose(float dt) { if (_net.Role != Role.Client || _net.State != LinkState.Connected) { return; } float num = 1f / Mathf.Max(1f, HeldPoseHz); _heldPoseTimer += dt; if (_heldPoseTimer < num) { return; } _heldPoseTimer = 0f; PickupableItem obj = HeldItem(); ShipItem val = (ShipItem)(object)((obj is ShipItem) ? obj : null); if ((Object)(object)val != (Object)null) { StreamLocalPose(val); } if (_localHeld.Count <= 0) { return; } _poseScratch.Clear(); foreach (KeyValuePair item in _localHeld) { _poseScratch.Add(item.Key); } foreach (ShipItem item2 in _poseScratch) { if (!((Object)(object)item2 == (Object)null) && !((Object)(object)item2 == (Object)(object)val) && InPersonalInventory(item2)) { StreamLocalPose(item2); } } } private void StreamLocalPose(ShipItem item) { RefreshItems(force: false); if (_byItem.TryGetValue(item, out var value)) { _localHeld[item] = _net.MyNetId; if (!InPersonalInventory(item)) { SetPuppet(item, puppet: true); } else { RestoreLocalInventoryVisual(item, inInventory: true); } SendRequest(value, ItemAction.Pose, reliable: false); } } private static bool InPersonalInventory(ShipItem item) { return PersonalInventorySlotOf(item) >= 0; } private static int PersonalInventorySlotOf(ShipItem item) { if ((Object)(object)item == (Object)null) { return -1; } try { int currentInventorySlot = item.GetCurrentInventorySlot(); return (currentInventorySlot >= 0 && currentInventorySlot < 100) ? currentInventorySlot : (-1); } catch { return -1; } } private static void SetRemoteInventoryVisual(ShipItem item, bool hidden) { //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_0090: 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_0142: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)item == (Object)null) { return; } HiddenVisualState value; if (hidden) { HiddenVisualState hiddenVisualState = new HiddenVisualState(); Renderer[] componentsInChildren = ((Component)item).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val != (Object)null && val.enabled) { val.enabled = false; hiddenVisualState.Disabled.Add(val); } } hiddenVisualState.RootScale = ((Component)item).transform.localScale; Transform transform = ((Component)item).transform; for (int j = 0; j < transform.childCount; j++) { hiddenVisualState.ChildScales.Add(transform.GetChild(j).localScale); } _hiddenVisuals[item] = hiddenVisualState; } else if (_hiddenVisuals.TryGetValue(item, out value)) { foreach (Renderer item2 in value.Disabled) { if ((Object)(object)item2 != (Object)null) { item2.enabled = true; } } ((Component)item).transform.localScale = value.RootScale; Transform transform2 = ((Component)item).transform; for (int k = 0; k < transform2.childCount && k < value.ChildScales.Count; k++) { transform2.GetChild(k).localScale = value.ChildScales[k]; } _hiddenVisuals.Remove(item); } ItemRigidbody itemRigidbody = item.GetItemRigidbody(); if ((Object)(object)itemRigidbody != (Object)null) { itemRigidbody.ToggleCollider(!hidden); itemRigidbody.disableCol = hidden; } Collider component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { component.enabled = !hidden; } } catch { } } private static void RestoreLocalInventoryVisual(ShipItem item, bool inInventory) { //IL_013d: 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_0093: 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) try { if ((Object)(object)item == (Object)null) { return; } ItemRigidbody itemRigidbody = item.GetItemRigidbody(); if (!inInventory && (Object)(object)itemRigidbody != (Object)null && (Object)(object)itemRigidbody.GetCurrentInventorySlot() != (Object)null) { itemRigidbody.ExitInventorySlot(); } if (_hiddenVisuals.TryGetValue(item, out var value)) { foreach (Renderer item2 in value.Disabled) { if ((Object)(object)item2 != (Object)null) { item2.enabled = true; } } if (!inInventory) { ((Component)item).transform.localScale = value.RootScale; Transform transform = ((Component)item).transform; for (int i = 0; i < transform.childCount && i < value.ChildScales.Count; i++) { transform.GetChild(i).localScale = value.ChildScales[i]; } } _hiddenVisuals.Remove(item); } Collider component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { component.enabled = !inInventory; } if ((Object)(object)itemRigidbody != (Object)null) { if (!((Behaviour)itemRigidbody).enabled) { ((Behaviour)itemRigidbody).enabled = true; } itemRigidbody.disableCol = false; itemRigidbody.ToggleCollider(!inInventory); if (!inInventory) { ((Component)item).transform.localScale = Vector3.one; ((Component)itemRigidbody).transform.localScale = Vector3.one; } } } catch { } } private static void EnterRemoteInventoryHidden(ShipItem item, string reason) { LogItemTransition("before hidden " + reason, item); EnsureWorldParentState(item); SetRemoteInventoryVisual(item, hidden: true); SetPuppet(item, puppet: true); LogItemTransition("after hidden " + reason, item); } private static void LeaveRemoteInventoryHidden(ShipItem item, string reason) { LogItemTransition("before unhidden " + reason, item); SetRemoteInventoryVisual(item, hidden: false); LogItemTransition("after unhidden " + reason, item); } private static void EnterRemoteHeldVisual(ShipItem item, string reason) { LogItemTransition("before held " + reason, item); SetRemoteInventoryVisual(item, hidden: false); SetRootCollider(item, enabled: false); SetPuppet(item, puppet: true); LogItemTransition("after held " + reason, item); } private static void EnterFreeDynamic(ShipItem item, CoordFrame frame, Vector3 vel, string reason) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) LogItemTransition("before free " + reason, item); if (frame == CoordFrame.World) { EnsureWorldParentState(item); } SetRemoteInventoryVisual(item, hidden: false); RestoreInteractableLayer(item); SetRootCollider(item, enabled: true); SetPuppet(item, puppet: false); if (frame == CoordFrame.Boat) { vel = BoatToProxyAxes(item, vel); } MoveProxyToItem(item, kinematic: false, vel); LogItemTransition("after free " + reason, item); } private static Vector3 BoatToProxyAxes(ShipItem item, Vector3 v) { //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_0054: 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_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_0046: 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_0042: 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) try { Transform val = (((Object)(object)item != (Object)null) ? item.currentWalkCol : null); if ((Object)(object)val != (Object)null) { return val.TransformDirection(v); } Transform val2 = (((Object)(object)item != (Object)null) ? item.currentActualBoat : null); return ((Object)(object)val2 != (Object)null) ? val2.TransformDirection(v) : v; } catch { return v; } } private static Vector3 ProxyToBoatAxes(ShipItem item, Vector3 v) { //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_0054: 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_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_0046: 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_0042: 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) try { Transform val = (((Object)(object)item != (Object)null) ? item.currentWalkCol : null); if ((Object)(object)val != (Object)null) { return val.InverseTransformDirection(v); } Transform val2 = (((Object)(object)item != (Object)null) ? item.currentActualBoat : null); return ((Object)(object)val2 != (Object)null) ? val2.InverseTransformDirection(v) : v; } catch { return v; } } private static Vector3 WorldToFrameAxes(CoordFrame frame, ushort boatIndex, Vector3 v) { //IL_0004: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (frame != CoordFrame.Boat) { return v; } Transform val = BoatLocator.FindByIndex(boatIndex); if (!((Object)(object)val != (Object)null)) { return v; } return val.InverseTransformDirection(v); } private static bool IsProxyAttached(ShipItem item) { try { ItemRigidbody val = (((Object)(object)item != (Object)null) ? item.GetItemRigidbody() : null); return (Object)(object)val != (Object)null && val.attached; } catch { return false; } } private static void SetProxyAttached(ShipItem item, bool value) { try { ItemRigidbody val = (((Object)(object)item != (Object)null) ? item.GetItemRigidbody() : null); if ((Object)(object)val != (Object)null) { val.attached = value; } } catch { } } private static void MoveItemToProxy(ShipItem item) { //IL_00b6: 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_0049: 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_0053: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0082: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) try { ItemRigidbody val = (((Object)(object)item != (Object)null) ? item.GetItemRigidbody() : null); if (!((Object)(object)val == (Object)null)) { if ((Object)(object)item.currentActualBoat != (Object)null && (Object)(object)item.currentWalkCol != (Object)null) { Vector3 val2 = item.currentWalkCol.InverseTransformPoint(((Component)val).transform.position); Quaternion val3 = Quaternion.Inverse(item.currentWalkCol.rotation) * ((Component)val).transform.rotation; ((Component)item).transform.position = item.currentActualBoat.TransformPoint(val2); ((Component)item).transform.rotation = item.currentActualBoat.rotation * val3; } else { ((Component)item).transform.position = ((Component)val).transform.position; ((Component)item).transform.rotation = ((Component)val).transform.rotation; } } } catch { } } private static void EnterAttachedStatic(ShipItem item, CoordFrame frame, string reason) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) LogItemTransition("before attach " + reason, item); if ((Object)(object)item != (Object)null) { if (frame == CoordFrame.World) { EnsureWorldParentState(item); } SetRemoteInventoryVisual(item, hidden: false); RestoreInteractableLayer(item); SetRootCollider(item, enabled: true); SetPuppet(item, puppet: false); MoveProxyToItem(item, kinematic: true, Vector3.zero); SetProxyAttached(item, value: true); try { ItemRigidbody itemRigidbody = item.GetItemRigidbody(); Rigidbody val = (((Object)(object)itemRigidbody != (Object)null) ? itemRigidbody.GetBody() : null); if ((Object)(object)val != (Object)null) { val.detectCollisions = true; } } catch { } } LogItemTransition("after attach " + reason, item); } private static void SnapHangableToHook(ShipItem item, ShipItemLampHook hook) { //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_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_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_006e: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)item == (Object)null) && !((Object)(object)hook == (Object)null)) { ((Component)item).transform.position = ((Component)hook).transform.position + ((Component)hook).transform.forward * -0.128f; Vector3 eulerAngles = ((Component)item).transform.eulerAngles; eulerAngles.x = 0f; eulerAngles.z = 0f; ((Component)item).transform.eulerAngles = eulerAngles; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] SnapHangableToHook: " + ex.Message)); } } private void ScheduleFreeDynamic(ShipItem item, CoordFrame frame, Vector3 vel, string reason) { //IL_003b: 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) LogItemTransition("before pending-free " + reason, item); if ((Object)(object)item != (Object)null) { if (frame == CoordFrame.World) { EnsureWorldParentState(item); } SetRemoteInventoryVisual(item, hidden: false); SetRootCollider(item, enabled: false); SetPuppet(item, puppet: true); MoveProxyToItem(item, kinematic: true, Vector3.zero); _pendingDynamic.Add(new PendingDynamicRelease { Item = item, Frame = frame, Vel = vel, ReleaseAt = Time.time + 0.15f, Reason = reason }); } LogItemTransition("after pending-free " + reason, item); } private static void SetRootCollider(ShipItem item, bool enabled) { try { Collider val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent() : null); if ((Object)(object)val != (Object)null) { val.enabled = enabled; } } catch { } } private static void RestoreInteractableLayer(ShipItem item) { try { if (!((Object)(object)item == (Object)null) && ((Component)item).gameObject.layer == 2) { ((Component)item).gameObject.layer = 0; } } catch { } } private static void RestoreDisconnectedItem(ShipItem item) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)item == (Object)null)) { ((PickupableItem)item).held = null; SetRemoteInventoryVisual(item, hidden: false); RestoreLocalInventoryVisual(item, inInventory: false); RestoreInteractableLayer(item); SetRootCollider(item, enabled: true); SetPuppet(item, puppet: false); MoveProxyToItem(item, kinematic: false, Vector3.zero); } } catch { } } private static void LogItemTransition(string label, ShipItem item) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)item == (Object)null) { Plugin.Logger.LogInfo((object)("[ItemSync] " + label + ": item=null")); return; } ItemRigidbody itemRigidbody = item.GetItemRigidbody(); Rigidbody val = (((Object)(object)itemRigidbody != (Object)null) ? itemRigidbody.GetBody() : null); string text = "-"; try { text = (((Object)(object)itemRigidbody != (Object)null && (Object)(object)itemRigidbody.GetCurrentInventorySlot() != (Object)null) ? ((Object)itemRigidbody.GetCurrentInventorySlot()).name : "-"); } catch { } string text2 = "?"; try { if ((Object)(object)itemRigidbody != (Object)null) { if (_fItemRigidbodyOnBoat == null) { _fItemRigidbodyOnBoat = typeof(ItemRigidbody).GetField("onBoat", BindingFlags.Instance | BindingFlags.NonPublic); } text2 = ((_fItemRigidbodyOnBoat != null) ? ((bool)_fItemRigidbodyOnBoat.GetValue(itemRigidbody)).ToString() : "?"); } } catch { } ManualLogSource logger = Plugin.Logger; string[] obj3 = new string[24] { "[ItemSync] ", label, " '", item.name, "' pos=", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null }; Vector3 val2 = ((Component)item).transform.position; obj3[5] = ((Vector3)(ref val2)).ToString("F2"); obj3[6] = " parent="; obj3[7] = (((Object)(object)((Component)item).transform.parent != (Object)null) ? ((Object)((Component)item).transform.parent).name : "-"); obj3[8] = " boat="; obj3[9] = (((Object)(object)item.currentActualBoat != (Object)null) ? ((Object)item.currentActualBoat).name : "-"); obj3[10] = " walk="; obj3[11] = (((Object)(object)item.currentWalkCol != (Object)null) ? ((Object)item.currentWalkCol).name : "-"); obj3[12] = " slot="; obj3[13] = text; obj3[14] = " irbEnabled="; obj3[15] = (((Object)(object)itemRigidbody != (Object)null) ? ((Behaviour)itemRigidbody).enabled.ToString() : "-"); obj3[16] = " rbKin="; obj3[17] = (((Object)(object)val != (Object)null) ? val.isKinematic.ToString() : "-"); obj3[18] = " rbDetect="; obj3[19] = (((Object)(object)val != (Object)null) ? val.detectCollisions.ToString() : "-"); obj3[20] = " onBoat="; obj3[21] = text2; obj3[22] = " scale="; val2 = ((Component)item).transform.localScale; obj3[23] = ((Vector3)(ref val2)).ToString("F2"); logger.LogInfo((object)string.Concat(obj3)); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] LogItemTransition: " + ex.Message)); } } private static void PrepareLocalWithdrawPickup(ShipItem item) { //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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_0072: 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_0079: 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_0098: 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_00ab: 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_00d6: 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) try { if (!((Object)(object)item == (Object)null) && !((Object)(object)((PickupableItem)item).held == (Object)null)) { RestoreLocalInventoryVisual(item, inInventory: false); Transform transform = ((Component)((PickupableItem)item).held).transform; if ((Object)(object)transform != (Object)null) { Vector3 position = transform.position + transform.forward * Mathf.Max(0.7f, ((PickupableItem)item).holdDistance) + transform.up * ((PickupableItem)item).holdHeight; Quaternion rotation = transform.rotation * Quaternion.Euler(((PickupableItem)item).heldRotationOffset, 0f, 0f); ((Component)item).transform.position = position; ((Component)item).transform.rotation = rotation; ((Component)item).transform.localScale = Vector3.one; } if ((Object)(object)LocalPlayerBoat() == (Object)null) { EnsureWorldParentState(item); } MoveProxyToItem(item, kinematic: true, Vector3.zero); ItemRigidbody itemRigidbody = item.GetItemRigidbody(); if ((Object)(object)itemRigidbody != (Object)null) { ((Component)itemRigidbody).transform.localScale = Vector3.one; } ResetPointerBigItemCapture(item); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] PrepareLocalWithdrawPickup: " + ex.Message)); } } private static void ResetPointerBigItemCapture(ShipItem item) { //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_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_0067: 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_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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_014e: 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_018c: 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_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)item == (Object)null || (Object)(object)((PickupableItem)item).held == (Object)null || !((PickupableItem)item).big) { return; } Transform transform = ((Component)((PickupableItem)item).held).transform; if (!((Object)(object)transform == (Object)null)) { float num = Mathf.Max(1.6f, ((PickupableItem)item).holdDistance); Vector3 val = transform.position + transform.forward * num + transform.up * ((PickupableItem)item).holdHeight; Quaternion val2 = transform.rotation * Quaternion.Euler(((PickupableItem)item).heldRotationOffset, 0f, 0f); ((Component)item).transform.position = val; ((Component)item).transform.rotation = val2; ((Component)item).transform.localScale = Vector3.one; ItemRigidbody itemRigidbody = item.GetItemRigidbody(); if ((Object)(object)itemRigidbody != (Object)null) { ((Component)itemRigidbody).transform.localScale = Vector3.one; } if (_fPointerBigItemLocalPos == null) { _fPointerBigItemLocalPos = typeof(GoPointer).GetField("bigItemLocalPos", BindingFlags.Instance | BindingFlags.NonPublic); _fPointerDecolLocalPos = typeof(GoPointer).GetField("decolLocalPos", BindingFlags.Instance | BindingFlags.NonPublic); _fPointerBigItemLocalRot = typeof(GoPointer).GetField("bigItemLocalRot", BindingFlags.Instance | BindingFlags.NonPublic); } Vector3 val3 = transform.InverseTransformPoint(val); if (_fPointerBigItemLocalPos != null) { _fPointerBigItemLocalPos.SetValue(((PickupableItem)item).held, val3); } if (_fPointerDecolLocalPos != null) { _fPointerDecolLocalPos.SetValue(((PickupableItem)item).held, val3); } if (_fPointerBigItemLocalRot != null) { _fPointerBigItemLocalRot.SetValue(((PickupableItem)item).held, Quaternion.Inverse(transform.rotation) * val2); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] ResetPointerBigItemCapture: " + ex.Message)); } } private static void PrepareLocalPickupPose(GoPointer pointer, ShipItem item) { //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_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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_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_008b: 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_009e: 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) try { if (!((Object)(object)pointer == (Object)null) && !((Object)(object)item == (Object)null)) { RestoreLocalInventoryVisual(item, inInventory: false); Transform transform = ((Component)pointer).transform; if ((Object)(object)transform != (Object)null) { Vector3 position = transform.position + transform.forward * Mathf.Max(0.7f, ((PickupableItem)item).holdDistance) + transform.up * ((PickupableItem)item).holdHeight; Quaternion rotation = transform.rotation * Quaternion.Euler(((PickupableItem)item).heldRotationOffset, 0f, 0f); ((Component)item).transform.position = position; ((Component)item).transform.rotation = rotation; } if ((Object)(object)LocalPlayerBoat() == (Object)null) { EnsureWorldParentState(item); } MoveProxyToItem(item, kinematic: true, Vector3.zero); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] PrepareLocalPickupPose: " + ex.Message)); } } private void SendRequest(ItemEntry e, ItemAction action, bool reliable) { if (e != null && !((Object)(object)e.Item == (Object)null)) { ItemRequestMsg msg = BuildRequest(e, action, _net.Clock.ServerTick); _net.Broadcast(msg, (DeliveryMethod)(reliable ? 2 : 4)); } } private ItemRequestMsg BuildRequest(ItemEntry e, ItemAction action, long tick) { //IL_00d3: 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_00da: 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_00e3: 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_0050: 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_0084: 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_007a: 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) BuildPose(e.Item, tick, out var frame, out var boatIndex, out var pos, out var rot, out var vel); if (action == ItemAction.AltHeld) { ShipItem item = e.Item; ShipItemOar val = (ShipItemOar)(object)((item is ShipItemOar) ? item : null); if (val != null && (Object)(object)val.waterPos != (Object)null) { BuildTransformPose(val.waterPos, tick, out frame, out boatIndex, out pos, out rot); vel = Vector3.zero; } } if (action == ItemAction.Drop) { Vector3 val2 = RealItemVelocity(e.Item); if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f) { vel = ((frame == CoordFrame.Boat) ? ProxyToBoatAxes(e.Item, val2) : val2); } } return new ItemRequestMsg { Action = action, Index = e.Index, InstanceId = e.InstanceId, PrefabIndex = e.PrefabIndex, Tick = tick, Frame = frame, BoatIndex = boatIndex, Pos = pos, Rot = rot, Vel = vel, Amount = e.Item.amount, Health = e.Item.health, Sold = e.Item.sold, Nailed = e.Item.nailed, CrateId = CrateIdOf(e.Item), CargoPort = CargoPortOf(e.Item), InventorySlot = PersonalInventorySlotOf(e.Item), Attached = IsProxyAttached(e.Item) }; } private static void BuildTransformPose(Transform source, long tick, out CoordFrame frame, out ushort boatIndex, out Vector3 pos, out Quaternion rot) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0045: 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_0050: 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_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_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) Transform val = ParentBoat(source); if ((Object)(object)val == (Object)null) { val = LocalPlayerBoat(); } if ((Object)(object)val != (Object)null) { frame = CoordFrame.Boat; boatIndex = BoatLocator.IndexOf(val); pos = val.InverseTransformPoint(source.position); rot = Quaternion.Inverse(val.rotation) * source.rotation; } else { frame = CoordFrame.World; boatIndex = ushort.MaxValue; pos = (CoordSpace.Ready ? CoordSpace.LocalToReal(source.position) : source.position); rot = source.rotation; } } private ItemStateMsg BuildState(ItemEntry e, long tick) { //IL_00a3: 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_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_00b3: Unknown result type (might be due to invalid IL or missing references) BuildPose(e.Item, tick, out var frame, out var boatIndex, out var pos, out var rot, out var vel); uint holderNetId = ((e.HolderNetId != 0) ? e.HolderNetId : (((Object)(object)((PickupableItem)e.Item).held != (Object)null) ? _net.MyNetId : 0u)); int num = PersonalInventorySlotOf(e.Item); if (num < 0) { num = e.InventorySlot; } return new ItemStateMsg { Index = e.Index, InstanceId = e.InstanceId, PrefabIndex = e.PrefabIndex, Tick = tick, Frame = frame, BoatIndex = boatIndex, Pos = pos, Rot = rot, Vel = vel, HolderNetId = holderNetId, Amount = e.Item.amount, Health = e.Item.health, Sold = e.Item.sold, Nailed = e.Item.nailed, CrateId = CrateIdOf(e.Item), CargoPort = CargoPortOf(e.Item), InventorySlot = num, Attached = IsProxyAttached(e.Item) }; } private void BuildPose(ShipItem item, long tick, out CoordFrame frame, out ushort boatIndex, out Vector3 pos, out Quaternion rot, out Vector3 vel) { //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_007f: 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) //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_00a1: 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_00c2: 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_00f7: 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_00e6: 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_0159: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: 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_0151: Unknown result type (might be due to invalid IL or missing references) _byItem.TryGetValue(item, out var value); Transform val = ((value != null && value.ForceWorldPoseUntilDrop) ? null : (((Object)(object)((PickupableItem)item).held != (Object)null) ? LocalPlayerBoat() : (((Object)(object)item.currentActualBoat != (Object)null) ? item.currentActualBoat : ParentBoat(((Component)item).transform)))); if ((Object)(object)val != (Object)null) { frame = CoordFrame.Boat; boatIndex = BoatLocator.IndexOf(val); pos = val.InverseTransformPoint(((Component)item).transform.position); rot = Quaternion.Inverse(val.rotation) * ((Component)item).transform.rotation; } else { frame = CoordFrame.World; boatIndex = ushort.MaxValue; pos = (CoordSpace.Ready ? CoordSpace.LocalToReal(((Component)item).transform.position) : ((Component)item).transform.position); rot = ((Component)item).transform.rotation; } vel = Vector3.zero; if (value == null) { return; } if (value.HaveLast && value.LastFrame == frame && value.LastBoatIndex == boatIndex) { float num = (float)(tick - value.LastTick) / 1000f; if (num > 0.0001f) { vel = (pos - value.LastPos) / num; } } value.LastPos = pos; value.LastTick = tick; value.LastFrame = frame; value.LastBoatIndex = boatIndex; value.HaveLast = true; } private static Transform LocalPlayerBoat() { try { PlayerEmbarkerNew val = Object.FindObjectOfType(); return ((Object)(object)val != (Object)null) ? val.debugOutCurrentBoat : null; } catch { return null; } } private void ApplyWirePose(ShipItem item, CoordFrame frame, ushort boatIndex, Vector3 pos, Quaternion rot, Vector3 vel, float amount, float health, bool sold, bool nailed, bool held) { //IL_0043: 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_003f: 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_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_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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null) { return; } Vector3 position; Quaternion rotation; if (frame == CoordFrame.Boat) { Transform val = BoatLocator.FindByIndex(boatIndex); if ((Object)(object)val == (Object)null) { return; } position = val.TransformPoint(pos); rotation = val.rotation * rot; } else { position = (CoordSpace.Ready ? CoordSpace.RealToLocal(pos) : pos); rotation = rot; } ((Component)item).transform.position = position; ((Component)item).transform.rotation = rotation; ((Component)item).transform.localScale = Vector3.one; if (frame == CoordFrame.Boat) { EnsureBoatParentState(item, boatIndex); } else { EnsureWorldParentState(item); } ApplyScalarState(item, amount, health, sold, nailed); MoveProxyToItem(item, held, vel); } private void ApplyScalarState(ShipItem item, float amount, float health, bool sold, bool nailed) { if (!((Object)(object)item == (Object)null)) { item.amount = amount; item.health = health; item.sold = sold; item.nailed = nailed; ShipItemFishingRod val = (ShipItemFishingRod)(object)((item is ShipItemFishingRod) ? item : null); if (val != null) { InvokeRodUpdateHook(val); } } } private static void InvokeRodUpdateHook(ShipItemFishingRod rod) { try { RodUpdateHookMethod?.Invoke(rod, null); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Fishing rod UpdateHook: " + ex.Message)); } } private void ApplyCrateMembership(ShipItem item, int crateId) { if ((Object)(object)item == (Object)null) { return; } SaveablePrefab component = ((Component)item).GetComponent(); if ((Object)(object)component == (Object)null || component.currentCrateId == crateId) { return; } ApplyingCrate = true; try { int currentCrateId = component.currentCrateId; if (currentCrateId != 0 && _byInstanceId.TryGetValue(currentCrateId, out var value) && (Object)(object)value.Item != (Object)null) { CrateInventory component2 = ((Component)value.Item).GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.WithdrawItem(item); } } if (crateId != 0 && _byInstanceId.TryGetValue(crateId, out var value2) && (Object)(object)value2.Item != (Object)null) { CrateInventory component3 = ((Component)value2.Item).GetComponent(); if ((Object)(object)component3 != (Object)null) { component3.InsertItem(item); } } component.currentCrateId = crateId; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] ApplyCrateMembership: " + ex.Message)); } finally { ApplyingCrate = false; } } private void ApplyCargoMembership(ShipItem item, int port) { if ((Object)(object)item == (Object)null) { return; } int num = CargoPortOf(item); if (num == port) { return; } ApplyingCargo = true; try { CargoCarrier[] carriers = CargoCarrier.carriers; if (num >= 0 && carriers != null && num < carriers.Length && (Object)(object)carriers[num] != (Object)null) { carriers[num].cargo.Remove(item); item.WithdrawFromCarrier(); RestoreLocalInventoryVisual(item, inInventory: false); } if (port >= 0 && carriers != null && port < carriers.Length && (Object)(object)carriers[port] != (Object)null) { carriers[port].LoadSavedItem(item); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] ApplyCargoMembership: " + ex.Message)); } finally { ApplyingCargo = false; } } private void ConfigureNetFrame(ItemEntry e, CoordFrame frame, ushort boatIndex) { if (frame == CoordFrame.Boat) { e.Net.ToWorldPos = delegate(Vector3 p) { //IL_0018: 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_0015: Unknown result type (might be due to invalid IL or missing references) Transform val = BoatLocator.FindByIndex(boatIndex); return (!((Object)(object)val != (Object)null)) ? p : val.TransformPoint(p); }; e.Net.ToWorldRot = delegate(Quaternion q) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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) Transform val = BoatLocator.FindByIndex(boatIndex); return (!((Object)(object)val != (Object)null)) ? q : (val.rotation * q); }; } else { e.Net.ToWorldPos = CoordSpace.RealToLocal; e.Net.ToWorldRot = (Quaternion q) => q; } } private void PrepareForRemotePose(ShipItem item, bool held) { if ((Object)(object)item == (Object)null) { return; } if (held) { ((PickupableItem)item).held = ((item is ShipItemBroom) ? VisualPointer() : null); if (((Component)item).gameObject.layer != 2) { ((Component)item).gameObject.layer = 2; } } else if (((Component)item).gameObject.layer == 2) { ((PickupableItem)item).held = null; RestoreInteractableLayer(item); } } private static GoPointer VisualPointer() { try { if ((Object)(object)_visualPointer == (Object)null) { _visualPointer = Object.FindObjectOfType(); } return _visualPointer; } catch { return null; } } private static void MoveProxyToItem(ShipItem item, bool kinematic, Vector3 vel) { //IL_00d2: 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_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_006f: 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_007b: 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_008b: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_013b: 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_0112: Unknown result type (might be due to invalid IL or missing references) try { ItemRigidbody val = (((Object)(object)item != (Object)null) ? item.GetItemRigidbody() : null); Rigidbody val2 = (((Object)(object)val != (Object)null) ? val.GetBody() : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { if ((Object)(object)item.currentActualBoat != (Object)null && (Object)(object)item.currentWalkCol != (Object)null) { Vector3 val3 = item.currentActualBoat.InverseTransformPoint(((Component)item).transform.position); Quaternion val4 = Quaternion.Inverse(item.currentActualBoat.rotation) * ((Component)item).transform.rotation; ((Component)val).transform.position = item.currentWalkCol.TransformPoint(val3); ((Component)val).transform.rotation = item.currentWalkCol.rotation * val4; } else { ((Component)val).transform.position = ((Component)item).transform.position; ((Component)val).transform.rotation = ((Component)item).transform.rotation; } val2.isKinematic = kinematic; val2.detectCollisions = !kinematic; if (kinematic) { val2.velocity = Vector3.zero; val2.angularVelocity = Vector3.zero; } else { val2.maxDepenetrationVelocity = 1f; val2.velocity = Vector3.ClampMagnitude(vel, 15f); val2.angularVelocity = Vector3.zero; } } } catch { } } private static void EnsureBoatParentState(ShipItem item, ushort boatIndex) { if ((Object)(object)item == (Object)null) { return; } try { Transform val = BoatLocator.FindByIndex(boatIndex); if ((Object)(object)val == (Object)null) { return; } BoatEmbarkCollider val2 = null; BoatEmbarkCollider[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (BoatEmbarkCollider val3 in componentsInChildren) { if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val3).transform.parent == (Object)(object)val) { val2 = val3; break; } if ((Object)(object)val2 == (Object)null) { val2 = val3; } } if ((Object)(object)val2 == (Object)null || (Object)(object)val2.walkCollider == (Object)null) { return; } item.currentActualBoat = val; item.currentWalkCol = val2.walkCollider; ((Component)item).transform.parent = val; Collider component = ((Component)val2).GetComponent(); if ((Object)(object)component != (Object)null) { if (_fShipItemCurrentBoatCollider == null) { _fShipItemCurrentBoatCollider = typeof(ShipItem).GetField("currentBoatCollider", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fShipItemCurrentBoatCollider != null) { _fShipItemCurrentBoatCollider.SetValue(item, component); } } SaveablePrefab component2 = ((Component)item).GetComponent(); SaveableObject val4 = (((Object)(object)val.parent != (Object)null) ? ((Component)val.parent).GetComponent() : null); if ((Object)(object)component2 != (Object)null && (Object)(object)val4 != (Object)null) { component2.SetParentObject(val4.sceneIndex); } ItemRigidbody itemRigidbody = item.GetItemRigidbody(); if (!((Object)(object)itemRigidbody == (Object)null)) { if (_fItemRigidbodyOnBoat == null) { _fItemRigidbodyOnBoat = typeof(ItemRigidbody).GetField("onBoat", BindingFlags.Instance | BindingFlags.NonPublic); } if (!(_fItemRigidbodyOnBoat != null) || !(bool)_fItemRigidbodyOnBoat.GetValue(itemRigidbody)) { itemRigidbody.EnterBoat(); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Failed to parent item to boat: " + ex.Message)); } } private static void EnsureWorldParentState(ShipItem item) { if ((Object)(object)item == (Object)null) { return; } try { if (_mShipItemExitBoat == null) { _mShipItemExitBoat = typeof(ShipItem).GetMethod("ExitBoat", BindingFlags.Instance | BindingFlags.NonPublic); } if ((Object)(object)item.currentActualBoat != (Object)null && _mShipItemExitBoat != null) { _mShipItemExitBoat.Invoke(item, null); } Transform val = (((Object)(object)FloatingOriginManager.instance != (Object)null) ? ((Component)FloatingOriginManager.instance).transform : null); if ((Object)(object)val != (Object)null) { ((Component)item).transform.parent = val; ItemRigidbody itemRigidbody = item.GetItemRigidbody(); if ((Object)(object)itemRigidbody != (Object)null) { ((Component)itemRigidbody).transform.parent = val; } } item.currentActualBoat = null; item.currentWalkCol = null; if (_fShipItemCurrentBoatCollider == null) { _fShipItemCurrentBoatCollider = typeof(ShipItem).GetField("currentBoatCollider", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fShipItemCurrentBoatCollider != null) { _fShipItemCurrentBoatCollider.SetValue(item, null); } if (_fShipItemCurrentlyStayedEmbarkCol == null) { _fShipItemCurrentlyStayedEmbarkCol = typeof(ShipItem).GetField("currentlyStayedEmbarkCol", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fShipItemCurrentlyStayedEmbarkCol != null) { _fShipItemCurrentlyStayedEmbarkCol.SetValue(item, null); } ItemRigidbody itemRigidbody2 = item.GetItemRigidbody(); if ((Object)(object)itemRigidbody2 != (Object)null) { if (_fItemRigidbodyOnBoat == null) { _fItemRigidbodyOnBoat = typeof(ItemRigidbody).GetField("onBoat", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fItemRigidbodyOnBoat != null) { _fItemRigidbodyOnBoat.SetValue(itemRigidbody2, false); } } SaveablePrefab component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null && component.GetParentObject() != -3) { component.SetParentObject(-1); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Failed to unparent item from boat: " + ex.Message)); } } private void BroadcastSpawn(ItemEntry e) { //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_0078: 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_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) if (e != null && !((Object)(object)e.Item == (Object)null)) { BuildPose(e.Item, _net.Clock.ServerTick, out var frame, out var boatIndex, out var pos, out var rot, out var vel); _net.Broadcast(new SpawnObjectMsg { Kind = 8, InstanceId = e.InstanceId, PrefabIndex = e.PrefabIndex, Frame = frame, BoatIndex = boatIndex, Pos = pos, Rot = rot, Vel = vel, HolderNetId = e.HolderNetId, Amount = e.Item.amount, Health = e.Item.health, Sold = e.Item.sold, Nailed = e.Item.nailed, CrateId = CrateIdOf(e.Item), CargoPort = CargoPortOf(e.Item), InventorySlot = e.InventorySlot }, (DeliveryMethod)2); Remember("out spawn id=" + e.InstanceId + " prefab=" + e.PrefabIndex); } } private void BroadcastDespawn(ItemEntry e) { if (e != null) { _net.Registry.Remove(e.NetId); _net.Broadcast(new DespawnObjectMsg { Kind = 8, InstanceId = e.InstanceId }, (DeliveryMethod)2); Remember("out despawn id=" + e.InstanceId); } } public void OnSpawnObject(SpawnObjectMsg msg, NetPeer fromPeer) { //IL_0033: 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_016a: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client || msg.Kind != 8) { return; } ItemEntry itemEntry = ResolveClient(msg.InstanceId, msg.PrefabIndex, msg.Frame, msg.BoatIndex, msg.Pos, msg.Amount, msg.Health, msg.Sold, msg.Nailed, msg.HolderNetId == 0); if (itemEntry == null || (Object)(object)itemEntry.Item == (Object)null) { Remember("reject spawn id=" + msg.InstanceId); return; } itemEntry.HolderNetId = msg.HolderNetId; itemEntry.InventorySlot = msg.InventorySlot; ApplyScalarState(itemEntry.Item, msg.Amount, msg.Health, msg.Sold, msg.Nailed); ApplyCrateMembership(itemEntry.Item, msg.CrateId); ApplyCargoMembership(itemEntry.Item, msg.CargoPort); SetRemoteInventoryVisual(itemEntry.Item, msg.InventorySlot >= 0 && msg.HolderNetId != _net.MyNetId); if (msg.HolderNetId != 0 && msg.HolderNetId != _net.MyNetId) { ConfigureNetFrame(itemEntry, msg.Frame, msg.BoatIndex); itemEntry.Net.Push(_net.Clock.ServerTick, msg.Pos, msg.Rot, msg.Vel); } Remember("in spawn id=" + msg.InstanceId); } public void OnDespawnObject(DespawnObjectMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Client || msg.Kind != 8) { return; } if (_localClaimed.Remove(msg.InstanceId)) { if (_byInstanceId.TryGetValue(msg.InstanceId, out var value)) { _items.Remove(value); _byInstanceId.Remove(msg.InstanceId); _net.Registry.Remove(value.NetId); if ((Object)(object)value.Item != (Object)null) { _byItem.Remove(value.Item); } } Remember("in despawn id=" + msg.InstanceId + " (claimed -> keeping in belt)"); return; } if (!_byInstanceId.TryGetValue(msg.InstanceId, out var value2)) { Remember("missing despawn id=" + msg.InstanceId); return; } ShipItem item = value2.Item; _net.Registry.Remove(value2.NetId); _items.Remove(value2); _byInstanceId.Remove(msg.InstanceId); if ((Object)(object)item != (Object)null) { _byItem.Remove(item); _localHeld.Remove(item); try { Object.Destroy((Object)(object)((Component)item).gameObject); } catch { } } Remember("in despawn id=" + msg.InstanceId); } public void NotifyAltHeld(ShipItem item) { if (_net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)item == (Object)null) && !((Object)(object)((PickupableItem)item).held == (Object)null)) { float num = 1f / Mathf.Max(1f, AltHeldHz); _altHeldTimer += Time.deltaTime; if (!(_altHeldTimer < num)) { _altHeldTimer = 0f; ForwardHeldAction(item, ItemAction.AltHeld, reliable: false); } } } public void NotifyConsume(ShipItem item) { if (_net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)item == (Object)null)) { RefreshItems(force: false); if (_byItem.TryGetValue(item, out var value)) { SendRequest(value, ItemAction.Consume, reliable: true); Remember("out consume #" + value.Index + " '" + item.name + "'"); } } } public void OnLocalNail(ShipItem target) { if (_net.State != LinkState.Connected || (Object)(object)target == (Object)null) { return; } RefreshItems(force: false); if (_byItem.TryGetValue(target, out var value)) { long serverTick = _net.Clock.ServerTick; if (_net.Role == Role.Client) { ItemRequestMsg itemRequestMsg = BuildRequest(value, ItemAction.Nail, serverTick); itemRequestMsg.Nailed = target.nailed; _net.Broadcast(itemRequestMsg, (DeliveryMethod)2); Remember("out nail #" + value.Index + "=" + target.nailed + " '" + target.name + "'"); } else { _net.Broadcast(BuildState(value, serverTick), (DeliveryMethod)2); Remember("nail(host) #" + value.Index + "=" + target.nailed); } } } public void OnLocalRodHook(ShipItemFishingRod rod, bool attached, ShipItem consumedHook) { if (_net.State != LinkState.Connected || (Object)(object)rod == (Object)null) { return; } RefreshItems(force: false); if (!_byItem.TryGetValue((ShipItem)(object)rod, out var value)) { return; } long serverTick = _net.Clock.ServerTick; if (_net.Role == Role.Client) { if ((Object)(object)consumedHook != (Object)null) { int num = InstanceIdOf(consumedHook); int num2 = PrefabIndexOf(consumedHook); if (num > 0 && num2 > 0 && _hostIds.Contains(num)) { _net.Broadcast(new ItemRequestMsg { Action = ItemAction.Consume, InstanceId = num, PrefabIndex = num2 }, (DeliveryMethod)2); } } _net.Broadcast(BuildRequest(value, ItemAction.RodHook, serverTick), (DeliveryMethod)2); Remember("out rod-hook #" + value.Index + "=" + (attached ? 1 : 0)); } else { _net.Broadcast(BuildState(value, serverTick), (DeliveryMethod)2); Remember("rod-hook(host) #" + value.Index + "=" + (attached ? 1 : 0)); } } public void OnLocalCrate(ShipItem item) { if (ApplyingCrate || _net.State != LinkState.Connected || (Object)(object)item == (Object)null) { return; } RefreshItems(force: false); if (_byItem.TryGetValue(item, out var value)) { long serverTick = _net.Clock.ServerTick; if (_net.Role == Role.Client) { _net.Broadcast(BuildRequest(value, ItemAction.Crate, serverTick), (DeliveryMethod)2); Remember("out crate #" + value.Index + "->" + CrateIdOf(item)); } else { _net.Broadcast(BuildState(value, serverTick), (DeliveryMethod)2); Remember("crate(host) #" + value.Index + "->" + CrateIdOf(item)); } } } public bool ForwardUnseal(ShipItemCrate crate) { if (_net.Role != Role.Client || _net.State != LinkState.Connected || (Object)(object)crate == (Object)null) { return false; } SaveablePrefab component = ((Component)crate).GetComponent(); if ((Object)(object)component == (Object)null || component.instanceId <= 0) { return false; } _net.Broadcast(new ItemRequestMsg { Action = ItemAction.Unseal, InstanceId = component.instanceId, PrefabIndex = component.prefabIndex }, (DeliveryMethod)2); Remember("out unseal crate id=" + component.instanceId); return true; } public void OnLocalCargo(ShipItem item) { if (ApplyingCargo || _net.State != LinkState.Connected || (Object)(object)item == (Object)null) { return; } RefreshItems(force: false); if (_byItem.TryGetValue(item, out var value)) { int num = CargoPortOf(item); if (_net.Role == Role.Client && num < 0) { PrepareLocalWithdrawPickup(item); value.DropWithoutProxyVelocity = true; value.ForceWorldPoseUntilDrop = (Object)(object)LocalPlayerBoat() == (Object)null; } else if (_net.Role == Role.Host && num < 0) { ResetPointerBigItemCapture(item); } long serverTick = _net.Clock.ServerTick; if (_net.Role == Role.Client) { _net.Broadcast(BuildRequest(value, ItemAction.Cargo, serverTick), (DeliveryMethod)2); } else { _net.Broadcast(BuildState(value, serverTick), (DeliveryMethod)2); } Remember("out cargo #" + value.Index + " port=" + num); } } public void OnLocalInventory(ShipItem item) { if (_net.State == LinkState.Connected && !((Object)(object)item == (Object)null)) { int num = PersonalInventorySlotOf(item); if (_net.Role == Role.Host) { RefreshItems(force: true); Remember("local host belt slot=" + num); return; } if (num >= 0) { ClaimItemToBelt(item, num); return; } RefreshItems(force: true); PrepareLocalWithdrawPickup(item); _pendingHeldItem = item; NotifyClientAuthored(item); Remember("out belt->hand author '" + item.name + "'"); } } private void ClaimItemToBelt(ShipItem item, int slot) { RefreshItems(force: true); if (_net.Role == Role.Host) { _localHeld.Remove(item); RestoreLocalInventoryVisual(item, inInventory: true); Remember("local host claim->belt slot=" + slot); return; } int num = InstanceIdOf(item); int num2 = PrefabIndexOf(item); if (num > 0 && num2 > 0 && _hostIds.Contains(num) && !_localClaimed.Contains(num)) { _localClaimed.Add(num); _net.Broadcast(new ItemRequestMsg { Action = ItemAction.Consume, InstanceId = num, PrefabIndex = num2 }, (DeliveryMethod)2); Remember("out claim->belt id=" + num + " slot=" + slot); } _localHeld.Remove(item); RestoreLocalInventoryVisual(item, inInventory: true); } public void NotifyAltActivate(ShipItem item) { if (_net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)item == (Object)null) && !((Object)(object)((PickupableItem)item).held == (Object)null) && !(item is ShipItemCrate)) { ForwardHeldAction(item, ItemAction.AltActivate, reliable: true); } } public void NotifyBroomActivated(ShipItemBroom broom) { if (_net.State != LinkState.Connected || (Object)(object)broom == (Object)null) { return; } RefreshItems(force: false); if (!_byItem.TryGetValue((ShipItem)(object)broom, out var value)) { return; } if (_net.Role == Role.Client) { if (!((Object)(object)((PickupableItem)broom).held == (Object)null)) { _localHeld[(ShipItem)(object)broom] = _net.MyNetId; SendRequest(value, ItemAction.AltActivate, reliable: true); Remember("out broom #" + value.Index + " '" + ((ShipItem)broom).name + "'"); } } else if (_net.Role == Role.Host) { ItemRequestMsg msg = BuildRequest(value, ItemAction.AltActivate, _net.Clock.ServerTick); _net.Broadcast(msg, (DeliveryMethod)2); Remember("broom(host) #" + value.Index + " '" + ((ShipItem)broom).name + "'"); } } public void NotifyLampHook(ShipItemLampHook hook, PickupableItem heldItem) { //IL_0097: 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) if (_net.Role != Role.Client || _net.State != LinkState.Connected) { return; } ShipItem val = (ShipItem)(object)((heldItem is ShipItem) ? heldItem : null); if ((Object)(object)hook == (Object)null || (Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponent() == (Object)null) { return; } RefreshItems(force: true); if (_byItem.TryGetValue(val, out var value)) { int num = InstanceIdOf((ShipItem)(object)hook); int num2 = PrefabIndexOf((ShipItem)(object)hook); if (num > 0 && num2 > 0) { _suppressNextDrop.Add(val); _localHeld.Remove(val); SnapHangableToHook(val, hook); MoveProxyToItem(val, kinematic: true, Vector3.zero); SetProxyAttached(val, value: true); ItemRequestMsg itemRequestMsg = BuildRequest(value, ItemAction.LampHook, _net.Clock.ServerTick); itemRequestMsg.CrateId = num; itemRequestMsg.CargoIndex = num2; itemRequestMsg.Attached = true; itemRequestMsg.Vel = Vector3.zero; _net.Broadcast(itemRequestMsg, (DeliveryMethod)2); Remember("out lamp-hook #" + value.Index + " hook=" + num); } } } public void NotifyHeldItemStateChanged(ShipItem item, string reason) { if (_net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)item == (Object)null) && !((Object)(object)((PickupableItem)item).held == (Object)null)) { NotifyItemStateChanged(item, reason); } } public void NotifyItemStateChanged(ShipItem item, string reason) { if (_net.State != LinkState.Connected || (Object)(object)item == (Object)null) { return; } RefreshItems(force: false); if (!_byItem.TryGetValue(item, out var value)) { return; } if (_net.Role == Role.Host) { _net.Broadcast(BuildState(value, _net.Clock.ServerTick), (DeliveryMethod)2); Remember("state(host) #" + value.Index + " '" + item.name + "' " + reason); } else if (_net.Role == Role.Client) { if ((Object)(object)((PickupableItem)item).held != (Object)null) { _localHeld[item] = _net.MyNetId; SetPuppet(item, puppet: true); } SendRequest(value, ItemAction.State, reliable: true); Remember("out state #" + value.Index + " '" + item.name + "' " + reason + " amount=" + item.amount.ToString("0.##") + " health=" + item.health.ToString("0.##")); } } private void ForwardHeldAction(ShipItem item, ItemAction action, bool reliable) { RefreshItems(force: false); if (_byItem.TryGetValue(item, out var value)) { _localHeld[item] = _net.MyNetId; SendRequest(value, action, reliable); Remember("out " + action.ToString() + " #" + value.Index + " '" + item.name + "'"); } } private void ReplayHeldAction(ItemEntry e, ItemAction action, uint actor) { if (e == null || (Object)(object)e.Item == (Object)null) { return; } e.HolderNetId = actor; _localHeld[e.Item] = actor; string text = ((action == ItemAction.AltActivate) ? "OnAltActivate" : "OnAltHeld"); GoPointer held = ((PickupableItem)e.Item).held; try { ((PickupableItem)e.Item).held = HostPointer(); if (ShouldReplayNoArgHeldAction(e.Item, action)) { MethodInfo method = ((object)e.Item).GetType().GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { method.Invoke(e.Item, null); } } MethodInfo method2 = ((object)e.Item).GetType().GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(GoPointer) }, null); if (method2 != null) { method2.Invoke(e.Item, new object[1] { HostPointer() }); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Replay error " + text + " on '" + ((object)e.Item).GetType().Name + "': " + ex.Message)); } finally { try { ((PickupableItem)e.Item).held = held; } catch { } } } private static void DisconnectHangable(ShipItem item) { try { HangableItem val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent() : null); if ((Object)(object)val != (Object)null && val.IsHanging()) { val.DisconnectJoint(); } SetProxyAttached(item, value: false); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] DisconnectHangable: " + ex.Message)); } } private static bool ShouldReplayNoArgHeldAction(ShipItem item, ItemAction action) { if ((Object)(object)item == (Object)null) { return false; } if (item is ShipItemHammer) { return false; } if (item is ShipItemBroom) { return false; } if (item is ShipItemFoldable) { return false; } if (action == ItemAction.AltActivate && !item.sold) { return false; } if (item is ShipItemFood) { return false; } return true; } private void PulseBroom(ShipItem item) { try { Cleaner val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponentInChildren(true) : null); if ((Object)(object)val != (Object)null) { if ((Object)(object)((PickupableItem)item).held == (Object)null) { ((PickupableItem)item).held = ((_net.Role == Role.Host) ? HostPointer() : VisualPointer()); } val.activated = true; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] PulseBroom: " + ex.Message)); } } private bool TryApplyOarRow(ItemEntry e, ItemRequestMsg msg, uint actor) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_0114: 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_011a: 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_0132: 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_0141: 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) ShipItemOar val = (ShipItemOar)((e != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { return false; } try { e.HolderNetId = actor; _localHeld[e.Item] = actor; if (!e.Item.sold) { return true; } Rigidbody val2 = BoatBody((msg.Frame == CoordFrame.Boat) ? BoatLocator.FindByIndex(msg.BoatIndex) : null); if ((Object)(object)val2 == (Object)null) { Remember("oar-row without Rigidbody boat=" + msg.BoatIndex); return true; } if (!WireToWorld(msg.Frame, msg.BoatIndex, msg.Pos, msg.Rot, out var worldPos, out var worldRot)) { Remember("oar-row without pose boat=" + msg.BoatIndex); return true; } float maxBoatSpeed = val.maxBoatSpeed; Vector3 velocity = val2.velocity; float num = Mathf.InverseLerp(maxBoatSpeed, 0f, ((Vector3)(ref velocity)).magnitude); float num2 = 1f / Mathf.Max(1f, AltHeldHz); Vector3 val3 = worldRot * Vector3.forward * (0f - val.rowForce) * num2 * num; val2.AddForceAtPosition(val3, worldPos); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Failed to apply oar stroke: " + ex.Message)); return true; } } private static Rigidbody BoatBody(Transform boat) { if ((Object)(object)boat == (Object)null) { return null; } Rigidbody component = ((Component)boat).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } if (!((Object)(object)boat.parent != (Object)null)) { return null; } return ((Component)boat.parent).GetComponent(); } private static bool WireToWorld(CoordFrame frame, ushort boatIndex, Vector3 pos, Quaternion rot, out Vector3 worldPos, out Quaternion worldRot) { //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_005a: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_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_0063: 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) if (frame == CoordFrame.Boat) { Transform val = BoatLocator.FindByIndex(boatIndex); if ((Object)(object)val == (Object)null) { worldPos = Vector3.zero; worldRot = Quaternion.identity; return false; } worldPos = val.TransformPoint(pos); worldRot = val.rotation * rot; return true; } worldPos = (CoordSpace.Ready ? CoordSpace.RealToLocal(pos) : pos); worldRot = rot; return true; } private GoPointer HostPointer() { if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } return _gp; } private static Vector3 RealItemVelocity(ShipItem item) { //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_0048: 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_002f: 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) try { ItemRigidbody val = (((Object)(object)item != (Object)null) ? item.GetItemRigidbody() : null); Rigidbody val2 = (((Object)(object)val != (Object)null) ? val.GetBody() : null); return ((Object)(object)val2 != (Object)null) ? val2.velocity : Vector3.zero; } catch { return Vector3.zero; } } private void SendExtraState(float dt) { float num = 1f / Mathf.Max(0.5f, ExtraStateHz); _extraTimer += dt; if (_extraTimer < num) { return; } _extraTimer = 0f; foreach (ItemEntry item in _items) { if (!((Object)(object)item.Item == (Object)null) && ExtraFields.TryGetValue(((object)item.Item).GetType().Name, out var value)) { float[] array = ReadExtra(item.Item, value); if (array != null) { _net.Broadcast(new ItemExtraStateMsg { InstanceId = item.InstanceId, PrefabIndex = item.PrefabIndex, Values = array }, (DeliveryMethod)4); } } } } public void OnItemExtraState(ItemExtraStateMsg msg, NetPeer fromPeer) { if (_net.Role == Role.Client && _byInstanceId.TryGetValue(msg.InstanceId, out var value) && !((Object)(object)value.Item == (Object)null) && value.HolderNetId != _net.MyNetId && ExtraFields.TryGetValue(((object)value.Item).GetType().Name, out var value2)) { WriteExtra(value.Item, value2, msg.Values); } } private static float[] ReadExtra(ShipItem item, string[] names) { try { float[] array = new float[names.Length]; for (int i = 0; i < names.Length; i++) { FieldInfo fieldDeep = GetFieldDeep(((object)item).GetType(), names[i]); if (fieldDeep == null) { array[i] = 0f; continue; } object value = fieldDeep.GetValue(item); array[i] = ToFloat(value, fieldDeep.FieldType); } return array; } catch { return null; } } private static void WriteExtra(ShipItem item, string[] names, float[] values) { try { int num = Mathf.Min(names.Length, values.Length); for (int i = 0; i < num; i++) { FieldInfo fieldDeep = GetFieldDeep(((object)item).GetType(), names[i]); if (!(fieldDeep == null)) { fieldDeep.SetValue(item, FromFloat(values[i], fieldDeep.FieldType)); } } } catch { } } private static float ToFloat(object v, Type t) { if (v == null) { return 0f; } if (t == typeof(bool)) { if (!(bool)v) { return 0f; } return 1f; } if (t.IsEnum) { return Convert.ToInt32(v); } if (t == typeof(int)) { return (int)v; } if (t == typeof(float)) { return (float)v; } try { return Convert.ToSingle(v); } catch { return 0f; } } private static object FromFloat(float f, Type t) { if (t == typeof(bool)) { return f > 0.5f; } if (t.IsEnum) { return Enum.ToObject(t, Mathf.RoundToInt(f)); } if (t == typeof(int)) { return Mathf.RoundToInt(f); } if (t == typeof(float)) { return f; } try { return Convert.ChangeType(f, t); } catch { return f; } } private static FieldInfo GetFieldDeep(Type type, string name) { string key = type.FullName + "::" + name; if (_fieldCache.TryGetValue(key, out var value)) { return value; } FieldInfo fieldInfo = null; Type type2 = type; while (type2 != null && fieldInfo == null) { fieldInfo = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); type2 = type2.BaseType; } _fieldCache[key] = fieldInfo; return fieldInfo; } private PickupableItem HeldItem() { try { if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } if ((Object)(object)_gp == (Object)null) { return null; } if (_fHeldItem == null) { _fHeldItem = typeof(GoPointer).GetField("heldItem", BindingFlags.Instance | BindingFlags.NonPublic); } return (PickupableItem)((_fHeldItem != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private void RefreshItems(float dt) { _refreshTimer += dt; if (!(_refreshTimer < 2f) || _items.Count <= 0) { _refreshTimer = 0f; RefreshItems(force: true); } } private void RefreshItems(bool force) { Dictionary dictionary = new Dictionary(_byInstanceId); Dictionary dictionary2 = new Dictionary(); foreach (KeyValuePair item in dictionary) { if ((Object)(object)item.Value.Item != (Object)null && HasStableIdentity(item.Value.Item)) { dictionary2[item.Key] = item.Value.Item; } } ShipItem[] array = Object.FindObjectsOfType(); foreach (ShipItem val in array) { if ((Object)(object)val != (Object)null && HasStableIdentity(val)) { dictionary2[InstanceIdOf(val)] = val; } } if (dictionary2.Count != _baselineCount) { _baselineCount = dictionary2.Count; _baselineChangedAt = Time.unscaledTime; } if (!_baselineReady && _baselineCount > 0 && Time.unscaledTime - _baselineChangedAt >= 4f) { _baselineReady = true; if (_net.Role == Role.Client) { SendReadyPing(); } } if (_net.Role == Role.Host && _baselineReady) { foreach (KeyValuePair item2 in dictionary) { if (!dictionary2.ContainsKey(item2.Key)) { BroadcastDespawn(item2.Value); } } } List list = new List(dictionary2.Values); list.Sort(CompareItems); _items.Clear(); _byItem.Clear(); _byInstanceId.Clear(); List list2 = new List(); for (int j = 0; j < list.Count && j <= 65535; j++) { ShipItem val2 = list[j]; int num = InstanceIdOf(val2); int prefabIndex = PrefabIndexOf(val2); dictionary.TryGetValue(num, out var value); bool num2 = value == null; if (value == null) { value = new ItemEntry { InstanceId = num, PrefabIndex = prefabIndex, NetId = NetIdFor(num) }; if (_localHeld.TryGetValue(val2, out var value2)) { value.HolderNetId = value2; } } value.Index = (ushort)j; value.InstanceId = num; value.PrefabIndex = prefabIndex; value.Item = val2; value.Net.InterpDelayMs = 90f; _items.Add(value); _byItem[val2] = value; _byInstanceId[num] = value; _net.Registry.RegisterFixed(value.NetId, NetObjKind.Item, 0u, val2); if (num2 && _net.Role == Role.Host && _baselineReady) { list2.Add(value); } } foreach (ItemEntry item3 in list2) { BroadcastSpawn(item3); } } private ItemEntry HostLookup(int instanceId, int prefabIndex) { if (instanceId <= 0 || prefabIndex <= 0) { return null; } if (_byInstanceId.TryGetValue(instanceId, out var value) && value.PrefabIndex == prefabIndex) { return value; } return null; } private static ShipItem FindLiveItem(int instanceId, int prefabIndex) { if (instanceId <= 0 || prefabIndex <= 0) { return null; } ShipItem[] array = Object.FindObjectsOfType(); foreach (ShipItem val in array) { if (!((Object)(object)val == (Object)null)) { SaveablePrefab component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && component.instanceId == instanceId && component.prefabIndex == prefabIndex) { return val; } } } return null; } private ItemEntry ResolveClient(int instanceId, int prefabIndex, CoordFrame frame, ushort boatIndex, Vector3 wirePos, float amount, float health, bool sold, bool nailed, bool allowSpawn) { //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_018d: 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 (instanceId <= 0 || prefabIndex <= 0) { return null; } _hostIds.Add(instanceId); if (_byInstanceId.TryGetValue(instanceId, out var value)) { if (value.PrefabIndex != prefabIndex) { Plugin.Logger.LogWarning((object)("[ItemSync] id=" + instanceId + " prefab mismatch local=" + value.PrefabIndex + ", wire=" + prefabIndex)); return null; } return value; } if (!_baselineReady) { return null; } ShipItem val = PopPendingClientItem(prefabIndex); if ((Object)(object)val != (Object)null) { RemapLocalItem(val, instanceId); RefreshItems(force: true); _byInstanceId.TryGetValue(instanceId, out var value2); if ((Object)(object)val == (Object)(object)_pendingHeldItem) { _pendingHeldItem = null; if (value2 != null && (Object)(object)value2.Item != (Object)null) { value2.HolderNetId = _net.MyNetId; _localHeld[value2.Item] = _net.MyNetId; SetPuppet(value2.Item, puppet: true); SendRequest(value2, ItemAction.Pickup, reliable: true); Remember("out belt->hand pickup id=" + instanceId); } } return value2; } ShipItem val2 = FindUnclaimedMatch(prefabIndex, frame, boatIndex, wirePos); if ((Object)(object)val2 != (Object)null) { RemapLocalItem(val2, instanceId); RefreshItems(force: true); if (!_byInstanceId.TryGetValue(instanceId, out var value3)) { return null; } return value3; } if (!allowSpawn) { return null; } if ((Object)(object)SpawnClientItem(instanceId, prefabIndex, frame, boatIndex, wirePos, Quaternion.identity, amount, health, sold, nailed) == (Object)null) { return null; } RefreshItems(force: true); if (!_byInstanceId.TryGetValue(instanceId, out var value4) || value4.PrefabIndex != prefabIndex) { return null; } return value4; } private ShipItem PopPendingClientItem(int prefabIndex) { for (int i = 0; i < _pendingClientItems.Count; i++) { ShipItem val = _pendingClientItems[i]; if ((Object)(object)val == (Object)null) { _pendingClientItems.RemoveAt(i--); } else if (PrefabIndexOf(val) == prefabIndex) { _pendingClientItems.RemoveAt(i); return val; } } return null; } private ShipItem FindUnclaimedMatch(int prefabIndex, CoordFrame frame, ushort boatIndex, Vector3 wirePos) { //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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_00b5: 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_00b9: 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) ShipItem result = null; float num = 0.25f; foreach (ItemEntry item in _items) { if ((Object)(object)item.Item == (Object)null || item.PrefabIndex != prefabIndex || _hostIds.Contains(item.InstanceId)) { continue; } Vector3 val2; if (frame == CoordFrame.Boat) { Transform val = BoatLocator.FindByIndex(boatIndex); if ((Object)(object)val == (Object)null) { continue; } val2 = val.InverseTransformPoint(((Component)item.Item).transform.position); } else { val2 = (CoordSpace.Ready ? CoordSpace.LocalToReal(((Component)item.Item).transform.position) : ((Component)item.Item).transform.position); } Vector3 val3 = val2 - wirePos; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = item.Item; } } return result; } private void RemapLocalItem(ShipItem local, int hostId) { SaveablePrefab val = (((Object)(object)local != (Object)null) ? ((Component)local).GetComponent() : null); if ((Object)(object)val == (Object)null) { return; } int instanceId = val.instanceId; if (instanceId == hostId) { return; } try { if (SaveablePrefab.existingInstanceIds != null) { SaveablePrefab.existingInstanceIds.Remove(instanceId); if (!SaveablePrefab.existingInstanceIds.Contains(hostId)) { SaveablePrefab.existingInstanceIds.Add(hostId); } } } catch { } RemoveCachedLocalItem(instanceId); val.instanceId = hostId; if (_byInstanceId.TryGetValue(instanceId, out var value)) { _byInstanceId.Remove(instanceId); if ((Object)(object)value.Item != (Object)null) { _byItem.Remove(value.Item); } _net.Registry.Remove(value.NetId); } Remember("remap id " + instanceId + "→" + hostId + " '" + local.name + "'"); } private void SendManifest() { if (_net.Role != Role.Host) { return; } int num = 0; foreach (ItemEntry item in _items) { if (!((Object)(object)item.Item == (Object)null) && ShouldReplicate(item.Item)) { BroadcastSpawn(item); num++; } } Plugin.Logger.LogInfo((object)("[ItemSync] Item manifest sent: " + num + " items")); } private void SendReadyPing() { if (_net.Role == Role.Client) { _net.Broadcast(new ItemRequestMsg { Action = ItemAction.Pose, InstanceId = 0, PrefabIndex = 0 }, (DeliveryMethod)2); Remember("out ready-ping"); } } private static bool ShouldReplicate(ShipItem item) { if ((Object)(object)item == (Object)null) { return false; } return HasStableIdentity(item); } private static bool HasStableIdentity(ShipItem item) { if ((Object)(object)item == (Object)null) { return false; } SaveablePrefab component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null && component.instanceId > 0 && component.prefabIndex > 0 && item.sold) { return !InPersonalInventory(item); } return false; } private static int CompareItems(ShipItem a, ShipItem b) { int num = InstanceIdOf(a); int num2 = InstanceIdOf(b); if (num != 0 && num2 != 0 && num != num2) { return num.CompareTo(num2); } if (num != num2) { return num2.CompareTo(num); } return string.CompareOrdinal(BoatLocator.PathOf(((Component)a).transform), BoatLocator.PathOf(((Component)b).transform)); } private static int InstanceIdOf(ShipItem item) { SaveablePrefab val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return 0; } return val.instanceId; } private static int CrateIdOf(ShipItem item) { SaveablePrefab val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return 0; } return val.currentCrateId; } private static int CargoPortOf(ShipItem item) { if ((Object)(object)item == (Object)null) { return -1; } try { int currentInventorySlot = item.GetCurrentInventorySlot(); return (currentInventorySlot >= 100) ? (currentInventorySlot - 100) : (-1); } catch { return -1; } } private static int PrefabIndexOf(ShipItem item) { SaveablePrefab val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return 0; } return val.prefabIndex; } private static string PoseLabel(CoordFrame frame, ushort boatIndex, Vector3 pos) { return "frame=" + frame.ToString() + " boat=" + boatIndex + " pos=" + ((Vector3)(ref pos)).ToString("F2"); } private ShipItem SpawnClientItem(int instanceId, int prefabIndex, CoordFrame frame, ushort boatIndex, Vector3 wirePos, Quaternion wireRot, float amount, float health, bool sold, bool nailed) { //IL_00d6: 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_00d2: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: 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_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_00e3: Unknown result type (might be due to invalid IL or missing references) try { if (!_baselineReady) { return null; } if (instanceId <= 0 || prefabIndex <= 0) { return null; } if (_byInstanceId.ContainsKey(instanceId)) { return _byInstanceId[instanceId].Item; } PrefabsDirectory instance = PrefabsDirectory.instance; if ((Object)(object)instance == (Object)null || instance.directory == null || prefabIndex <= 0 || prefabIndex >= instance.directory.Length) { return null; } GameObject val = instance.directory[prefabIndex]; if ((Object)(object)val == (Object)null) { return null; } Transform val2 = null; Vector3 val3; Quaternion val4; if (frame == CoordFrame.Boat) { val2 = BoatLocator.FindByIndex(boatIndex); if ((Object)(object)val2 == (Object)null) { return null; } val3 = val2.TransformPoint(wirePos); val4 = val2.rotation * wireRot; } else { val3 = (CoordSpace.Ready ? CoordSpace.RealToLocal(wirePos) : wirePos); val4 = wireRot; } GameObject val5 = Object.Instantiate(val, val3, val4); SaveablePrefab component = val5.GetComponent(); ShipItem component2 = val5.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { Object.Destroy((Object)(object)val5); return null; } component.instanceId = instanceId; component.prefabIndex = prefabIndex; component2.sold = sold; component2.nailed = nailed; component2.amount = amount; component2.health = health; if ((Object)(object)val2 != (Object)null) { ((Component)component2).transform.parent = val2; } RemoveCachedLocalItem(instanceId); Remember("spawn client id=" + instanceId + " prefab=" + prefabIndex); return component2; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Failed to create client item id=" + instanceId + ": " + ex.Message)); return null; } } private static void RemoveCachedLocalItem(int instanceId) { if (instanceId <= 0) { return; } try { if (_fBoatCachedItems == null) { _fBoatCachedItems = typeof(BoatLocalItems).GetField("cachedItems", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fBoatCachedItems == null) { return; } BoatLocalItems[] array = Object.FindObjectsOfType(); foreach (BoatLocalItems obj in array) { if (!(_fBoatCachedItems.GetValue(obj) is List list)) { continue; } for (int num = list.Count - 1; num >= 0; num--) { if (list[num] != null && list[num].instanceId == instanceId) { list.RemoveAt(num); } } if (list.Count == 0) { _fBoatCachedItems.SetValue(obj, null); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Failed to remove cached item id=" + instanceId + ": " + ex.Message)); } } private static uint NetIdFor(int instanceId) { return (uint)(0x40000000 | (instanceId & 0x3FFFFFFF)); } private static Transform ParentBoat(Transform t) { while ((Object)(object)t != (Object)null) { if ((Object)(object)((Component)t).GetComponent() != (Object)null && (Object)(object)t.parent != (Object)null) { return t.parent; } t = t.parent; } return null; } private void Remember(string text) { _last = text; _lastEventTick = _net.Clock.ServerTick; if (text != null && text.IndexOf("Pose", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("held ", StringComparison.OrdinalIgnoreCase) < 0) { Plugin.Logger.LogInfo((object)("[ItemSync] " + text)); } } public void NotifyClientAuthored(ShipItem item) { //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_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) if (_net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)item == (Object)null)) { if (!_pendingClientItems.Contains(item)) { _pendingClientItems.Add(item); } BuildPose(item, _net.Clock.ServerTick, out var frame, out var boatIndex, out var pos, out var rot, out var _); _net.Broadcast(new FishCatchMsg { PrefabIndex = PrefabIndexOf(item), Frame = frame, BoatIndex = boatIndex, Pos = pos, Rot = rot }, (DeliveryMethod)2); Remember("out client-authored prefab=" + PrefabIndexOf(item) + " '" + item.name + "'"); } } public void NotifySold(int instanceId, int prefabIndex) { if (_net.Role == Role.Client && _net.State == LinkState.Connected && instanceId > 0 && prefabIndex > 0) { _net.Broadcast(new ItemRequestMsg { Action = ItemAction.Consume, InstanceId = instanceId, PrefabIndex = prefabIndex }, (DeliveryMethod)2); Remember("out sold(despawn) id=" + instanceId); } } public void OnFishCatch(FishCatchMsg msg, NetPeer fromPeer) { //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_00e7: 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_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_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_00d7: 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_00f9: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Host) { return; } try { PrefabsDirectory instance = PrefabsDirectory.instance; if ((Object)(object)instance == (Object)null || instance.directory == null || msg.PrefabIndex <= 0 || msg.PrefabIndex >= instance.directory.Length) { Remember("reject fish prefab=" + msg.PrefabIndex); return; } GameObject val = instance.directory[msg.PrefabIndex]; if ((Object)(object)val == (Object)null) { return; } Vector3 val3; Quaternion val4; if (msg.Frame == CoordFrame.Boat) { Transform val2 = BoatLocator.FindByIndex(msg.BoatIndex); if ((Object)(object)val2 == (Object)null) { Remember("reject fish boat=" + msg.BoatIndex); return; } val3 = val2.TransformPoint(msg.Pos); val4 = val2.rotation * msg.Rot; } else { val3 = (CoordSpace.Ready ? CoordSpace.RealToLocal(msg.Pos) : msg.Pos); val4 = msg.Rot; } GameObject val5 = Object.Instantiate(val, val3, val4); ShipItem component = val5.GetComponent(); SaveablePrefab component2 = val5.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { Object.Destroy((Object)(object)val5); return; } component.sold = true; component2.prefabIndex = msg.PrefabIndex; component2.RegisterToSave(); RefreshItems(force: true); Remember("in fish catch prefab=" + msg.PrefabIndex + " id=" + component2.instanceId); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] OnFishCatch: " + ex.Message)); } } private void TickRods(float dt) { SendLocalRodState(dt); ApplyRemoteRods(); } private void SendLocalRodState(float dt) { //IL_00d4: 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_00e5: 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) _rodSendTimer += dt; if (_rodSendTimer < 0.1f) { return; } _rodSendTimer = 0f; if (!CoordSpace.Ready) { return; } try { foreach (ItemEntry item2 in _items) { ShipItem item = item2.Item; ShipItemFishingRod val = (ShipItemFishingRod)(object)((item is ShipItemFishingRod) ? item : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)((PickupableItem)val).held == (Object)null) && ((ShipItem)val).sold && item2.InstanceId > 0) { object? obj = RodBobberJointField?.GetValue(val); ConfigurableJoint val2 = (ConfigurableJoint)((obj is ConfigurableJoint) ? obj : null); if (!((Object)(object)val2 == (Object)null)) { CoopNet net = _net; RodStateMsg obj2 = new RodStateMsg { InstanceId = item2.InstanceId, PrefabIndex = item2.PrefabIndex, RealPos = CoordSpace.LocalToReal(((Component)val2).transform.position) }; SoftJointLimit linearLimit = val2.linearLimit; obj2.Limit = ((SoftJointLimit)(ref linearLimit)).limit; obj2.Bend = ((RodBendField != null) ? ((float)RodBendField.GetValue(val)) : 0f); net.Broadcast(obj2, (DeliveryMethod)4); } } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Fishing rod stream error: " + ex.Message)); } } public void OnRodState(RodStateMsg msg, NetPeer fromPeer) { //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) if (_net.Role == Role.Host) { _net.Broadcast(msg, (DeliveryMethod)4); } if (!_byInstanceId.TryGetValue(msg.InstanceId, out var value)) { return; } ShipItem item = value.Item; ShipItemFishingRod val = (ShipItemFishingRod)(object)((item is ShipItemFishingRod) ? item : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)((PickupableItem)val).held != (Object)null)) { if (!_rodRemote.TryGetValue(msg.InstanceId, out var value2)) { value2 = new RodRemote(); _rodRemote[msg.InstanceId] = value2; Remember("in rod-cast id=" + msg.InstanceId); } value2.RealPos = msg.RealPos; value2.Limit = msg.Limit; value2.Bend = msg.Bend; value2.LastTime = Time.unscaledTime; } } private void ApplyRemoteRods() { //IL_0165: 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_017c: 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_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: 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) //IL_01a1: 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_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) if (_rodRemote.Count == 0) { return; } _rodDone.Clear(); foreach (KeyValuePair item in _rodRemote) { RodRemote value = item.Value; ItemEntry value2; ShipItemFishingRod val = (ShipItemFishingRod)(_byInstanceId.TryGetValue(item.Key, out value2) ? /*isinst with value type is only supported in some contexts*/: null); ConfigurableJoint val2 = null; try { val2 = (ConfigurableJoint)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { } if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)((PickupableItem)val).held != (Object)null || Time.unscaledTime - value.LastTime > 1.5f) { try { Rigidbody val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent() : null); if (value.Kinematic && (Object)(object)val3 != (Object)null) { val3.isKinematic = false; val3.velocity = Vector3.zero; val3.angularVelocity = Vector3.zero; } } catch { } _rodDone.Add(item.Key); } else { if (!CoordSpace.Ready) { continue; } try { Rigidbody component = ((Component)val2).GetComponent(); if ((Object)(object)component != (Object)null && !component.isKinematic) { component.isKinematic = true; value.Kinematic = true; } Vector3 val4 = CoordSpace.RealToLocal(value.RealPos); Transform transform = ((Component)val2).transform; Vector3 val5 = val4 - transform.position; transform.position = ((((Vector3)(ref val5)).sqrMagnitude > 25f) ? val4 : Vector3.Lerp(transform.position, val4, Time.deltaTime * 12f)); RodTargetLengthField?.SetValue(val, value.Limit); RodBendField?.SetValue(val, value.Bend); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ItemSync] Bobber tracking error id=" + item.Key + ": " + ex.Message)); _rodDone.Add(item.Key); } } } foreach (int item2 in _rodDone) { _rodRemote.Remove(item2); } } public void Clear() { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) foreach (ItemEntry item in _items) { if (item != null && (Object)(object)item.Item != (Object)null) { RestoreDisconnectedItem(item.Item); } } foreach (KeyValuePair item2 in _rodRemote) { if (!item2.Value.Kinematic) { continue; } try { ItemEntry value; ShipItemFishingRod val = (ShipItemFishingRod)(_byInstanceId.TryGetValue(item2.Key, out value) ? /*isinst with value type is only supported in some contexts*/: null); ConfigurableJoint val2 = (ConfigurableJoint)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); Rigidbody val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent() : null); if ((Object)(object)val3 != (Object)null) { val3.isKinematic = false; val3.velocity = Vector3.zero; } } catch { } } _rodRemote.Clear(); _rodSendTimer = 0f; _items.Clear(); _byItem.Clear(); _byInstanceId.Clear(); _localHeld.Clear(); _pendingDynamic.Clear(); _suppressNextDrop.Clear(); _hostIds.Clear(); _localClaimed.Clear(); _pendingClientItems.Clear(); _pendingHeldItem = null; _gp = null; _fHeldItem = null; _refreshTimer = 0f; _sendTimer = 0f; _heldPoseTimer = 0f; _extraTimer = 0f; _altHeldTimer = 0f; _baselineReady = false; _baselineCount = -1; _baselineChangedAt = 0f; _last = "—"; _lastEventTick = 0L; } } public sealed class JoinPause { public float TimeoutSec = 120f; private readonly HashSet _pending = new HashSet(); private bool _paused; private float _prevTimeScale = 1f; private float _pausedAt; public bool Active => _paused; public int PendingCount => _pending.Count; public void Hold(uint netId) { _pending.Add(netId); if (!_paused) { _prevTimeScale = Time.timeScale; Time.timeScale = 0f; Physics.autoSyncTransforms = false; _pausedAt = Time.realtimeSinceStartup; _paused = true; Plugin.Logger.LogInfo((object)("[JoinPause] Host world paused: waiting for client load NetId=" + netId)); } } public void Release(uint netId) { if (_pending.Remove(netId) && _pending.Count == 0) { Unpause("client NetId=" + netId + " loaded"); } } public void Clear() { _pending.Clear(); if (_paused) { Unpause("session reset"); } } public void Tick() { if (_paused && Time.realtimeSinceStartup - _pausedAt > TimeoutSec) { Plugin.Logger.LogWarning((object)("[JoinPause] Client did not report loaded within " + TimeoutSec + " s - force releasing pause")); _pending.Clear(); Unpause("timeout"); } } private void Unpause(string why) { _paused = false; if (Time.timeScale == 0f) { Time.timeScale = ((_prevTimeScale > 0f) ? _prevTimeScale : 1f); } Physics.autoSyncTransforms = true; Plugin.Logger.LogInfo((object)("[JoinPause] Pause released (" + why + ")")); } } public sealed class LightSync { private readonly CoopNet _net; private readonly List _lights = new List(); private readonly Dictionary _index = new Dictionary(); private float _refreshTimer; private float _sendTimer; private string _last = "—"; private long _lastTick; private static FieldInfo _fOn; private static MethodInfo _miSetLight; public float SnapshotHz = 2f; public static LightSync Instance { get; private set; } public int LightCount => _lights.Count; public string LightText { get { string text = "—"; if (_lastTick != 0L) { long num = _net.Clock.ServerTick - _lastTick; if (num < 0) { num = 0L; } text = _last + " " + num + "ms"; } return _lights.Count + " pcs · " + text; } } public LightSync(CoopNet net) { _net = net; Instance = this; } public void Tick(float dt) { if (_net.State != LinkState.Connected) { return; } RefreshLights(dt); if (_net.Role != Role.Host) { return; } float num = 1f / Mathf.Max(0.5f, SnapshotHz); _sendTimer += dt; if (_sendTimer < num) { return; } _sendTimer = 0f; for (int i = 0; i < _lights.Count; i++) { ShipItemLight val = _lights[i]; if (!((Object)(object)val == (Object)null)) { _net.Broadcast(new LightStateMsg { Index = (ushort)i, On = IsOn(val), Health = ((ShipItem)val).health }, (DeliveryMethod)4); } } } public void NotifyLocalLightChanged(ShipItemLight light) { if (_net.Role == Role.Client && _net.State == LinkState.Connected && !((Object)(object)light == (Object)null)) { RefreshLights(force: true); if (_index.TryGetValue(light, out var value)) { LightRequestMsg lightRequestMsg = new LightRequestMsg { Index = value, On = IsOn(light), Health = ((ShipItem)light).health }; _net.Broadcast(lightRequestMsg, (DeliveryMethod)2); Remember("out #" + value + " " + (lightRequestMsg.On ? "on" : "off")); } } } public void OnLightRequest(LightRequestMsg msg, NetPeer fromPeer) { if (_net.Role == Role.Host) { RefreshLights(force: true); ShipItemLight light = GetLight(msg.Index); if (!((Object)(object)light == (Object)null)) { Apply(light, msg.On, msg.Health); Remember("in #" + msg.Index + " " + (msg.On ? "on" : "off")); _net.Broadcast(new LightStateMsg { Index = msg.Index, On = IsOn(light), Health = ((ShipItem)light).health }, (DeliveryMethod)2); } } } public void OnLightState(LightStateMsg msg, NetPeer fromPeer) { if (_net.Role == Role.Client) { RefreshLights(force: true); ShipItemLight light = GetLight(msg.Index); if (!((Object)(object)light == (Object)null)) { Apply(light, msg.On, msg.Health); Remember("in #" + msg.Index + " " + (msg.On ? "on" : "off")); } } } private void Apply(ShipItemLight light, bool on, float health) { if ((Object)(object)light == (Object)null) { return; } ((ShipItem)light).health = Mathf.Max(0f, health); try { if (_miSetLight == null) { _miSetLight = typeof(ShipItemLight).GetMethod("SetLight", BindingFlags.Instance | BindingFlags.NonPublic); } if (_miSetLight != null) { _miSetLight.Invoke(light, new object[1] { on }); } else if (_fOn != null) { _fOn.SetValue(light, on); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[LightSync] SetLight failed: " + ex.Message)); } } private static bool IsOn(ShipItemLight light) { try { if (_fOn == null) { _fOn = typeof(ShipItemLight).GetField("on", BindingFlags.Instance | BindingFlags.NonPublic); } return _fOn != null && (bool)_fOn.GetValue(light); } catch { return (Object)(object)light != (Object)null && ((ShipItem)light).amount >= 1f; } } private ShipItemLight GetLight(ushort index) { if (index >= _lights.Count) { return null; } return _lights[index]; } private void RefreshLights(float dt) { _refreshTimer += dt; if (!(_refreshTimer < 2f) || _lights.Count <= 0) { _refreshTimer = 0f; RefreshLights(force: true); } } private void RefreshLights(bool force) { _lights.Clear(); _index.Clear(); ShipItemLight[] array = Object.FindObjectsOfType(); foreach (ShipItemLight val in array) { if ((Object)(object)val != (Object)null) { _lights.Add(val); } } _lights.Sort((ShipItemLight a, ShipItemLight b) => string.CompareOrdinal(BoatLocator.PathOf(((Component)a).transform), BoatLocator.PathOf(((Component)b).transform))); for (int num = 0; num < _lights.Count && num <= 65535; num++) { if ((Object)(object)_lights[num] != (Object)null) { _index[_lights[num]] = (ushort)num; } } } private void Remember(string text) { _last = text; _lastTick = _net.Clock.ServerTick; } public void Clear() { _lights.Clear(); _index.Clear(); _refreshTimer = 0f; _sendTimer = 0f; _last = "—"; _lastTick = 0L; } } public static class LightPatches { public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, "OnAltActivate", Type.EmptyTypes, "PostLightChanged"); bool flag2 = TryPatch(harmony, "OnItemClick", new Type[1] { typeof(PickupableItem) }, "PostLightChanged"); Plugin.Logger.LogInfo((object)("[LightPatches] Light patches: Alt=" + flag + ", ItemClick=" + flag2)); PatchHealth.Report("Lights", (flag ? 1 : 0) + (flag2 ? 1 : 0), 2); } private static bool TryPatch(Harmony harmony, string method, Type[] args, string postfixName) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown try { MethodInfo method2 = typeof(ShipItemLight).GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method2 == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(LightPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[LightPatches] " + method + ": " + ex.Message)); return false; } } private static void PostLightChanged(ShipItemLight __instance) { try { LightSync.Instance?.NotifyLocalLightChanged(__instance); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[LightPatches] PostLightChanged: " + ex.Message)); } } } public sealed class MissionSync { private const int Slots = 5; private readonly CoopNet _net; private string _lastSig; private string _lastApplied; private float _heartbeat; public float HeartbeatSeconds = 4f; public static MissionSync Instance { get; private set; } public string MissionText { get; private set; } = "—"; public MissionSync(CoopNet net) { _net = net; Instance = this; } public void Tick(float dt) { if (_net.Role == Role.Host && _net.State == LinkState.Connected) { _heartbeat += dt; MissionEntry[] entries; string text = BuildSignature(out entries); bool flag = _heartbeat >= HeartbeatSeconds; if (!(text == _lastSig) || flag) { _heartbeat = 0f; _lastSig = text; _net.Broadcast(new MissionJournalMsg { Missions = entries }, (DeliveryMethod)2); MissionText = entries.Length + " missions (host)"; } } } private string BuildSignature(out MissionEntry[] entries) { List list = new List(5); StringBuilder stringBuilder = new StringBuilder(); Mission[] missions = PlayerMissions.missions; if (missions != null) { for (int i = 0; i < missions.Length && i < 5; i++) { Mission val = missions[i]; if (val != null) { SaveMissionData val2; try { val2 = val.PrepareSaveData(); } catch { continue; } list.Add(new MissionEntry { MissionIndex = (byte)val2.missionIndex, OriginPort = val2.originPort, DestinationPort = val2.destinationPort, GoodPrefabIndex = val2.goodPrefabIndex, GoodCount = val2.goodCount, TotalPrice = val2.totalPrice, InsuranceLevel = val2.insuranceLevel, Distance = val2.distance, DeliveredGoods = val2.deliveredGoods, DueDay = val2.dueDay }); stringBuilder.Append(val2.missionIndex).Append(':').Append(val2.deliveredGoods) .Append(':') .Append(val2.dueDay) .Append('|'); } } } entries = list.ToArray(); return stringBuilder.ToString(); } public void OnMissionJournal(MissionJournalMsg msg, NetPeer fromPeer) { if (_net.Role == Role.Client) { StringBuilder stringBuilder = new StringBuilder(); MissionEntry[] missions = msg.Missions; for (int i = 0; i < missions.Length; i++) { MissionEntry missionEntry = missions[i]; stringBuilder.Append(missionEntry.MissionIndex).Append(':').Append(missionEntry.DeliveredGoods) .Append(':') .Append(missionEntry.DueDay) .Append('|'); } string text = stringBuilder.ToString(); if (!(text == _lastApplied)) { _lastApplied = text; ApplyJournal(msg.Missions); MissionText = msg.Missions.Length + " missions (mirror)"; } } } private void ApplyJournal(MissionEntry[] entries) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown try { if (PlayerMissions.missions == null || PlayerMissions.missions.Length < 5) { PlayerMissions.missions = (Mission[])(object)new Mission[5]; } Mission[] missions = PlayerMissions.missions; for (int i = 0; i < missions.Length; i++) { missions[i] = null; } for (int j = 0; j < entries.Length; j++) { MissionEntry missionEntry = entries[j]; if (missionEntry.MissionIndex < missions.Length) { try { SaveMissionData val = new SaveMissionData((int)missionEntry.MissionIndex, missionEntry.OriginPort, missionEntry.DestinationPort, missionEntry.GoodPrefabIndex, missionEntry.GoodCount, missionEntry.TotalPrice, missionEntry.InsuranceLevel, missionEntry.Distance, missionEntry.DeliveredGoods, missionEntry.DueDay); missions[missionEntry.MissionIndex] = new Mission(val); } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; byte missionIndex = missionEntry.MissionIndex; logger.LogWarning((object)("[MissionSync] mission " + missionIndex + ": " + ex.Message)); } } } Plugin.Logger.LogInfo((object)("[MissionSync] journal mirrored: " + entries.Length + " missions")); } catch (Exception ex2) { Plugin.Logger.LogWarning((object)("[MissionSync] ApplyJournal: " + ex2.Message)); } } public bool RequestAccept(Mission mission) { if (_net.Role != Role.Client || _net.State != LinkState.Connected || mission == null) { return false; } SaveMissionData val; try { val = mission.PrepareSaveData(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionSync] PrepareSaveData: " + ex.Message)); return false; } _net.Broadcast(new MissionAcceptMsg { Mission = new MissionEntry { OriginPort = val.originPort, DestinationPort = val.destinationPort, GoodPrefabIndex = val.goodPrefabIndex, GoodCount = val.goodCount, TotalPrice = val.totalPrice, InsuranceLevel = val.insuranceLevel, Distance = val.distance, DueDay = val.dueDay } }, (DeliveryMethod)2); Plugin.Logger.LogInfo((object)("[MissionSync] out accept dest=" + val.destinationPort + " good=" + val.goodPrefabIndex)); return true; } public bool RequestAbandon(int missionIndex) { if (_net.Role != Role.Client || _net.State != LinkState.Connected) { return false; } if (missionIndex < 0 || missionIndex >= 5) { return false; } _net.Broadcast(new MissionAbandonMsg { MissionIndex = (byte)missionIndex }, (DeliveryMethod)2); Plugin.Logger.LogInfo((object)("[MissionSync] out abandon slot=" + missionIndex)); return true; } public void OnMissionAccept(MissionAcceptMsg msg, NetPeer fromPeer) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown if (_net.Role != Role.Host) { return; } try { MissionEntry mission = msg.Mission; PlayerMissions.AcceptMission(new Mission(new SaveMissionData(-1, mission.OriginPort, mission.DestinationPort, mission.GoodPrefabIndex, mission.GoodCount, mission.TotalPrice, mission.InsuranceLevel, mission.Distance, 0, mission.DueDay))); _heartbeat = HeartbeatSeconds; Plugin.Logger.LogInfo((object)("[MissionSync] in accept dest=" + mission.DestinationPort + " good=" + mission.GoodPrefabIndex)); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionSync] OnMissionAccept: " + ex.Message)); } } public void OnMissionAbandon(MissionAbandonMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Host) { return; } try { int missionIndex = msg.MissionIndex; if (PlayerMissions.missions != null && missionIndex >= 0 && missionIndex < PlayerMissions.missions.Length && PlayerMissions.missions[missionIndex] != null) { PlayerMissions.AbandonMission(missionIndex); _heartbeat = HeartbeatSeconds; Plugin.Logger.LogInfo((object)("[MissionSync] in abandon slot=" + missionIndex)); } else { Plugin.Logger.LogInfo((object)("[MissionSync] in abandon: empty slot " + missionIndex)); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionSync] OnMissionAbandon: " + ex.Message)); } } public void ForwardReward(MissionRewardMsg reward) { if (_net.Role == Role.Host && _net.State == LinkState.Connected && reward != null && reward.Amount > 0 && reward.Region >= 0) { _net.Broadcast(reward, (DeliveryMethod)2); Plugin.Logger.LogInfo((object)("[MissionSync] out reward region=" + reward.Region + " amount=" + reward.Amount + " rep=" + reward.RepAmount)); } } public void OnMissionReward(MissionRewardMsg msg, NetPeer fromPeer) { if (_net.Role != Role.Client) { return; } try { if (PlayerGold.currency == null || msg.Region < 0 || msg.Region >= PlayerGold.currency.Length || msg.Amount <= 0) { return; } PlayerGold.currency[msg.Region] += msg.Amount; if (msg.RepAmount != 0) { if (msg.OriginRegion >= 0) { PlayerReputation.ChangeReputation(msg.RepAmount, (PortRegion)msg.OriginRegion); } if (msg.DestinationRegion >= 0 && msg.DestinationRegion != msg.OriginRegion) { PlayerReputation.ChangeReputation(msg.RepAmount, (PortRegion)msg.DestinationRegion); } try { ReputationNotifUI instance = ReputationNotifUI.instance; if (instance != null) { instance.ShowNotif(); } } catch { } } if (msg.GoodPrefabIndex > 0) { try { MissionLog instance2 = MissionLog.instance; if (instance2 != null) { instance2.AddToLog(msg.GoodPrefabIndex, msg.DestinationName ?? "", msg.Amount, msg.RepAmount, msg.Region); } } catch { } } try { if ((Object)(object)DayLogs.instance != (Object)null && DayLogs.instance.dayLogs != null && msg.Region >= 0 && msg.Region < DayLogs.instance.dayLogs.Length && DayLogs.instance.dayLogs[msg.Region] != null) { DayLogs.instance.dayLogs[msg.Region].LogMissionDelivery(msg.ExpectedReward, msg.Amount); } } catch { } try { MoneyNotification instance3 = MoneyNotification.instance; if (instance3 != null) { instance3.PlayNotif(msg.Amount, msg.Region); } } catch { } Plugin.Logger.LogInfo((object)("[MissionSync] in reward +" + msg.Amount + " region=" + msg.Region)); SaveGuestProfileNow("mission reward"); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionSync] OnMissionReward: " + ex.Message)); } } public void SaveGuestProfileNow(string reason) { try { if (_net.Role == Role.Client && _net.State == LinkState.Connected) { CoopProfile.SaveFromGame(); Plugin.Logger.LogInfo((object)("[MissionSync] client profile saved: " + reason)); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionSync] SaveGuestProfileNow: " + ex.Message)); } } public void Clear() { _lastSig = null; _lastApplied = null; _heartbeat = 0f; MissionText = "—"; } } public static class MissionPatches { private sealed class DeliveryState { public int Region; public int CurrencyBefore; public int OriginRegion; public int DestinationRegion; public int OriginRepBefore; public int DestinationRepBefore; public int GoodPrefabIndex; public string DestinationName; public int ExpectedReward; } public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, "DeliverGood", "PreDeliver", "PostDeliver"); bool flag2 = TryPatchStatic(harmony, "AcceptMission", new Type[1] { AccessTools.TypeByName("Mission") }, "PreAcceptMission"); bool flag3 = TryPatchStatic(harmony, "AbandonMission", new Type[1] { typeof(int) }, "PreAbandonMission"); bool flag4 = TryPatchEconomy(harmony, "BuyGood", "PostEconomyChanged"); bool flag5 = TryPatchEconomy(harmony, "SellGood", "PostEconomyChanged"); bool flag6 = TryPatchEconomy(harmony, "PrintReceipt", "PostEconomyChanged"); Plugin.Logger.LogInfo((object)("[MissionPatches] Mission patch: DeliverGood=" + flag + ", Accept=" + flag2 + ", Abandon=" + flag3 + ", BuyGood=" + flag4 + ", SellGood=" + flag5 + ", PrintReceipt=" + flag6)); PatchHealth.Report("Missions", (flag ? 1 : 0) + (flag2 ? 1 : 0) + (flag3 ? 1 : 0) + (flag4 ? 1 : 0) + (flag5 ? 1 : 0) + (flag6 ? 1 : 0), 6); } private static bool TryPatch(Harmony harmony, string method, string prefixName, string postfixName) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("Mission"); if (type == null) { return false; } MethodInfo method2 = type.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method2 == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(MissionPatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val2 = new HarmonyMethod(typeof(MissionPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionPatches] " + method + ": " + ex.Message)); return false; } } private static bool TryPatchStatic(Harmony harmony, string method, Type[] args, string prefixName) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("PlayerMissions"); if (type == null || args == null || args[0] == null) { return false; } MethodInfo method2 = type.GetMethod(method, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method2 == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(MissionPatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionPatches] " + method + ": " + ex.Message)); return false; } } private static bool TryPatchEconomy(Harmony harmony, string method, string postfixName) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown try { MethodInfo method2 = typeof(EconomyUI).GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method2 == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(MissionPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionPatches] EconomyUI." + method + ": " + ex.Message)); return false; } } private static bool PreAcceptMission(Mission mission) { try { MissionSync instance = MissionSync.Instance; if (instance != null && instance.RequestAccept(mission)) { return false; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionPatches] PreAcceptMission: " + ex.Message)); } return true; } private static bool PreAbandonMission(int missionIndex) { try { MissionSync instance = MissionSync.Instance; if (instance != null && instance.RequestAbandon(missionIndex)) { return false; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionPatches] PreAbandonMission: " + ex.Message)); } return true; } private static void PreDeliver(Mission __instance, out DeliveryState __state) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected I4, but got Unknown //IL_005d: 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) __state = null; try { if (__instance != null && !((Object)(object)__instance.destinationPort == (Object)null)) { int num = (int)__instance.destinationPort.region; if (PlayerGold.currency != null && num >= 0 && num < PlayerGold.currency.Length) { SaveMissionData val = __instance.PrepareSaveData(); int num2 = ((!((Object)(object)__instance.originPort != (Object)null)) ? (-1) : ((int)__instance.originPort.region)); int num3 = ((!((Object)(object)__instance.destinationPort != (Object)null)) ? (-1) : ((int)__instance.destinationPort.region)); __state = new DeliveryState { Region = num, CurrencyBefore = PlayerGold.currency[num], OriginRegion = num2, DestinationRegion = num3, OriginRepBefore = ((num2 >= 0) ? PlayerReputation.GetRep(num2) : 0), DestinationRepBefore = ((num3 >= 0) ? PlayerReputation.GetRep(num3) : 0), GoodPrefabIndex = val.goodPrefabIndex, DestinationName = __instance.destinationPort.GetPortName(), ExpectedReward = ((val.goodCount > 0) ? (val.totalPrice / val.goodCount) : 0) }; } } } catch { __state = null; } } private static void PostDeliver(DeliveryState __state) { try { if (__state == null) { return; } int num = PlayerGold.currency[__state.Region] - __state.CurrencyBefore; if (num > 0) { int num2 = 0; if (__state.OriginRegion >= 0) { num2 = Math.Max(num2, PlayerReputation.GetRep(__state.OriginRegion) - __state.OriginRepBefore); } if (__state.DestinationRegion >= 0) { num2 = Math.Max(num2, PlayerReputation.GetRep(__state.DestinationRegion) - __state.DestinationRepBefore); } MissionSync.Instance?.ForwardReward(new MissionRewardMsg { Region = __state.Region, Amount = num, OriginRegion = __state.OriginRegion, DestinationRegion = __state.DestinationRegion, RepAmount = num2, GoodPrefabIndex = __state.GoodPrefabIndex, DestinationName = __state.DestinationName, ExpectedReward = __state.ExpectedReward }); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionPatches] PostDeliver: " + ex.Message)); } } private static void PostEconomyChanged() { try { MissionSync.Instance?.SaveGuestProfileNow("economy"); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MissionPatches] PostEconomyChanged: " + ex.Message)); } } } public sealed class MooringSync { private readonly CoopNet _net; private PlayerEmbarkerNew _emb; private Transform _cachedBoat; private BoatMooringRopes _bm; private readonly Dictionary _ropeIndex = new Dictionary(); private static bool _applying; private float _suppressLocalUntil; private string _lastAction = "—"; private long _lastActionTick; private static FieldInfo _fSpring; public static MooringSync Instance { get; private set; } public string MooringText { get { PickupableBoatMooringRope[] array = (((Object)(object)_bm != (Object)null) ? _bm.ropes : null); if (array == null || array.Length == 0) { return "no moorings"; } int num = 0; for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null && array[i].IsMoored()) { num++; } } string text = "—"; if (_lastActionTick != 0L) { long num2 = _net.Clock.ServerTick - _lastActionTick; if (num2 < 0) { num2 = 0L; } text = _lastAction + " " + num2 + "ms"; } return num + "/" + array.Length + " moored · " + text; } } public MooringSync(CoopNet net) { _net = net; Instance = this; } public void Tick(float dt) { if ((Object)(object)_emb == (Object)null) { _emb = Object.FindObjectOfType(); } Transform val = (((Object)(object)_emb != (Object)null) ? _emb.debugOutCurrentBoat : null); if ((Object)(object)val == (Object)null) { val = BoatLocator.FindByIndex(0); } if (!((Object)(object)val == (Object)(object)_cachedBoat) || !((Object)(object)_bm != (Object)null) || _ropeIndex.Count <= 0) { bool flag = (Object)(object)val != (Object)(object)_cachedBoat; _cachedBoat = val; if (_net.Role == Role.Client && flag) { _suppressLocalUntil = Time.time + 3f; } _bm = (((Object)(object)val != (Object)null) ? (((Component)val).GetComponentInChildren(true) ?? ((Component)val).GetComponentInParent()) : null); RebuildIndex(); } } private void RebuildIndex() { _ropeIndex.Clear(); PickupableBoatMooringRope[] array = (((Object)(object)_bm != (Object)null) ? _bm.ropes : null); if (array == null) { return; } for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null) { _ropeIndex[array[i]] = i; } } } public void NotifyLocalUnmoor(PickupableBoatMooringRope rope) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) NotifyLocal(rope, MooringKind.Unmoor, Vector3.zero, 0f); } public void NotifyLocalMoor(PickupableBoatMooringRope rope, GPButtonDockMooring dock) { //IL_0010: 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_002b: 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_0022: Unknown result type (might be due to invalid IL or missing references) Vector3 dockReal = (((Object)(object)dock != (Object)null && CoordSpace.Ready) ? CoordSpace.LocalToReal(((Component)dock).transform.position) : Vector3.zero); NotifyLocal(rope, MooringKind.Moor, dockReal, 0f); } public void NotifyLocalLength(PickupableBoatMooringRope rope) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rope == (Object)null)) { NotifyLocal(rope, MooringKind.Length, Vector3.zero, rope.currentRopeLengthSquared); } } private void NotifyLocal(PickupableBoatMooringRope rope, MooringKind kind, Vector3 dockReal, float lengthSq) { //IL_00da: 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_0119: 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) try { if (_applying || _net.State != LinkState.Connected || (_net.Role == Role.Client && (GameState.currentlyLoading || GameState.justStarted || Time.time < _suppressLocalUntil))) { return; } int num = IndexOf(rope); if (num < 0) { Plugin.Logger.LogWarning((object)("[MooringSync] Local " + kind.ToString() + ": rope index not found (rope count in map=" + _ropeIndex.Count + ")")); return; } if (_net.Role == Role.Host) { _net.Broadcast(new MooringStateMsg { Index = (ushort)num, Kind = kind, DockReal = dockReal, LengthSq = lengthSq }, (DeliveryMethod)2); } else if (_net.Role == Role.Client) { _net.Broadcast(new MooringRequestMsg { Index = (ushort)num, Kind = kind, DockReal = dockReal, LengthSq = lengthSq }, (DeliveryMethod)2); } Remember("out " + kind.ToString() + " #" + num); Plugin.Logger.LogInfo((object)("[MooringSync] Local " + kind.ToString() + " rope #" + num + " (role " + _net.Role.ToString() + ") -> sent")); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MooringSync] NotifyLocal " + kind.ToString() + ": " + ex)); } } public void OnMooringState(MooringStateMsg msg, NetPeer fromPeer) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (_net.Role == Role.Client) { Plugin.Logger.LogInfo((object)("[MooringSync] Client received from host " + msg.Kind.ToString() + " rope #" + msg.Index)); Apply(msg.Index, msg.Kind, msg.DockReal, msg.LengthSq, "in"); } } public void OnMooringRequest(MooringRequestMsg msg, NetPeer fromPeer) { //IL_0052: 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_0091: Unknown result type (might be due to invalid IL or missing references) if (_net.Role == Role.Host) { Plugin.Logger.LogInfo((object)("[MooringSync] Host received request " + msg.Kind.ToString() + " rope #" + msg.Index)); Apply(msg.Index, msg.Kind, msg.DockReal, msg.LengthSq, "in request"); _net.RelayExcept(new MooringStateMsg { Index = msg.Index, Kind = msg.Kind, DockReal = msg.DockReal, LengthSq = msg.LengthSq }, fromPeer, (DeliveryMethod)2); } } private void Apply(ushort index, MooringKind kind, Vector3 dockReal, float lengthSq, string tag) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) PickupableBoatMooringRope[] array = (((Object)(object)_bm != (Object)null) ? _bm.ropes : null); if (array == null || index >= array.Length) { RefetchBoat(); array = (((Object)(object)_bm != (Object)null) ? _bm.ropes : null); } if (array == null || index >= array.Length) { return; } PickupableBoatMooringRope val = array[index]; if ((Object)(object)val == (Object)null) { return; } _applying = true; try { switch (kind) { case MooringKind.Unmoor: if (val.IsMoored()) { val.Unmoor(); val.ResetRopePos(); } break; case MooringKind.Moor: if (!val.IsMoored()) { GPButtonDockMooring val2 = FindDockNear(dockReal); if ((Object)(object)val2 != (Object)null) { val.MoorTo(val2); } else { Plugin.Logger.LogWarning((object)("[MooringSync] Nearby dock not found for rope #" + index)); } } break; default: if (val.IsMoored()) { val.currentRopeLengthSquared = lengthSq; SpringJoint mooredSpring = GetMooredSpring(val); if ((Object)(object)mooredSpring != (Object)null) { mooredSpring.maxDistance = Mathf.Sqrt(Mathf.Max(0f, lengthSq)); } } break; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MooringSync] Apply " + kind.ToString() + " #" + index + ": " + ex.Message)); } finally { _applying = false; } Remember(tag + " " + kind.ToString() + " #" + index); Plugin.Logger.LogInfo((object)("[MooringSync] Applied " + kind.ToString() + " rope #" + index)); } private static SpringJoint GetMooredSpring(PickupableBoatMooringRope rope) { try { if (_fSpring == null) { _fSpring = typeof(PickupableBoatMooringRope).GetField("mooredToSpring", BindingFlags.Instance | BindingFlags.NonPublic); } return (SpringJoint)((_fSpring != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private int IndexOf(PickupableBoatMooringRope rope) { if ((Object)(object)rope == (Object)null) { return -1; } if (_ropeIndex.Count == 0) { RebuildIndex(); } if (_ropeIndex.TryGetValue(rope, out var value)) { return value; } BoatMooringRopes componentInParent = ((Component)rope).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && componentInParent.ropes != null) { return Array.IndexOf(componentInParent.ropes, rope); } return -1; } private void RefetchBoat() { Transform val = (((Object)(object)_emb != (Object)null) ? _emb.debugOutCurrentBoat : null); if ((Object)(object)val == (Object)null) { val = BoatLocator.FindByIndex(0); } if ((Object)(object)val != (Object)null) { _bm = ((Component)val).GetComponentInChildren(true) ?? ((Component)val).GetComponentInParent(); RebuildIndex(); } } private static GPButtonDockMooring FindDockNear(Vector3 real) { //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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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 (!CoordSpace.Ready) { return null; } Vector3 val = CoordSpace.RealToLocal(real); GPButtonDockMooring result = null; float num = 9f; GPButtonDockMooring[] array = Object.FindObjectsOfType(); foreach (GPButtonDockMooring val2 in array) { Vector3 val3 = ((Component)val2).transform.position - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = val2; } } return result; } private void Remember(string text) { _lastAction = text; _lastActionTick = _net.Clock.ServerTick; } public void Clear() { _cachedBoat = null; _bm = null; _ropeIndex.Clear(); _applying = false; _lastAction = "—"; _lastActionTick = 0L; _suppressLocalUntil = 0f; } } public static class MooringPatches { public static void Apply(Harmony harmony) { Type typeFromHandle = typeof(PickupableBoatMooringRope); bool flag = TryPatch(harmony, typeFromHandle, "Unmoor", Type.EmptyTypes, "PostUnmoor"); bool flag2 = TryPatch(harmony, typeFromHandle, "MoorTo", new Type[1] { typeof(GPButtonDockMooring) }, "PostMoorTo"); bool flag3 = TryPatch(harmony, typeFromHandle, "ChangeRopeLength", new Type[1] { typeof(float) }, "PostChangeLength"); Plugin.Logger.LogInfo((object)("[MooringPatches] Mooring patches: Unmoor=" + flag + ", MoorTo=" + flag2 + ", ChangeRopeLength=" + flag3)); PatchHealth.Report("Mooring", (flag ? 1 : 0) + (flag2 ? 1 : 0) + (flag3 ? 1 : 0), 3); } private static bool TryPatch(Harmony harmony, Type t, string method, Type[] args, string postfixName) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown try { MethodInfo method2 = t.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method2 == null) { Plugin.Logger.LogWarning((object)("[MooringPatches] Not found " + method)); return false; } HarmonyMethod val = new HarmonyMethod(typeof(MooringPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MooringPatches] " + method + ": " + ex.Message)); return false; } } private static void PostUnmoor(PickupableBoatMooringRope __instance) { try { MooringSync.Instance?.NotifyLocalUnmoor(__instance); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MooringPatches] PostUnmoor: " + ex.Message)); } } private static void PostMoorTo(PickupableBoatMooringRope __instance, GPButtonDockMooring mooring) { try { MooringSync.Instance?.NotifyLocalMoor(__instance, mooring); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MooringPatches] PostMoorTo: " + ex.Message)); } } private static void PostChangeLength(PickupableBoatMooringRope __instance, bool __result) { if (!__result) { return; } try { MooringSync.Instance?.NotifyLocalLength(__instance); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[MooringPatches] PostChangeLength: " + ex.Message)); } } } public sealed class NetTransform { private struct Snap { public long Tick; public Vector3 RealPos; public Quaternion Rot; public Vector3 RealVel; } private readonly List _buf = new List(32); private const int MaxBuffer = 32; private const float MaxExtrapolateMs = 250f; public float InterpDelayMs = 100f; public Func ToWorldPos = CoordSpace.RealToLocal; public Func ToWorldRot = (Quaternion q) => q; public bool HasData => _buf.Count > 0; public void Push(long tick, Vector3 realPos, Quaternion rot, Vector3 realVel = default(Vector3)) { //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_001a: 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_0022: 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) Snap snap = new Snap { Tick = tick, RealPos = realPos, Rot = rot, RealVel = realVel }; if (_buf.Count > 0 && tick <= _buf[_buf.Count - 1].Tick) { if (tick <= _buf[0].Tick) { return; } int num = _buf.Count - 1; while (num >= 0 && _buf[num].Tick > tick) { num--; } if (num >= 0 && _buf[num].Tick == tick) { _buf[num] = snap; return; } _buf.Insert(num + 1, snap); } else { _buf.Add(snap); } while (_buf.Count > 32) { _buf.RemoveAt(0); } } public void Apply(Transform t, long serverTickNow) { //IL_0043: 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_009e: 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_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_012c: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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) //IL_015a: 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) if ((Object)(object)t == (Object)null || _buf.Count == 0) { return; } long num = serverTickNow - (long)InterpDelayMs; if (num <= _buf[0].Tick) { Set(t, _buf[0].RealPos, _buf[0].Rot); return; } Snap snap = _buf[_buf.Count - 1]; if (num >= snap.Tick) { float num2 = Mathf.Min((float)(num - snap.Tick), 250f) / 1000f; Set(t, snap.RealPos + snap.RealVel * num2, snap.Rot); return; } for (int i = 0; i < _buf.Count - 1; i++) { Snap snap2 = _buf[i]; Snap snap3 = _buf[i + 1]; if (num >= snap2.Tick && num <= snap3.Tick) { float num3 = snap3.Tick - snap2.Tick; float num4 = ((num3 > 0f) ? ((float)(num - snap2.Tick) / num3) : 0f); Vector3 storedPos = Vector3.Lerp(snap2.RealPos, snap3.RealPos, num4); Quaternion storedRot = Quaternion.Slerp(snap2.Rot, snap3.Rot, num4); Set(t, storedPos, storedRot); break; } } } private void Set(Transform t, Vector3 storedPos, Quaternion storedRot) { //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_0009: 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_0026: Unknown result type (might be due to invalid IL or missing references) t.position = ((ToWorldPos != null) ? ToWorldPos(storedPos) : storedPos); t.rotation = ((ToWorldRot != null) ? ToWorldRot(storedRot) : storedRot); } public void Clear() { _buf.Clear(); } } public sealed class PlayerSync { private sealed class RemoteAvatar { public GameObject Go; public Transform Body; public Transform Head; public Animator Animator; public AvatarPoseDriver PoseDriver; public readonly NetTransform Net = new NetTransform(); public Quaternion HeadWorldRot = Quaternion.identity; public bool HeadDrivenByAnimator; public float AnimSpeed; public float AnimTurn; public float AnimCrouch; public float AnimTargetSpeed; public float AnimTargetTurn; public float AnimTargetCrouch; public bool AnimMoving; public Quaternion LastAnimRot; public long LastAnimTick; public CoordFrame LastAnimFrame; public CoordFrame LastPoseFrame; public ushort LastPoseBoatIndex; public bool HasPoseFrame; public bool HasAnimSnapshot; public bool HasSpeedParam; public bool HasTurnParam; public bool HasCrouchFloatParam; public bool HasCrouchBoolParam; public bool HasIsCrouchingParam; public float VisualOffsetY; public NpcLocomotionDriver NpcLoco; public bool NpcFitPending; } private sealed class AvatarPoseDriver : MonoBehaviour { public Transform Spine; public Transform Chest; public Transform Neck; public float TargetPitch; private bool _ready; public void CaptureBase() { _ready = (Object)(object)Spine != (Object)null || (Object)(object)Chest != (Object)null || (Object)(object)Neck != (Object)null; } private void LateUpdate() { //IL_0039: 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_0055: 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_0090: 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_00b9: 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_00d5: Unknown result type (might be due to invalid IL or missing references) if (_ready) { float num = Mathf.Clamp(TargetPitch, -35f, 45f); if ((Object)(object)Spine != (Object)null) { Spine.localRotation *= Quaternion.Euler((0f - num) * 0.25f, 0f, 0f); } if ((Object)(object)Chest != (Object)null) { Chest.localRotation *= Quaternion.Euler((0f - num) * 0.35f, 0f, 0f); } if ((Object)(object)Neck != (Object)null) { Neck.localRotation *= Quaternion.Euler((0f - num) * 0.2f, 0f, 0f); } } } } internal sealed class NpcLocomotionDriver : MonoBehaviour { public float TargetSpeedMps; private float _speed; private float _phase; private Transform _spine; private Transform _legL; private Transform _legR; private Transform _kneeL; private Transform _kneeR; private Transform _armL; private Transform _armR; private Transform _elbowL; private Transform _elbowR; private Quaternion _qSpine; private Quaternion _qLegL; private Quaternion _qLegR; private Quaternion _qKneeL; private Quaternion _qKneeR; private Quaternion _qArmL; private Quaternion _qArmR; private Quaternion _qElbowL; private Quaternion _qElbowR; public bool Ready { get; private set; } public void Setup() { //IL_00db: 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_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_0119: 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_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: 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_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_0195: 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_01b4: 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_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) _spine = Find(((Component)this).transform, "Spine_01"); _legL = Find(((Component)this).transform, "UpperLeg_L"); _legR = Find(((Component)this).transform, "UpperLeg_R"); _kneeL = Find(((Component)this).transform, "LowerLeg_L"); _kneeR = Find(((Component)this).transform, "LowerLeg_R"); _armL = Find(((Component)this).transform, "Shoulder_L"); _armR = Find(((Component)this).transform, "Shoulder_R"); _elbowL = Find(((Component)this).transform, "Elbow_L"); _elbowR = Find(((Component)this).transform, "Elbow_R"); if ((Object)(object)_spine != (Object)null) { _qSpine = _spine.localRotation; } if ((Object)(object)_legL != (Object)null) { _qLegL = _legL.localRotation; } if ((Object)(object)_legR != (Object)null) { _qLegR = _legR.localRotation; } if ((Object)(object)_kneeL != (Object)null) { _qKneeL = _kneeL.localRotation; } if ((Object)(object)_kneeR != (Object)null) { _qKneeR = _kneeR.localRotation; } if ((Object)(object)_armL != (Object)null) { _qArmL = _armL.localRotation; } if ((Object)(object)_armR != (Object)null) { _qArmR = _armR.localRotation; } if ((Object)(object)_elbowL != (Object)null) { _qElbowL = _elbowL.localRotation; } if ((Object)(object)_elbowR != (Object)null) { _qElbowR = _elbowR.localRotation; } Ready = (Object)(object)_legL != (Object)null && (Object)(object)_legR != (Object)null; } private void LateUpdate() { //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_00bd: 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_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) //IL_0115: 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_0154: 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_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_0192: 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_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: 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_01ed: Unknown result type (might be due to invalid IL or missing references) if (Ready) { float num = Mathf.Max(Time.deltaTime, 0.0001f); _speed = Mathf.Lerp(_speed, TargetSpeedMps, 1f - Mathf.Exp(-8f * num)); float num2 = Mathf.Clamp01(_speed / 1.4f); _phase = Mathf.Repeat(_phase + _speed * 4.2f * num, (float)Math.PI * 2f); float num3 = Mathf.Sin(_phase); float num4 = Mathf.Sin(_phase + (float)Math.PI); Swing(_legL, _qLegL, Vector3.up, 28f * num2 * num3); Swing(_legR, _qLegR, Vector3.up, 28f * num2 * num4); Swing(_kneeL, _qKneeL, Vector3.back, 40f * num2 * Mathf.Max(0f, Mathf.Sin(_phase + 1.1f))); Swing(_kneeR, _qKneeR, Vector3.back, 40f * num2 * Mathf.Max(0f, Mathf.Sin(_phase + (float)Math.PI + 1.1f))); Swing(_armL, _qArmL, Vector3.down, 22f * num2 * num4); Swing(_armR, _qArmR, Vector3.down, 22f * num2 * num3); Swing(_elbowL, _qElbowL, Vector3.down, 16f * num2 * (0.5f + 0.5f * num4)); Swing(_elbowR, _qElbowR, Vector3.down, 16f * num2 * (0.5f + 0.5f * num3)); Swing(_spine, _qSpine, Vector3.forward, Mathf.Sin(Time.time * 0.22f * 2f * (float)Math.PI) * 2.2f); } } private static void Swing(Transform t, Quaternion bind, Vector3 axis, float deg) { //IL_000b: 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) if (!((Object)(object)t == (Object)null)) { t.localRotation = bind; t.Rotate(axis, deg, (Space)1); } } private static Transform Find(Transform root, string name) { if (((Object)root).name == name) { return root; } for (int i = 0; i < root.childCount; i++) { Transform val = Find(root.GetChild(i), name); if ((Object)(object)val != (Object)null) { return val; } } return null; } } private sealed class AvatarVisualOffsetDriver : MonoBehaviour { public float OffsetY; private void LateUpdate() { //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) ((Component)this).transform.localPosition = new Vector3(0f, OffsetY, 0f); ((Component)this).transform.localRotation = Quaternion.identity; } } private readonly CoopNet _net; private readonly Dictionary _remotes = new Dictionary(); private readonly Dictionary _bundleCache = new Dictionary(); private readonly Dictionary _prefabCache = new Dictionary(); private readonly Dictionary _remoteAvatarFile = new Dictionary(); private readonly Dictionary _npcRetryAt = new Dictionary(); private const float NpcRetrySec = 5f; private Transform _localPlayer; private PlayerEmbarkerNew _emb; private Transform _lastLocalBoat; private ushort _lastLocalBoatIndex = ushort.MaxValue; private float _lastLocalBoatSeenAt; private float _boatSurfaceValidUntil; private bool _dumped; private float _sendTimer; private Vector3 _lastRealPos; private long _lastRealTick; private bool _haveLast; private CoordFrame _lastFrame; private PlayerCrouching _localCrouching; private bool _lastLocalCrouch; private float _lastLocalCrouchHeight; private static readonly FieldInfo CrouchingField = typeof(PlayerCrouching).GetField("crouching", BindingFlags.Instance | BindingFlags.NonPublic); public float InterpDelayMs = 100f; public int SnapshotHz = 20; public int RemoteCount => _remotes.Count; public bool LocalPlayerFound => (Object)(object)_localPlayer != (Object)null; public string LocalCrouchText => "local " + (_lastLocalCrouch ? "YES" : "—") + ", h " + _lastLocalCrouchHeight.ToString("0.00"); public string NearestRemoteAnim { get { //IL_004c: 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) RemoteAvatar remoteAvatar = null; float num = -1f; foreach (RemoteAvatar value in _remotes.Values) { if (!((Object)(object)value.Go == (Object)null)) { float num2 = (((Object)(object)_localPlayer != (Object)null) ? Vector3.Distance(_localPlayer.position, value.Go.transform.position) : 0f); if (remoteAvatar == null || num2 < num) { remoteAvatar = value; num = num2; } } } if (remoteAvatar == null || (Object)(object)remoteAvatar.Animator == (Object)null) { return "—"; } return "Speed " + remoteAvatar.AnimSpeed.ToString("0.00") + " -> " + remoteAvatar.AnimTargetSpeed.ToString("0.0") + ", Turn " + remoteAvatar.AnimTurn.ToString("0.00") + ", Crouch " + remoteAvatar.AnimCrouch.ToString("0.00") + " -> " + remoteAvatar.AnimTargetCrouch.ToString("0.0") + ", param " + (remoteAvatar.HasCrouchFloatParam ? "float" : (remoteAvatar.HasCrouchBoolParam ? "bool" : (remoteAvatar.HasIsCrouchingParam ? "IsCrouching" : "NO"))) + ", moving " + (remoteAvatar.AnimMoving ? "YES" : "—"); } } public float NearestRemoteDistance { get { //IL_0049: 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) if ((Object)(object)_localPlayer == (Object)null) { return -1f; } float num = -1f; foreach (RemoteAvatar value in _remotes.Values) { if (!((Object)(object)value.Go == (Object)null)) { float num2 = Vector3.Distance(_localPlayer.position, value.Go.transform.position); if (num < 0f || num2 < num) { num = num2; } } } return num; } } public PlayerSync(CoopNet net) { _net = net; } public void RegisterRemoteAvatarFile(uint netId, string bundleFile) { string value = ResolveBundleKey(bundleFile); _remoteAvatarFile[netId] = value; } public void ApplyAvatarChange(uint netId, string bundleFile) { string text = ResolveBundleKey(bundleFile); _remoteAvatarFile[netId] = text; _npcRetryAt.Remove(netId); if (_remotes.TryGetValue(netId, out var value) && (Object)(object)value.Go != (Object)null) { Plugin.Logger.LogInfo((object)("[PlayerSync] AvatarChange NetId=" + netId + " -> '" + text + "' (recreating avatar)")); Object.Destroy((Object)(object)value.Go); _remotes.Remove(netId); } else { Plugin.Logger.LogInfo((object)("[PlayerSync] AvatarChange NetId=" + netId + " -> '" + text + "' (avatar not created yet, selection remembered)")); } } private string ResolveBundleKey(string bundleFile) { if (string.IsNullOrWhiteSpace(bundleFile)) { return "avatar.bundle"; } return bundleFile.Trim(); } public void Tick(float dt) { //IL_0121: 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_012b: 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_0133: 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_013c: 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_00e3: 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_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_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_0100: 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_010f: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_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_01d9: 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_01e1: 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_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0171: 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_0179: 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_0185: Unknown result type (might be due to invalid IL or missing references) if (_net.State != LinkState.Connected || !CoordSpace.Ready) { return; } EnsureLocalPlayer(); if ((Object)(object)_localPlayer == (Object)null) { return; } float num = 1f / (float)Mathf.Max(1, SnapshotHz); _sendTimer += dt; if (_sendTimer < num) { return; } _sendTimer = 0f; long serverTick = _net.Clock.ServerTick; Transform localPlayer = _localPlayer; Transform val = CurrentBoat(); ushort num2 = ushort.MaxValue; if ((Object)(object)val != (Object)null) { num2 = ResolveBoatIndex(val); if (num2 == ushort.MaxValue) { val = null; } } _lastLocalCrouch = IsLocalCrouching(); Camera val2 = PickRenderCamera(); Transform val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).transform : localPlayer); CoordFrame coordFrame; Vector3 val4; Quaternion rot; Quaternion headRot; if ((Object)(object)val != (Object)null) { coordFrame = CoordFrame.Boat; val4 = val.InverseTransformPoint(localPlayer.position); rot = Quaternion.Inverse(val.rotation) * localPlayer.rotation; headRot = Quaternion.Inverse(val.rotation) * val3.rotation; } else { coordFrame = CoordFrame.World; val4 = CoordSpace.LocalToReal(localPlayer.position); rot = localPlayer.rotation; headRot = val3.rotation; } Vector3 vel = Vector3.zero; if (_haveLast && _lastFrame == coordFrame) { float num3 = (float)(serverTick - _lastRealTick) / 1000f; if (num3 > 0.0001f) { vel = (val4 - _lastRealPos) / num3; } } _lastRealPos = val4; _lastRealTick = serverTick; _lastFrame = coordFrame; _haveLast = true; _net.Broadcast(new PlayerStateMsg { NetId = _net.MyNetId, Tick = serverTick, Frame = coordFrame, BoatIndex = num2, Pos = val4, Rot = rot, HeadRot = headRot, Vel = vel, Crouch = _lastLocalCrouch }, (DeliveryMethod)4); } public void OnPlayerState(PlayerStateMsg msg, NetPeer fromPeer) { //IL_0175: 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_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_0198: 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_0121: 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_012b: Unknown result type (might be due to invalid IL or missing references) if (_net.Role == Role.Host) { _net.RelayExcept(msg, fromPeer, (DeliveryMethod)4); } if (msg.NetId == _net.MyNetId) { return; } if (!_remotes.TryGetValue(msg.NetId, out var value)) { value = CreateAvatar(msg.NetId); _remotes[msg.NetId] = value; } value.Net.InterpDelayMs = InterpDelayMs; if (value.HasPoseFrame && value.LastPoseFrame != msg.Frame) { value.Net.Clear(); } value.LastPoseFrame = msg.Frame; value.LastPoseBoatIndex = msg.BoatIndex; value.HasPoseFrame = true; if (msg.Frame == CoordFrame.Boat) { Transform boat = BoatLocator.FindByIndex(msg.BoatIndex); if ((Object)(object)boat != (Object)null) { value.Net.ToWorldPos = (Vector3 p) => boat.TransformPoint(p); value.Net.ToWorldRot = (Quaternion q) => boat.rotation * q; value.HeadWorldRot = boat.rotation * msg.HeadRot; } } else { value.Net.ToWorldPos = CoordSpace.RealToLocal; value.Net.ToWorldRot = (Quaternion q) => q; value.HeadWorldRot = msg.HeadRot; } value.Net.Push(msg.Tick, msg.Pos, msg.Rot, msg.Vel); UpdateAnimatorTargets(value, msg); } public void ApplyRemotes() { if (!CoordSpace.Ready) { return; } long serverTick = _net.Clock.ServerTick; foreach (RemoteAvatar value in _remotes.Values) { if (!((Object)(object)value.Go == (Object)null)) { value.Net.Apply(value.Go.transform, serverTick); ApplyAvatarPolish(value); } } RetryPendingNpcSkins(); } private void RetryPendingNpcSkins() { if (_npcRetryAt.Count == 0) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; List list = null; foreach (KeyValuePair item in _npcRetryAt) { if (!(realtimeSinceStartup < item.Value)) { if (list == null) { list = new List(); } list.Add(item.Key); } } if (list == null) { return; } NpcSkinLibrary.Scan(force: false); foreach (uint item2 in list) { if (!NpcSkinLibrary.CanBuild) { _npcRetryAt[item2] = realtimeSinceStartup + 5f; continue; } _npcRetryAt.Remove(item2); if (_remotes.TryGetValue(item2, out var value)) { Plugin.Logger.LogInfo((object)("[PlayerSync] NPC template captured — rebuilding avatar NetId=" + item2)); if ((Object)(object)value.Go != (Object)null) { Object.Destroy((Object)(object)value.Go); } _remotes.Remove(item2); } } } public void RemoveRemote(uint netId) { if (_remotes.TryGetValue(netId, out var value)) { if ((Object)(object)value.Go != (Object)null) { Object.Destroy((Object)(object)value.Go); } _remotes.Remove(netId); } _remoteAvatarFile.Remove(netId); _npcRetryAt.Remove(netId); } public void Clear() { foreach (RemoteAvatar value in _remotes.Values) { if ((Object)(object)value.Go != (Object)null) { Object.Destroy((Object)(object)value.Go); } } _remotes.Clear(); _remoteAvatarFile.Clear(); _npcRetryAt.Clear(); _localPlayer = null; _lastLocalBoat = null; _lastLocalBoatIndex = ushort.MaxValue; _lastLocalBoatSeenAt = 0f; _boatSurfaceValidUntil = 0f; _localCrouching = null; _haveLast = false; foreach (AssetBundle value2 in _bundleCache.Values) { if ((Object)(object)value2 != (Object)null) { value2.Unload(false); } } _bundleCache.Clear(); _prefabCache.Clear(); } private void EnsureLocalPlayer() { if (!((Object)(object)_localPlayer != (Object)null)) { PlayerEmbarkerNew val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && (Object)(object)val.playerObserver != (Object)null) { _emb = val; _localPlayer = val.playerObserver; DumpHierarchy(); } else if ((Object)(object)Camera.main != (Object)null) { _localPlayer = ((Component)Camera.main).transform; } } } private Transform CurrentBoat() { //IL_00c9: 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_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) Transform val = (((Object)(object)_emb != (Object)null) ? _emb.debugOutCurrentBoat : null); if ((Object)(object)val != (Object)null) { int num = ProbeStandingSurface(val); if (num > 0) { _lastLocalBoat = val; _lastLocalBoatSeenAt = Time.time; _boatSurfaceValidUntil = Time.time + 1f; return val; } if ((Object)(object)_lastLocalBoat == (Object)(object)val && num == 0) { return val; } if ((Object)(object)_lastLocalBoat == (Object)(object)val && Time.time < _boatSurfaceValidUntil) { return val; } } if ((Object)(object)_lastLocalBoat != (Object)null && (Object)(object)_localPlayer != (Object)null && Time.time - _lastLocalBoatSeenAt < 0.2f && Time.time < _boatSurfaceValidUntil) { Vector3 val2 = _localPlayer.position - _lastLocalBoat.position; if (((Vector3)(ref val2)).sqrMagnitude < 6400f) { return _lastLocalBoat; } } _boatSurfaceValidUntil = 0f; return null; } private ushort ResolveBoatIndex(Transform boat) { if ((Object)(object)boat == (Object)null) { return ushort.MaxValue; } ushort num = BoatLocator.IndexOf(boat); if (num != ushort.MaxValue) { _lastLocalBoat = boat; _lastLocalBoatIndex = num; return num; } if ((Object)(object)boat == (Object)(object)_lastLocalBoat && _lastLocalBoatIndex != ushort.MaxValue) { return _lastLocalBoatIndex; } return ushort.MaxValue; } private int ProbeStandingSurface(Transform boat) { //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_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_0039: 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_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) //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_0098: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)boat == (Object)null || (Object)(object)_localPlayer == (Object)null) { return 0; } try { RaycastHit[] array = Physics.RaycastAll(_localPlayer.position + Vector3.up * 0.25f, Vector3.down, 3f, -1, (QueryTriggerInteraction)1); if (array == null || array.Length == 0) { return 0; } for (int i = 0; i < array.Length - 1; i++) { for (int j = i + 1; j < array.Length; j++) { if (((RaycastHit)(ref array[j])).distance < ((RaycastHit)(ref array[i])).distance) { RaycastHit val = array[i]; array[i] = array[j]; array[j] = val; } } } for (int k = 0; k < array.Length; k++) { Collider collider = ((RaycastHit)(ref array[k])).collider; if (!((Object)(object)collider == (Object)null) && !collider.isTrigger && !((Object)(object)((Component)collider).transform == (Object)(object)_localPlayer) && !((Component)collider).transform.IsChildOf(_localPlayer) && !((Object)(object)((Component)collider).GetComponentInParent() != (Object)null)) { return IsBoatSurface(((Component)collider).transform, boat) ? 1 : (-1); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[PlayerSync] standing surface probe: " + ex.Message)); } return 0; } private static bool IsBoatSurface(Transform t, Transform boat) { Transform val = t; while ((Object)(object)val != (Object)null) { if ((Object)(object)val == (Object)(object)boat) { return true; } if (((Component)val).CompareTag("Boat")) { return true; } if (((Component)val).CompareTag("WalkColBoat")) { return true; } if (((Component)val).gameObject.layer == 8 && ((Component)val).CompareTag("WalkColBoat")) { return true; } val = val.parent; } return false; } private void ApplyAvatarPolish(RemoteAvatar a) { //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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_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_00ae: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)a.Go == (Object)null)) { ApplyAnimatorParams(a); ApplyLookPitch(a); ApplyVisualOffset(a); if (a.NpcFitPending) { FitNpcBody(a); } if ((Object)(object)a.NpcLoco != (Object)null) { a.NpcLoco.TargetSpeedMps = (a.AnimMoving ? 1.4f : 0f); } if ((Object)(object)a.Head != (Object)null && !a.HeadDrivenByAnimator) { Quaternion val = Quaternion.Inverse(a.Go.transform.rotation) * a.HeadWorldRot; a.Head.localRotation = Quaternion.Slerp(a.Head.localRotation, val, 0.35f); } } } private void ApplyLookPitch(RemoteAvatar a) { //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_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_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) if (!((Object)(object)a.PoseDriver == (Object)null)) { float num = Mathf.Asin(Mathf.Clamp((Quaternion.Inverse(a.Go.transform.rotation) * (a.HeadWorldRot * Vector3.forward)).y, -1f, 1f)) * 57.29578f; a.PoseDriver.TargetPitch = Mathf.Clamp(num, -35f, 45f); } } private void ApplyVisualOffset(RemoteAvatar a) { //IL_003d: 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) if (!((Object)(object)a.Body == (Object)null) && !((Object)(object)a.Body == (Object)(object)a.Go.transform)) { a.Body.localPosition = new Vector3(0f, a.VisualOffsetY, 0f); a.Body.localRotation = Quaternion.identity; } } private void ApplyAnimatorParams(RemoteAvatar a) { if (!((Object)(object)a.Animator == (Object)null)) { float num = Mathf.Max(Time.deltaTime, 0.0001f); a.AnimSpeed = Mathf.Lerp(a.AnimSpeed, a.AnimTargetSpeed, 1f - Mathf.Exp(-12f * num)); a.AnimTurn = Mathf.Lerp(a.AnimTurn, a.AnimTargetTurn, 1f - Mathf.Exp(-10f * num)); a.AnimCrouch = Mathf.Lerp(a.AnimCrouch, a.AnimTargetCrouch, 1f - Mathf.Exp(-12f * num)); if (a.HasSpeedParam) { a.Animator.SetFloat("Speed", a.AnimSpeed); } if (a.HasTurnParam) { a.Animator.SetFloat("Turn", a.AnimTurn); } if (a.HasCrouchFloatParam) { a.Animator.SetFloat("Crouch", a.AnimCrouch); } if (a.HasCrouchBoolParam) { a.Animator.SetBool("Crouch", a.AnimTargetCrouch > 0.5f); } if (a.HasIsCrouchingParam) { a.Animator.SetBool("IsCrouching", a.AnimTargetCrouch > 0.5f); } } } private void UpdateAnimatorTargets(RemoteAvatar a, PlayerStateMsg msg) { //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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_00ec: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) Vector3 vel = msg.Vel; vel.y = 0f; float magnitude = ((Vector3)(ref vel)).magnitude; if (a.AnimMoving) { if (magnitude < 0.08f) { a.AnimMoving = false; } } else if (magnitude > 0.18f) { a.AnimMoving = true; } a.AnimTargetSpeed = (a.AnimMoving ? 3f : 0f); a.AnimTargetCrouch = (msg.Crouch ? 1f : 0f); if (!a.HasAnimSnapshot || a.LastAnimFrame != msg.Frame) { a.LastAnimRot = msg.Rot; a.LastAnimTick = msg.Tick; a.LastAnimFrame = msg.Frame; a.HasAnimSnapshot = true; a.AnimTargetTurn = 0f; return; } float num = (float)(msg.Tick - a.LastAnimTick) / 1000f; if (num > 0.0001f) { Vector3 val = a.LastAnimRot * Vector3.forward; Vector3 val2 = msg.Rot * Vector3.forward; float num2 = Vector3.SignedAngle(val, val2, Vector3.up) / num; a.AnimTargetTurn = ((Mathf.Abs(num2) < 15f) ? 0f : Mathf.Clamp(num2 / 180f, -1f, 1f)); } a.LastAnimRot = msg.Rot; a.LastAnimTick = msg.Tick; a.LastAnimFrame = msg.Frame; } private bool IsLocalCrouching() { try { if ((Object)(object)_localCrouching == (Object)null) { _localCrouching = Object.FindObjectOfType(); } if ((Object)(object)_localCrouching == (Object)null) { return false; } _lastLocalCrouchHeight = _localCrouching.GetCurrentHeadHeight(); if (CrouchingField != null) { return (bool)CrouchingField.GetValue(_localCrouching) || (_lastLocalCrouchHeight > 0.001f && _lastLocalCrouchHeight < 0.6f); } return _lastLocalCrouchHeight > 0.001f && _lastLocalCrouchHeight < 0.6f; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[PlayerSync] Failed to read crouch state: " + ex.Message)); return false; } } private void DumpHierarchy() { //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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_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_00df: 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_017a: 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) if (_dumped) { return; } _dumped = true; ManualLogSource logger = Plugin.Logger; string[] obj = new string[10] { "[PlayerSync] HIER observer@", null, null, null, null, null, null, null, null, null }; Vector3 val = P(_emb.playerObserver); obj[1] = ((Vector3)(ref val)).ToString("F1"); obj[2] = " controller@"; val = P(_emb.playerController); obj[3] = ((Vector3)(ref val)).ToString("F1"); obj[4] = " shiftingWorld@"; val = P(_emb.shiftingWorld); obj[5] = ((Vector3)(ref val)).ToString("F1"); obj[6] = " curBoat="; obj[7] = (((Object)(object)_emb.debugOutCurrentBoat != (Object)null) ? ((Object)_emb.debugOutCurrentBoat).name : "null"); obj[8] = "@"; val = P(_emb.debugOutCurrentBoat); obj[9] = ((Vector3)(ref val)).ToString("F1"); logger.LogInfo((object)string.Concat(obj)); Camera val2 = PickRenderCamera(); if (!((Object)(object)val2 != (Object)null)) { return; } string text = ""; Transform val3 = ((Component)val2).transform; for (int i = 0; i < 6; i++) { if (!((Object)(object)val3 != (Object)null)) { break; } text = text + ((Object)val3).name + " < "; val3 = val3.parent; } ManualLogSource logger2 = Plugin.Logger; string[] obj2 = new string[6] { "[PlayerSync] HIER cam=", ((Object)val2).name, "@", null, null, null }; val = ((Component)val2).transform.position; obj2[3] = ((Vector3)(ref val)).ToString("F1"); obj2[4] = " parents: "; obj2[5] = text; logger2.LogInfo((object)string.Concat(obj2)); static Vector3 P(Transform t) { //IL_0010: 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) if (!((Object)(object)t != (Object)null)) { return Vector3.zero; } return t.position; } } private RemoteAvatar CreateAvatar(uint netId) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_00ce: 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_0137: 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_019e: 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_022f: Unknown result type (might be due to invalid IL or missing references) //IL_024f: 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) if (!_remoteAvatarFile.TryGetValue(netId, out var value)) { value = "avatar.bundle"; } RemoteAvatar remoteAvatar = TryCreateBundledAvatar(netId, value); if (remoteAvatar != null) { return remoteAvatar; } GameObject val = new GameObject("CoopPlayer_" + netId); ((Object)val).name = "CoopPlayer_" + netId; val.layer = PickVisibleLayer(); Shader val2 = Shader.Find("Sprites/Default"); if ((Object)(object)val2 == (Object)null) { val2 = Shader.Find("Unlit/Color"); } if ((Object)(object)val2 == (Object)null) { val2 = Shader.Find("Standard"); } GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)1); ((Object)val3).name = "Body"; val3.transform.SetParent(val.transform, false); val3.transform.localPosition = new Vector3(0f, -0.45f, 0f); val3.transform.localScale = new Vector3(0.45f, 0.65f, 0.45f); val3.layer = val.layer; Collider component = val3.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } SetUnlit(val3.GetComponent(), val2, new Color(0.15f, 0.6f, 1f)); GameObject val4 = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)val4).name = "Head"; val4.transform.SetParent(val.transform, false); val4.transform.localPosition = new Vector3(0f, 0.35f, 0f); val4.transform.localScale = new Vector3(0.32f, 0.32f, 0.32f); val4.layer = val.layer; Collider component2 = val4.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } SetUnlit(val4.GetComponent(), val2, new Color(0.95f, 0.85f, 0.65f)); GameObject val5 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val5).name = "LookDir"; val5.transform.SetParent(val4.transform, false); val5.transform.localPosition = new Vector3(0f, 0f, 0.28f); val5.transform.localScale = new Vector3(0.08f, 0.08f, 0.22f); val5.layer = val.layer; Collider component3 = val5.GetComponent(); if ((Object)(object)component3 != (Object)null) { Object.Destroy((Object)(object)component3); } SetUnlit(val5.GetComponent(), val2, new Color(0.1f, 0.1f, 0.12f)); Plugin.Logger.LogInfo((object)("[PlayerSync] Avatar NetId=" + netId + " shader='" + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "NO") + "' layer=" + val.layer)); Object.DontDestroyOnLoad((Object)(object)val); Plugin.Logger.LogInfo((object)("[PlayerSync] Created player avatar NetId=" + netId)); return new RemoteAvatar { Go = val, Body = val3.transform, Head = val4.transform }; } private RemoteAvatar TryCreateBundledAvatar(uint netId, string bundleFile) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00bb: 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_00db: 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: Invalid comparison between Unknown and I4 //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Invalid comparison between Unknown and I4 if (NpcSkinLibrary.IsNpcKey(bundleFile)) { RemoteAvatar remoteAvatar = TryCreateNpcAvatar(netId, bundleFile); if (remoteAvatar != null) { _npcRetryAt.Remove(netId); return remoteAvatar; } _npcRetryAt[netId] = Time.realtimeSinceStartup + 5f; Plugin.Logger.LogWarning((object)"[PlayerSync] NPC skin unavailable (template missing), fallback to avatar.bundle; retry scheduled"); bundleFile = "avatar.bundle"; } GameObject avatarPrefab = GetAvatarPrefab(bundleFile); if ((Object)(object)avatarPrefab == (Object)null) { return null; } GameObject val = new GameObject("CoopPlayer_" + netId); GameObject val2 = Object.Instantiate(avatarPrefab); ((Object)val2).name = ((Object)avatarPrefab).name; val2.transform.SetParent(val.transform, false); float num = ResolveAvatarVerticalOffset(netId); val2.transform.localPosition = new Vector3(0f, num, 0f); val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = Vector3.one; val2.AddComponent().OffsetY = num; int layer = PickVisibleLayer(); SetLayerRecursive(val.transform, layer); StripColliders(val); StripRigidbodies(val); Animator componentInChildren = val2.GetComponentInChildren(true); bool hasSpeedParam = false; bool hasTurnParam = false; bool hasCrouchFloatParam = false; bool hasCrouchBoolParam = false; bool hasIsCrouchingParam = false; if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.cullingMode = (AnimatorCullingMode)0; componentInChildren.updateMode = (AnimatorUpdateMode)0; componentInChildren.applyRootMotion = false; ((Behaviour)componentInChildren).enabled = true; AnimatorControllerParameter[] parameters = componentInChildren.parameters; foreach (AnimatorControllerParameter val3 in parameters) { if ((int)val3.type == 1) { if (val3.name == "Speed") { hasSpeedParam = true; } if (val3.name == "Turn") { hasTurnParam = true; } if (val3.name == "Crouch") { hasCrouchFloatParam = true; } } else if ((int)val3.type == 4) { if (val3.name == "Crouch") { hasCrouchBoolParam = true; } if (val3.name == "IsCrouching") { hasIsCrouchingParam = true; } } } Plugin.Logger.LogInfo((object)("[PlayerSync] Animator params: Speed=" + hasSpeedParam + ", Turn=" + hasTurnParam + ", CrouchFloat=" + hasCrouchFloatParam + ", CrouchBool=" + hasCrouchBoolParam + ", IsCrouching=" + hasIsCrouchingParam)); } Transform val4 = FindChildRecursive(val2.transform, "Head"); if ((Object)(object)val4 == (Object)null) { val4 = FindChildRecursive(val2.transform, "Neck"); } Transform val5 = FindChildRecursive(val2.transform, "Spine"); Transform val6 = FindChildRecursive(val2.transform, "Spine1"); if ((Object)(object)val6 == (Object)null) { val6 = FindChildRecursive(val2.transform, "Chest"); } Transform val7 = FindChildRecursive(val2.transform, "Neck"); AvatarPoseDriver avatarPoseDriver = val2.AddComponent(); avatarPoseDriver.Spine = val5; avatarPoseDriver.Chest = val6; avatarPoseDriver.Neck = val7; avatarPoseDriver.CaptureBase(); Object.DontDestroyOnLoad((Object)(object)val); Plugin.Logger.LogInfo((object)("[PlayerSync] Created avatar.bundle avatar NetId=" + netId + ", head=" + (((Object)(object)val4 != (Object)null) ? ((Object)val4).name : "none") + ", offsetY=" + num.ToString("F2"))); Plugin.Logger.LogInfo((object)("[PlayerSync] Pose bones: spine=" + (((Object)(object)val5 != (Object)null) ? ((Object)val5).name : "none") + ", chest=" + (((Object)(object)val6 != (Object)null) ? ((Object)val6).name : "none") + ", neck=" + (((Object)(object)val7 != (Object)null) ? ((Object)val7).name : "none"))); return new RemoteAvatar { Go = val, Body = val2.transform, Head = val4, Animator = componentInChildren, PoseDriver = avatarPoseDriver, HeadDrivenByAnimator = ((Object)(object)componentInChildren != (Object)null), HasSpeedParam = hasSpeedParam, HasTurnParam = hasTurnParam, HasCrouchFloatParam = hasCrouchFloatParam, HasCrouchBoolParam = hasCrouchBoolParam, HasIsCrouchingParam = hasIsCrouchingParam, VisualOffsetY = num }; } private RemoteAvatar TryCreateNpcAvatar(uint netId, string key) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0054: 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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) GameObject val = NpcSkinLibrary.BuildModel(key); if ((Object)(object)val == (Object)null) { return null; } GameObject val2 = new GameObject("CoopPlayer_" + netId); val.transform.SetParent(val2.transform, false); float num = ResolveAvatarVerticalOffset(netId); val.transform.localPosition = new Vector3(0f, num, 0f); val.transform.localRotation = Quaternion.identity; val.AddComponent().OffsetY = num; SetLayerRecursive(val2.transform, PickVisibleLayer()); StripColliders(val2); StripRigidbodies(val2); Transform val3 = FindChildRecursive(val.transform, "Head"); if ((Object)(object)val3 == (Object)null) { val3 = FindChildRecursive(val.transform, "Neck"); } NpcLocomotionDriver npcLocomotionDriver = val.AddComponent(); npcLocomotionDriver.Setup(); Object.DontDestroyOnLoad((Object)(object)val2); Plugin.Logger.LogInfo((object)("[PlayerSync] Created NPC skin avatar NetId=" + netId + ", head=" + (((Object)(object)val3 != (Object)null) ? ((Object)val3).name : "none") + ", offsetY=" + num.ToString("F2") + ", scale=" + val.transform.localScale.x.ToString("F2") + ", loco=" + npcLocomotionDriver.Ready)); return new RemoteAvatar { Go = val2, Body = val.transform, Head = val3, Animator = null, PoseDriver = null, HeadDrivenByAnimator = true, VisualOffsetY = num, NpcLoco = npcLocomotionDriver, NpcFitPending = true }; } private void FitNpcBody(RemoteAvatar a) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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) try { if ((Object)(object)a.Body == (Object)null || (Object)(object)a.Go == (Object)null) { a.NpcFitPending = false; return; } Renderer[] componentsInChildren = ((Component)a.Body).GetComponentsInChildren(); if (componentsInChildren.Length == 0) { a.NpcFitPending = false; return; } Bounds bounds = componentsInChildren[0].bounds; for (int i = 1; i < componentsInChildren.Length; i++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds); } if (!(((Bounds)(ref bounds)).size.y < 0.5f)) { float y = a.Go.transform.InverseTransformPoint(new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y, ((Bounds)(ref bounds)).center.z)).y; float num = a.VisualOffsetY - y; a.VisualOffsetY += num; AvatarVisualOffsetDriver component = ((Component)a.Body).GetComponent(); if ((Object)(object)component != (Object)null) { component.OffsetY = a.VisualOffsetY; } a.NpcFitPending = false; Plugin.Logger.LogInfo((object)("[PlayerSync] NPC body fit: shift=" + num.ToString("F2") + ", offsetY=" + a.VisualOffsetY.ToString("F2"))); } } catch (Exception ex) { a.NpcFitPending = false; Plugin.Logger.LogWarning((object)("[PlayerSync] FitNpcBody: " + ex.Message)); } } private GameObject GetAvatarPrefab(string bundleFile) { if (string.IsNullOrWhiteSpace(bundleFile)) { bundleFile = "avatar.bundle"; } string text = bundleFile.Trim(); if (_prefabCache.TryGetValue(text, out var value) && (Object)(object)value != (Object)null) { return value; } string text2 = AvatarCatalog.ResolvePath(text); if (string.IsNullOrEmpty(text2)) { if (!string.Equals(text, "avatar.bundle", StringComparison.OrdinalIgnoreCase)) { Plugin.Logger.LogWarning((object)("[PlayerSync] '" + text + "' not found, fallback to avatar.bundle")); return GetAvatarPrefab("avatar.bundle"); } Plugin.Logger.LogInfo((object)"[PlayerSync] avatar.bundle not found, using primitive avatar"); return null; } AssetBundle val = AssetBundle.LoadFromFile(text2); if ((Object)(object)val == (Object)null) { Plugin.Logger.LogWarning((object)("[PlayerSync] Failed to load " + text + ": " + text2 + " (" + ReadBundleHeader(text2) + ")")); if (!string.Equals(text, "avatar.bundle", StringComparison.OrdinalIgnoreCase)) { return GetAvatarPrefab("avatar.bundle"); } return null; } GameObject val2 = val.LoadAsset("Modular Fantasy Character"); if ((Object)(object)val2 == (Object)null) { val2 = val.LoadAsset("Modular Fantasy Character.prefab"); } if ((Object)(object)val2 == (Object)null) { val2 = val.LoadAsset("Cowboy"); } string[] allAssetNames = val.GetAllAssetNames(); string text3 = ((allAssetNames != null && allAssetNames.Length != 0) ? string.Join(", ", allAssetNames) : ""); Plugin.Logger.LogInfo((object)("[PlayerSync] " + text + " assets: " + text3)); if ((Object)(object)val2 == (Object)null && allAssetNames != null) { val2 = PickAvatarPrefab(val, allAssetNames, "modular fantasy character"); } if ((Object)(object)val2 == (Object)null && allAssetNames != null) { val2 = PickAvatarPrefab(val, allAssetNames, preferCowboy: true); } if ((Object)(object)val2 == (Object)null && allAssetNames != null) { val2 = PickAvatarPrefab(val, allAssetNames, preferCowboy: false); } if ((Object)(object)val2 == (Object)null) { Plugin.Logger.LogWarning((object)("[PlayerSync] In " + text + " GameObject prefab not found")); val.Unload(false); if (!string.Equals(text, "avatar.bundle", StringComparison.OrdinalIgnoreCase)) { return GetAvatarPrefab("avatar.bundle"); } return null; } _bundleCache[text] = val; _prefabCache[text] = val2; Plugin.Logger.LogInfo((object)("[PlayerSync] Loaded '" + text + "' prefab '" + ((Object)val2).name + "'")); return val2; } private float ResolveAvatarVerticalOffset(uint netId) { if (Plugin.Cfg == null) { return -0.6f; } if (netId == 1) { float value = Plugin.Cfg.HostAvatarVerticalOffset.Value; if (Mathf.Abs(value - -0.25f) < 0.001f) { return -1.15f; } return value; } float value2 = Plugin.Cfg.AvatarVerticalOffset.Value; if (Mathf.Abs(value2 - -0.25f) < 0.001f) { Plugin.Logger.LogInfo((object)("[PlayerSync] Avatar.VerticalOffset=-0.25 is obsolete, applying " + (-0.6f).ToString("F2"))); return -0.6f; } return value2; } private GameObject PickAvatarPrefab(AssetBundle bundle, string[] names, string contains) { foreach (string text in names) { if (!string.IsNullOrEmpty(text) && text.ToLowerInvariant().IndexOf(contains) >= 0) { GameObject val = bundle.LoadAsset(text); if ((Object)(object)val != (Object)null) { Plugin.Logger.LogInfo((object)("[PlayerSync] selected asset '" + text + "' -> '" + ((Object)val).name + "'")); return val; } } } return null; } private GameObject PickAvatarPrefab(AssetBundle bundle, string[] names, bool preferCowboy) { foreach (string text in names) { if (!string.IsNullOrEmpty(text) && (!preferCowboy || text.ToLowerInvariant().IndexOf("cowboy") >= 0)) { GameObject val = bundle.LoadAsset(text); if ((Object)(object)val != (Object)null) { Plugin.Logger.LogInfo((object)("[PlayerSync] selected asset '" + text + "' -> '" + ((Object)val).name + "'")); return val; } } } return null; } private string ReadBundleHeader(string path) { try { byte[] array = File.ReadAllBytes(path); int count = Mathf.Min(array.Length, 96); return Encoding.ASCII.GetString(array, 0, count).Replace('\0', ' ').Trim(); } catch (Exception ex) { return "header read failed: " + ex.Message; } } private void SetUnlit(Renderer rend, Shader sh, Color color) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (!((Object)(object)rend == (Object)null)) { Material material = new Material(sh) { color = color }; rend.material = material; rend.shadowCastingMode = (ShadowCastingMode)0; rend.receiveShadows = false; } } private void SetLayerRecursive(Transform root, int layer) { if (!((Object)(object)root == (Object)null)) { ((Component)root).gameObject.layer = layer; for (int i = 0; i < root.childCount; i++) { SetLayerRecursive(root.GetChild(i), layer); } } } private void StripColliders(GameObject root) { if ((Object)(object)root == (Object)null) { return; } Collider[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } private void StripRigidbodies(GameObject root) { if ((Object)(object)root == (Object)null) { return; } Rigidbody[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Rigidbody val in componentsInChildren) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } private Transform FindChildRecursive(Transform root, string name) { if ((Object)(object)root == (Object)null) { return null; } if (((Object)root).name == name) { return root; } for (int i = 0; i < root.childCount; i++) { Transform val = FindChildRecursive(root.GetChild(i), name); if ((Object)(object)val != (Object)null) { return val; } } return null; } private Camera PickRenderCamera() { if ((Object)(object)Camera.main != (Object)null) { return Camera.main; } Camera val = null; Camera[] allCameras = Camera.allCameras; foreach (Camera val2 in allCameras) { if (((Behaviour)val2).enabled && ((Object)(object)val == (Object)null || val2.depth > val.depth)) { val = val2; } } return val; } private int PickVisibleLayer() { Camera val = Camera.main; if ((Object)(object)val == (Object)null) { Camera val2 = null; Camera[] allCameras = Camera.allCameras; foreach (Camera val3 in allCameras) { if (((Behaviour)val3).enabled && ((Object)(object)val2 == (Object)null || val3.depth > val2.depth)) { val2 = val3; } } val = val2; } int num = (((Object)(object)val != (Object)null) ? val.cullingMask : (-1)); int num2 = (((Object)(object)_localPlayer != (Object)null) ? ((Component)_localPlayer).gameObject.layer : (-1)); if ((num & 1) != 0 && num2 != 0) { return 0; } for (int j = 0; j < 32; j++) { if (j != num2 && (num & (1 << j)) != 0) { return j; } } return 0; } } public static class SavePatches { public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, typeof(SaveLoadManager), "SaveGame", new Type[1] { typeof(bool) }, "PreSaveGame"); Plugin.Logger.LogInfo((object)("[SavePatches] Save patch (guest does not write host world): SaveGame=" + flag)); PatchHealth.Report("Save", flag ? 1 : 0, 1); } private static bool GuestConnected() { CoopNet coopNet = (((Object)(object)CoopBehaviour.Instance != (Object)null) ? CoopBehaviour.Instance.Net : null); if (coopNet != null && coopNet.Role == Role.Client) { return coopNet.State == LinkState.Connected; } return false; } private static bool TryPatch(Harmony harmony, Type type, string method, Type[] args, string prefixName) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown try { MethodInfo method2 = type.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method2 == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(SavePatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SavePatches] " + method + ": " + ex.Message)); return false; } } private static bool PreSaveGame() { try { if (GuestConnected()) { CoopProfile.SaveFromGame(); Plugin.Logger.LogInfo((object)"[SavePatches] Host world not saved; guest character profile was written"); return false; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SavePatches] PreSaveGame: " + ex.Message)); } return true; } } public sealed class SaveTransferSync { private const int ChunkSize = 16384; private readonly CoopNet _net; public int CoopSlot = 5; private byte[][] _chunks; private int _expectedChunks; private int _receivedChunks; private int _totalBytes; private int _hostGameVersion; private bool _receiving; public bool Receiving => _receiving; public float Progress { get { if (_expectedChunks <= 0) { return 0f; } return (float)_receivedChunks / (float)_expectedChunks; } } public event Action OnSaveLoaded; public SaveTransferSync(CoopNet net) { _net = net; } public void SendSaveTo(NetPeer peer, byte[] bytes) { if (peer == null || bytes == null || bytes.Length == 0) { Plugin.Logger.LogWarning((object)"[SaveTransfer] Nothing to send to client (empty save)"); return; } int num = (bytes.Length + 16384 - 1) / 16384; peer.Send(new SaveSnapshotBeginMsg { TotalBytes = bytes.Length, ChunkCount = num, GameVersion = HostGameVersion() }, (DeliveryMethod)2); for (int i = 0; i < num; i++) { int num2 = i * 16384; int num3 = Math.Min(16384, bytes.Length - num2); byte[] array = new byte[num3]; Buffer.BlockCopy(bytes, num2, array, 0, num3); peer.Send(new SaveSnapshotChunkMsg { Index = i, Data = array }, (DeliveryMethod)2); } peer.Send(new SaveSnapshotEndMsg { Ok = true }, (DeliveryMethod)2); Plugin.Logger.LogInfo((object)("[SaveTransfer] Sent host save to client: " + bytes.Length + " bytes in " + num + " chunks")); } public static byte[] ReadHostSaveBytes() { try { string currentSavePath = SaveSlots.GetCurrentSavePath(); if (!File.Exists(currentSavePath)) { Plugin.Logger.LogWarning((object)("[SaveTransfer] Host save file not found: " + currentSavePath)); return null; } return File.ReadAllBytes(currentSavePath); } catch (Exception ex) { Plugin.Logger.LogError((object)("[SaveTransfer] Failed to read host save: " + ex)); return null; } } public static bool HostSaveBusy() { try { SaveLoadManager instance = SaveLoadManager.instance; if ((Object)(object)instance == (Object)null) { return false; } FieldInfo field = typeof(SaveLoadManager).GetField("busy", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { return (bool)field.GetValue(instance); } } catch { } return false; } private static int HostGameVersion() { try { SaveLoadManager instance = SaveLoadManager.instance; if ((Object)(object)instance == (Object)null) { return 1; } FieldInfo field = typeof(SaveLoadManager).GetField("gameVersion", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { return (int)field.GetValue(instance); } } catch { } return 1; } public void OnBegin(SaveSnapshotBeginMsg msg) { if (_net.Role == Role.Client) { _expectedChunks = Mathf.Max(0, msg.ChunkCount); _totalBytes = Mathf.Max(0, msg.TotalBytes); _hostGameVersion = msg.GameVersion; _chunks = new byte[_expectedChunks][]; _receivedChunks = 0; _receiving = true; Plugin.Logger.LogInfo((object)("[SaveTransfer] Receiving host save: " + _totalBytes + " bytes, " + _expectedChunks + " chunks (gameVersion=" + _hostGameVersion + ")")); } } public void OnChunk(SaveSnapshotChunkMsg msg) { if (_net.Role == Role.Client && _receiving && _chunks != null && msg.Index >= 0 && msg.Index < _chunks.Length) { if (_chunks[msg.Index] == null) { _receivedChunks++; } _chunks[msg.Index] = msg.Data; } } public void OnEnd(SaveSnapshotEndMsg msg) { if (_net.Role != Role.Client || !_receiving) { return; } _receiving = false; try { if (_receivedChunks != _expectedChunks) { Plugin.Logger.LogError((object)("[SaveTransfer] Received " + _receivedChunks + "/" + _expectedChunks + " chunks - receive aborted")); NotifyHostLoaded(ok: false); return; } byte[] array = Assemble(); if (array == null || array.Length != _totalBytes) { Plugin.Logger.LogError((object)("[SaveTransfer] Assembled save size mismatch (" + ((array != null) ? array.Length : 0) + " != " + _totalBytes + ")")); NotifyHostLoaded(ok: false); } else { ApplyHostSave(array); } } catch (Exception ex) { Plugin.Logger.LogError((object)("[SaveTransfer] Failed to apply host save: " + ex)); NotifyHostLoaded(ok: false); } finally { _chunks = null; } } private void NotifyHostLoaded(bool ok) { try { _net.Broadcast(new ClientWorldLoadedMsg { Ok = ok }, (DeliveryMethod)2); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SaveTransfer] ClientWorldLoaded not sent: " + ex.Message)); } } private byte[] Assemble() { byte[] array = new byte[_totalBytes]; int num = 0; for (int i = 0; i < _chunks.Length; i++) { byte[] array2 = _chunks[i]; if (array2 == null) { return null; } Buffer.BlockCopy(array2, 0, array, num, array2.Length); num += array2.Length; } return array; } private void ApplyHostSave(byte[] bytes) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown SaveContainer val; using (MemoryStream serializationStream = new MemoryStream(bytes)) { val = (SaveContainer)new BinaryFormatter().Deserialize(serializationStream); } CoopProfile.MergeInto(val); int num = Mathf.Clamp(CoopSlot, 0, 5); string slotSavePath = SaveSlots.GetSlotSavePath(num); using (FileStream serializationStream2 = File.Create(slotSavePath)) { new BinaryFormatter().Serialize(serializationStream2, val); } Plugin.Logger.LogInfo((object)("[SaveTransfer] Merged save written to slot " + num + ": " + slotSavePath)); TriggerLoad(num); } private void TriggerLoad(int slot) { CoopBehaviour instance = CoopBehaviour.Instance; if ((Object)(object)instance == (Object)null) { Plugin.Logger.LogError((object)"[SaveTransfer] No CoopBehaviour - nothing can start the load"); NotifyHostLoaded(ok: false); } else { ((MonoBehaviour)instance).StartCoroutine(LoadRoutine(slot)); } } private IEnumerator LoadRoutine(int slot) { if (GameState.playing || GameState.currentlyLoading) { Plugin.Logger.LogError((object)"[SaveTransfer] Client is already in-game - host world was not loaded. Return to the main menu and reconnect."); NotifyHostLoaded(ok: false); yield break; } SaveSlots.currentSlot = slot; if (SaveSlots.slotsActive != null && slot < SaveSlots.slotsActive.Length) { SaveSlots.slotsActive[slot] = true; } FieldInfo fAnims = typeof(StartMenu).GetField("animsPlaying", BindingFlags.Instance | BindingFlags.NonPublic); float t = 0f; while (!GameState.currentlyLoading) { if (t >= 10f) { Plugin.Logger.LogError((object)"[SaveTransfer] Failed to start host world load within 10 s (StartMenu busy or unavailable)"); NotifyHostLoaded(ok: false); yield break; } StartMenu val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && AnimsPlaying(fAnims, val) == 0) { val.selectedContinue = true; try { val.ButtonClick(slot, 0); } catch (Exception ex) { Plugin.Logger.LogError((object)("[SaveTransfer] ButtonClick: " + ex)); NotifyHostLoaded(ok: false); yield break; } if (GameState.currentlyLoading) { break; } } yield return null; t += Time.unscaledDeltaTime; } Plugin.Logger.LogInfo((object)("[SaveTransfer] Started host world load through menu (slot " + slot + ")")); t = 0f; while (!GameState.playing && t < 60f) { yield return null; t += Time.unscaledDeltaTime; } NotifyHostLoaded(GameState.playing); if (GameState.playing) { this.OnSaveLoaded?.Invoke(); } else { Plugin.Logger.LogWarning((object)"[SaveTransfer] Load started, but the world still did not come up within 60 s"); } } private static int AnimsPlaying(FieldInfo f, StartMenu menu) { try { return (f != null) ? ((int)f.GetValue(menu)) : 0; } catch { return 0; } } public void Reset() { _chunks = null; _receiving = false; _receivedChunks = 0; _expectedChunks = 0; _totalBytes = 0; } } public sealed class ShipyardSync { private readonly CoopNet _net; public static ShipyardSync Instance { get; private set; } public ShipyardSync(CoopNet net) { _net = net; Instance = this; } public void NotifyPurchase(int sceneIndex) { if (_net.State == LinkState.Connected && sceneIndex >= 0) { _net.Broadcast(new BoatPurchaseMsg { SceneIndex = sceneIndex }, (DeliveryMethod)2); Plugin.Logger.LogInfo((object)("[ShipyardSync] out boat purchase sceneIndex=" + sceneIndex)); } } public void OnBoatPurchase(BoatPurchaseMsg msg, NetPeer fromPeer) { try { SaveLoadManager instance = SaveLoadManager.instance; SaveableObject[] array = (((Object)(object)instance != (Object)null) ? instance.GetCurrentObjects() : null); if (array == null || msg.SceneIndex < 0 || msg.SceneIndex >= array.Length) { Plugin.Logger.LogWarning((object)("[ShipyardSync] in purchase: missing object sceneIndex=" + msg.SceneIndex)); return; } SaveableObject val = array[msg.SceneIndex]; PurchasableBoat val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); if ((Object)(object)val2 != (Object)null) { val2.LoadAsPurchased(); Plugin.Logger.LogInfo((object)("[ShipyardSync] in boat purchase sceneIndex=" + msg.SceneIndex + " marked")); } else { if ((Object)(object)val != (Object)null) { val.extraSetting = true; } Plugin.Logger.LogInfo((object)("[ShipyardSync] in purchase sceneIndex=" + msg.SceneIndex + " (extraSetting directly)")); } if (_net.Role == Role.Host) { _net.Broadcast(new BoatPurchaseMsg { SceneIndex = msg.SceneIndex }, (DeliveryMethod)2); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ShipyardSync] OnBoatPurchase: " + ex.Message)); } } } public static class ShipyardPatches { public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony); Plugin.Logger.LogInfo((object)("[ShipyardPatches] Boat purchase patch: PurchaseBoat=" + flag)); PatchHealth.Report("Shipyard", flag ? 1 : 0, 1); } private static bool TryPatch(Harmony harmony) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("PurchasableBoat"); if (type == null) { return false; } MethodInfo methodInfo = AccessTools.Method(type, "PurchaseBoat", (Type[])null, (Type[])null); if (methodInfo == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(ShipyardPatches).GetMethod("PostPurchase", BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ShipyardPatches] PurchaseBoat: " + ex.Message)); return false; } } private static void PostPurchase(PurchasableBoat __instance) { try { if (ShipyardSync.Instance != null && !((Object)(object)__instance == (Object)null)) { SaveableObject component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { ShipyardSync.Instance.NotifyPurchase(component.sceneIndex); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ShipyardPatches] PostPurchase: " + ex.Message)); } } } public static class ShopLocator { public const ushort NoShop = ushort.MaxValue; public static List FindKeepers() { List list = new List(); Shopkeeper[] array = Object.FindObjectsOfType(); foreach (Shopkeeper val in array) { if ((Object)(object)val != (Object)null) { list.Add(val); } } list.Sort((Shopkeeper a, Shopkeeper b) => string.CompareOrdinal(BoatLocator.PathOf(((Component)a).transform), BoatLocator.PathOf(((Component)b).transform))); return list; } public static Shopkeeper FindByIndex(ushort index) { if (index == ushort.MaxValue) { return null; } List list = FindKeepers(); if (index >= list.Count) { return null; } return list[index]; } public static ushort IndexOf(Shopkeeper keeper) { if ((Object)(object)keeper == (Object)null) { return ushort.MaxValue; } List list = FindKeepers(); for (int i = 0; i < list.Count; i++) { if ((Object)(object)list[i] == (Object)(object)keeper) { return (ushort)i; } } return ushort.MaxValue; } } public sealed class ShopSync { private readonly CoopNet _net; private string _last = "—"; public static ShopSync Instance { get; private set; } public string ShopText => _last; public ShopSync(CoopNet net) { _net = net; Instance = this; } public void OnBought(ShipItem item) { if (!((Object)(object)item == (Object)null)) { ItemSync.Instance?.NotifyClientAuthored(item); Remember("bought '" + item.name + "'"); } } public void OnSold(int instanceId, int prefabIndex) { ItemSync.Instance?.NotifySold(instanceId, prefabIndex); Remember("sold id=" + instanceId); } public void Clear() { _last = "—"; } private void Remember(string text) { _last = text; Plugin.Logger.LogInfo((object)("[ShopSync] " + text)); } } public static class ShopPatches { public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, "TryToSellItem", "PreBuy", "PostBuy"); bool flag2 = TryPatch(harmony, "TryToBuyItem", "PreSell", null); Plugin.Logger.LogInfo((object)("[ShopPatches] Shop patches (local money): buy(TryToSellItem)=" + flag + ", sell(TryToBuyItem)=" + flag2)); PatchHealth.Report("Shop", (flag ? 1 : 0) + (flag2 ? 1 : 0), 2); } private static bool TryPatch(Harmony harmony, string method, string prefixName, string postfixName) { //IL_004a: 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) try { MethodInfo method2 = typeof(Shopkeeper).GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(ShipItem) }, null); if (method2 == null) { return false; } HarmonyMethod val = ((prefixName == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ShopPatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic))); HarmonyMethod val2 = ((postfixName == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(ShopPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic))); harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ShopPatches] " + method + ": " + ex.Message)); return false; } } private static void PreBuy(ShipItem item, out bool __state) { __state = (Object)(object)item != (Object)null && item.sold; } private static void PostBuy(ShipItem item, bool __state) { try { if ((Object)(object)item != (Object)null && item.sold && !__state) { ShopSync.Instance?.OnBought(item); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ShopPatches] PostBuy: " + ex.Message)); } } private static void PreSell(ShipItem item) { try { if ((Object)(object)item == (Object)null) { return; } CrateInventory component = ((Component)item).GetComponent(); if (!((Object)(object)component != (Object)null) || component.containedItems == null || component.containedItems.Count <= 0) { SaveablePrefab component2 = ((Component)item).GetComponent(); if ((Object)(object)component2 != (Object)null) { ShopSync.Instance?.OnSold(component2.instanceId, component2.prefabIndex); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ShopPatches] PreSell: " + ex.Message)); } } } public sealed class SleepSync { private readonly CoopNet _net; private bool _clientAsleep; private float _safetyTimer; public const float MaxBlackoutSeconds = 45f; public const float FadeSeconds = 2.5f; public static SleepSync Instance { get; private set; } public string SleepText { get { if (_net.Role == Role.Host) { return "host"; } if (_net.Role != Role.Client) { return "—"; } if (!_clientAsleep) { return "—"; } try { return "sleep (slaved), energy " + PlayerNeeds.sleep.ToString("0") + "%"; } catch { return "sleep (slaved)"; } } } public SleepSync(CoopNet net) { _net = net; Instance = this; } public void BroadcastSleep(bool sleeping) { if (_net.Role == Role.Host && _net.State == LinkState.Connected) { _net.Broadcast(new SleepStateMsg { Sleeping = sleeping }, (DeliveryMethod)2); Plugin.Logger.LogInfo((object)("[SleepSync] out sleep=" + sleeping)); } } public void OnSleepState(SleepStateMsg msg, NetPeer fromPeer) { if (_net.Role == Role.Client) { Plugin.Logger.LogInfo((object)("[SleepSync] in sleep=" + msg.Sleeping)); SetClientAsleep(msg.Sleeping); } } public void Tick(float dt) { if (_net.Role == Role.Client && _clientAsleep) { _safetyTimer += dt; if (_safetyTimer > 45f) { Plugin.Logger.LogWarning((object)"[SleepSync] blackout timeout - restoring control"); SetClientAsleep(sleeping: false); } else { RestoreNeeds(dt); } } } private void RestoreNeeds(float dt) { try { if (!((Object)(object)PlayerNeeds.instance == (Object)null) && !((Object)(object)Sun.sun == (Object)null)) { float num = 1f; EnvironmentSync environmentSync = (((Object)(object)CoopBehaviour.Instance != (Object)null) ? CoopBehaviour.Instance.Env : null); if (environmentSync != null && environmentSync.WaveClockValid) { num = Mathf.Max(1f, environmentSync.HostTimeScale); } float num2 = dt * 8f * Sun.sun.timescale * num; if (PlayerNeeds.sleepDebt < 100f) { PlayerNeeds.sleepDebt = Mathf.Min(100f, PlayerNeeds.sleepDebt + num2); num2 *= 0.2f; } num2 += dt * Sun.sun.timescale * (5f + 15f * (PlayerNeeds.alcohol / 100f)); PlayerNeeds.sleep = Mathf.Min(100f, PlayerNeeds.sleep + num2); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SleepSync] restore: " + ex.Message)); } } private void SetClientAsleep(bool sleeping) { if (sleeping == _clientAsleep) { return; } _clientAsleep = sleeping; _safetyTimer = 0f; try { CoopBehaviour instance = CoopBehaviour.Instance; if ((Object)(object)instance != (Object)null) { ((MonoBehaviour)instance).StartCoroutine(Blackout.FadeTo(sleeping ? 1f : 0f, 2.5f)); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SleepSync] fade: " + ex.Message)); } try { Refs.SetPlayerControl(!sleeping); } catch (Exception ex2) { Plugin.Logger.LogWarning((object)("[SleepSync] control: " + ex2.Message)); } } public void Clear() { if (_clientAsleep) { try { CoopBehaviour instance = CoopBehaviour.Instance; if ((Object)(object)instance != (Object)null) { ((MonoBehaviour)instance).StartCoroutine(Blackout.FadeTo(0f, 0.5f)); } } catch { } try { Refs.SetPlayerControl(true); } catch { } } _clientAsleep = false; _safetyTimer = 0f; } } public static class SleepPatches { public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, "FallAsleep", "PostFallAsleep"); bool flag2 = TryPatch(harmony, "WakeUp", "PostWakeUp"); Plugin.Logger.LogInfo((object)("[SleepPatches] Sleep patches: FallAsleep=" + flag + ", WakeUp=" + flag2)); PatchHealth.Report("Sleep", (flag ? 1 : 0) + (flag2 ? 1 : 0), 2); } private static bool TryPatch(Harmony harmony, string method, string postfixName) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown try { MethodInfo method2 = typeof(Sleep).GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method2 == null) { return false; } HarmonyMethod val = new HarmonyMethod(typeof(SleepPatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SleepPatches] " + method + ": " + ex.Message)); return false; } } private static void PostFallAsleep() { try { SleepSync.Instance?.BroadcastSleep(sleeping: true); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SleepPatches] PostFallAsleep: " + ex.Message)); } } private static void PostWakeUp() { try { SleepSync.Instance?.BroadcastSleep(sleeping: false); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SleepPatches] PostWakeUp: " + ex.Message)); } } } public sealed class WeatherStormSync { private readonly CoopNet _net; private float _sendTimer; private static FieldInfo _fStorms; public float StormHz = 2f; public WeatherStormSync(CoopNet net) { _net = net; } public void Tick(float dt) { //IL_00cc: 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_00be: 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 (_net.Role != Role.Host || _net.State != LinkState.Connected || !CoordSpace.Ready) { return; } float num = 1f / Mathf.Max(0.5f, StormHz); _sendTimer += dt; if (_sendTimer < num) { return; } _sendTimer = 0f; WanderingStorm[] storms = GetStorms(); if (storms != null && storms.Length != 0 && storms.Length <= 255) { StormStateMsg stormStateMsg = new StormStateMsg { Pos = (Vector3[])(object)new Vector3[storms.Length], Active = new bool[storms.Length], Distance = WeatherStorms.currentStormDistance }; for (int i = 0; i < storms.Length; i++) { WanderingStorm val = storms[i]; stormStateMsg.Pos[i] = (((Object)(object)val != (Object)null) ? CoordSpace.LocalToReal(((Component)val).transform.position) : Vector3.zero); stormStateMsg.Active[i] = (Object)(object)val != (Object)null && val.active; } _net.Broadcast(stormStateMsg, (DeliveryMethod)4); } } public void OnStormState(StormStateMsg msg, NetPeer fromPeer) { //IL_0050: 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) if (_net.Role != Role.Client || !CoordSpace.Ready) { return; } WanderingStorm[] storms = GetStorms(); if (storms == null) { return; } int num = Mathf.Min(storms.Length, msg.Pos.Length); for (int i = 0; i < num; i++) { WanderingStorm val = storms[i]; if (!((Object)(object)val == (Object)null)) { ((Component)val).transform.position = CoordSpace.RealToLocal(msg.Pos[i]); val.active = msg.Active[i]; } } WeatherStorms.currentStormDistance = msg.Distance; } private static WanderingStorm[] GetStorms() { try { WeatherStorms instance = WeatherStorms.instance; if ((Object)(object)instance == (Object)null) { return null; } if (_fStorms == null) { _fStorms = typeof(WeatherStorms).GetField("storms", BindingFlags.Instance | BindingFlags.NonPublic); } return (_fStorms != null) ? (_fStorms.GetValue(instance) as WanderingStorm[]) : null; } catch { return null; } } } public sealed class WindTotemSync { private readonly CoopNet _net; private GoPointer _gp; private FieldInfo _fHeldItem; private float _sendTimer; private bool _active; private Vector3 _lastWind; public float SendHz = 15f; public bool Active => _active; public Vector3 LastWind => _lastWind; public WindTotemSync(CoopNet net) { _net = net; } public void Tick(float dt) { //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_008c: 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_00a5: 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) if (_net.Role != Role.Client || _net.State != LinkState.Connected) { _active = false; return; } WindTotemOrb val = HeldOrb(); if ((Object)(object)val == (Object)null || (Object)(object)val.totem == (Object)null) { _active = false; return; } float num = 1f / Mathf.Max(1f, SendHz); _sendTimer += dt; if (!(_sendTimer < num)) { _sendTimer = 0f; Vector3 wind = (_lastWind = ComputeOrbWind(val)); _active = true; _net.Broadcast(new WindRequestMsg { Wind = wind }, (DeliveryMethod)4); } } public void OnWindRequest(WindRequestMsg msg, NetPeer fromPeer) { //IL_0021: 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_0032: Unknown result type (might be due to invalid IL or missing references) if (_net.Role == Role.Host) { Wind instance = Wind.instance; if (!((Object)(object)instance == (Object)null)) { instance.ForceNewWind(msg.Wind); _lastWind = msg.Wind; } } } public void Clear() { //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) _gp = null; _fHeldItem = null; _sendTimer = 0f; _active = false; _lastWind = Vector3.zero; } private static Vector3 ComputeOrbWind(WindTotemOrb orb) { //IL_0039: 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_007b: 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) Wind instance = Wind.instance; float num = (((Object)(object)instance != (Object)null) ? instance.minimumMagnitude : 0f); float num2 = (((Object)(object)instance != (Object)null) ? instance.maximumMagnitude : 1f); float num3 = Vector3.Distance(((Component)orb).transform.position, orb.totem.position); float num4 = ((orb.maxCarryDistance > 0.0001f) ? (num3 / orb.maxCarryDistance) : 0f); float num5 = Mathf.Lerp(num, num2, num4); return orb.totem.forward * num5; } private WindTotemOrb HeldOrb() { PickupableItem obj = HeldItem(); return (WindTotemOrb)(object)((obj is WindTotemOrb) ? obj : null); } private PickupableItem HeldItem() { try { if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } if ((Object)(object)_gp == (Object)null) { return null; } if (_fHeldItem == null) { _fHeldItem = typeof(GoPointer).GetField("heldItem", BindingFlags.Instance | BindingFlags.NonPublic); } return (PickupableItem)((_fHeldItem != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } } } namespace SailwindCoop.Runtime { public sealed class CoopBehaviour : MonoBehaviour { private DebugOverlay _overlay; private bool _overlayVisible; private DebugPanel _debugPanel; private AvatarSelectUI _avatarUI; private CoopMenuUI _menuUI; private Harmony _harmony; private bool _clientProfileSavedOnShutdown; public static CoopBehaviour Instance { get; private set; } public CoopNet Net { get; private set; } public PlayerSync Players { get; private set; } public BoatSync Boats { get; private set; } public EnvironmentSync Env { get; private set; } public ControlsSync Controls { get; private set; } public AnchorSync Anchor { get; private set; } public MooringSync Mooring { get; private set; } public BoatDamageSync Damage { get; private set; } public LightSync Lights { get; private set; } public ItemSync Items { get; private set; } public InteractionSync Interactions { get; private set; } public WindTotemSync WindTotem { get; private set; } public ShopSync Shop { get; private set; } public WeatherStormSync Storms { get; private set; } public SleepSync Sleep { get; private set; } public MissionSync Missions { get; private set; } public ShipyardSync Shipyard { get; private set; } public SaveTransferSync SaveTransfer { get; private set; } public JoinPause Pause { get; private set; } public bool OverlayVisible { get { return _overlayVisible; } set { _overlayVisible = value; } } private void Awake() { //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Expected O, but got Unknown Instance = this; Net = new CoopNet(delegate(string m) { Plugin.Logger.LogInfo((object)m); }) { ModVersion = "0.1.3", PlayerName = Plugin.Cfg.PlayerName.Value, WorldIdProvider = () => "", MaxClients = Plugin.Cfg.MaxClients.Value, DisconnectTimeoutMs = Plugin.Cfg.DisconnectTimeoutMs.Value, UpdateTimeMs = Plugin.Cfg.UpdateTimeMs.Value, PingIntervalMs = Plugin.Cfg.PingIntervalMs.Value, ListenIp = Plugin.Cfg.ListenIp.Value, ConnectAttempts = Plugin.Cfg.ConnectAttempts.Value, ReconnectDelayMs = Plugin.Cfg.ReconnectDelayMs.Value }; Players = new PlayerSync(Net) { InterpDelayMs = Plugin.Cfg.InterpDelayMs.Value, SnapshotHz = Plugin.Cfg.SnapshotHz.Value }; Boats = new BoatSync(Net) { InterpDelayMs = Plugin.Cfg.InterpDelayMs.Value, SnapshotHz = Plugin.Cfg.SnapshotHz.Value }; Env = new EnvironmentSync(Net); Controls = new ControlsSync(Net); Anchor = new AnchorSync(Net); Mooring = new MooringSync(Net); Damage = new BoatDamageSync(Net); Lights = new LightSync(Net); Items = new ItemSync(Net); Interactions = new InteractionSync(Net); WindTotem = new WindTotemSync(Net); Shop = new ShopSync(Net); Storms = new WeatherStormSync(Net); Sleep = new SleepSync(Net); Missions = new MissionSync(Net); Shipyard = new ShipyardSync(Net); SaveTransfer = new SaveTransferSync(Net) { CoopSlot = Plugin.Cfg.CoopSaveSlot.Value }; Pause = new JoinPause(); _harmony = new Harmony("com.sailwind.coop"); try { InteractionPatches.Apply(_harmony); MooringPatches.Apply(_harmony); BoatDamagePatches.Apply(_harmony); LightPatches.Apply(_harmony); ItemPatches.Apply(_harmony); ShopPatches.Apply(_harmony); SavePatches.Apply(_harmony); SleepPatches.Apply(_harmony); MissionPatches.Apply(_harmony); ShipyardPatches.Apply(_harmony); OceanPatches.Apply(_harmony); } catch (Exception ex) { Plugin.Logger.LogError((object)("[Coop] Failed to apply Harmony patches: " + ex)); } Net.OnAccepted += delegate(HelloAckMsg ack) { Plugin.Logger.LogInfo((object)("[Coop] Connection accepted, NetId=" + ack.AssignedNetId)); }; Net.OnClientReady += delegate(PeerSession s) { Plugin.Logger.LogInfo((object)("[Coop] Client ready: " + s.PlayerName + ", avatar=" + (string.IsNullOrEmpty(s.SelectedAvatar) ? "(default)" : s.SelectedAvatar))); Players.RegisterRemoteAvatarFile(s.PlayerNetId, s.SelectedAvatar); if (Plugin.Cfg.PauseHostOnJoin.Value && GameState.playing) { Pause.Hold(s.PlayerNetId); } ((MonoBehaviour)this).StartCoroutine(StreamSaveToClient(s.Peer, s.PlayerNetId)); }; Net.OnGameMessage += OnGameMessage; Net.OnPlayerLeft += delegate(uint netId) { Players.RemoveRemote(netId); Items.ClearRemoteActor(netId); Damage.ClearRemoteActor(netId); Pause.Release(netId); }; AvatarCatalog.OnSelectionChanged += delegate(string newFile) { if (Net.State == LinkState.Connected) { Net.SendAvatarChange(newFile); } }; _overlay = new DebugOverlay(Net); _debugPanel = new DebugPanel(Net); _avatarUI = new AvatarSelectUI(AvatarCatalog.CurrentSelection); _menuUI = new CoopMenuUI(this, Net); } private void Update() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(Plugin.Cfg.MenuKey.Value)) { _menuUI.Toggle(); } Net.PollEvents(); Pause.Tick(); float deltaTime = Time.deltaTime; Boats.Tick(deltaTime); Boats.ApplyRemote(); Env.Tick(deltaTime); Storms.Tick(deltaTime); Sleep.Tick(deltaTime); Missions.Tick(deltaTime); Controls.Tick(deltaTime); Controls.ApplyClient(deltaTime); Anchor.Tick(deltaTime); Anchor.ApplyRemote(); Mooring.Tick(deltaTime); Damage.Tick(deltaTime); Lights.Tick(deltaTime); Items.Tick(deltaTime); Items.ApplyRemote(); WindTotem.Tick(deltaTime); Interactions.Tick(deltaTime); Players.Tick(deltaTime); Players.ApplyRemotes(); } private void OnGameMessage(MsgType type, INetMessage msg, NetPeer fromPeer) { switch (type) { case MsgType.PlayerState: Players.OnPlayerState((PlayerStateMsg)msg, fromPeer); break; case MsgType.BoatState: Boats.OnBoatState((BoatStateMsg)msg, fromPeer); break; case MsgType.EnvState: Env.OnEnvState((EnvStateMsg)msg, fromPeer); break; case MsgType.ControlState: Controls.OnControlState((ControlStateMsg)msg, fromPeer); break; case MsgType.AnchorState: Anchor.OnAnchorState((AnchorStateMsg)msg, fromPeer); break; case MsgType.MooringState: Mooring.OnMooringState((MooringStateMsg)msg, fromPeer); break; case MsgType.BoatDamageState: Damage.OnDamageState((BoatDamageStateMsg)msg, fromPeer); break; case MsgType.MooringRequest: Mooring.OnMooringRequest((MooringRequestMsg)msg, fromPeer); break; case MsgType.SteerRequest: Controls.OnSteerRequest((SteerRequestMsg)msg, fromPeer); break; case MsgType.ControlRequest: Controls.OnControlRequest((ControlRequestMsg)msg, fromPeer); break; case MsgType.ControlEvent: Interactions.OnControlEvent((ControlEventMsg)msg, fromPeer); break; case MsgType.HoldRequest: Interactions.OnHoldRequest((HoldRequestMsg)msg, fromPeer); break; case MsgType.DamageRequest: Damage.OnDamageRequest((DamageRequestMsg)msg, fromPeer); break; case MsgType.PushRequest: Interactions.OnPushRequest((PushRequestMsg)msg, fromPeer); break; case MsgType.LightState: Lights.OnLightState((LightStateMsg)msg, fromPeer); break; case MsgType.LightRequest: Lights.OnLightRequest((LightRequestMsg)msg, fromPeer); break; case MsgType.ItemState: Items.OnItemState((ItemStateMsg)msg, fromPeer); break; case MsgType.ItemRequest: Items.OnItemRequest((ItemRequestMsg)msg, fromPeer); break; case MsgType.SpawnObject: Items.OnSpawnObject((SpawnObjectMsg)msg, fromPeer); break; case MsgType.DespawnObject: Items.OnDespawnObject((DespawnObjectMsg)msg, fromPeer); break; case MsgType.ItemExtra: Items.OnItemExtraState((ItemExtraStateMsg)msg, fromPeer); break; case MsgType.WindRequest: WindTotem.OnWindRequest((WindRequestMsg)msg, fromPeer); break; case MsgType.FishCatch: Items.OnFishCatch((FishCatchMsg)msg, fromPeer); break; case MsgType.RodState: Items.OnRodState((RodStateMsg)msg, fromPeer); break; case MsgType.StormState: Storms.OnStormState((StormStateMsg)msg, fromPeer); break; case MsgType.SleepState: Sleep.OnSleepState((SleepStateMsg)msg, fromPeer); break; case MsgType.MissionJournal: Missions.OnMissionJournal((MissionJournalMsg)msg, fromPeer); break; case MsgType.MissionReward: Missions.OnMissionReward((MissionRewardMsg)msg, fromPeer); break; case MsgType.MissionAccept: Missions.OnMissionAccept((MissionAcceptMsg)msg, fromPeer); break; case MsgType.MissionAbandon: Missions.OnMissionAbandon((MissionAbandonMsg)msg, fromPeer); break; case MsgType.BoatPurchase: Shipyard.OnBoatPurchase((BoatPurchaseMsg)msg, fromPeer); break; case MsgType.AvatarChange: HandleAvatarChange((AvatarChangeMsg)msg, fromPeer); break; case MsgType.SaveSnapshotBegin: SaveTransfer.OnBegin((SaveSnapshotBeginMsg)msg); break; case MsgType.SaveSnapshotChunk: SaveTransfer.OnChunk((SaveSnapshotChunkMsg)msg); break; case MsgType.SaveSnapshotEnd: SaveTransfer.OnEnd((SaveSnapshotEndMsg)msg); break; case MsgType.ClientWorldLoaded: if (Net.Role == Role.Host) { uint netId = Net.PlayerNetIdForPeer(fromPeer); Plugin.Logger.LogInfo((object)("[Coop] Client NetId=" + netId + " loaded world: " + (((ClientWorldLoadedMsg)msg).Ok ? "ok" : "with error"))); Pause.Release(netId); } break; case (MsgType)22: case (MsgType)23: case (MsgType)24: case (MsgType)25: case (MsgType)26: case (MsgType)27: case (MsgType)28: case (MsgType)29: case (MsgType)37: case (MsgType)38: case (MsgType)39: case (MsgType)41: case (MsgType)42: case (MsgType)43: case (MsgType)44: case (MsgType)45: case (MsgType)46: case (MsgType)47: case (MsgType)48: case (MsgType)49: case MsgType.InteractRequest: case (MsgType)61: case MsgType.ShopRequest: case MsgType.ShopResult: case MsgType.CargoResult: break; } } private IEnumerator StreamSaveToClient(NetPeer peer, uint netId) { yield return null; if (!GameState.playing) { Plugin.Logger.LogError((object)"[Coop] Host is not in-game (save not loaded) - world was not sent to client. Load a save before accepting clients."); Pause.Release(netId); yield break; } if (Plugin.Cfg.ForceHostSaveOnJoin.Value && (Object)(object)SaveLoadManager.instance != (Object)null) { bool started = false; float t = 0f; while (!started && t < 15f) { if (SaveLoadManager.readyToSave && !SaveTransferSync.HostSaveBusy() && !Object.op_Implicit((Object)(object)GameState.inBed) && !Object.op_Implicit((Object)(object)GameState.currentShipyard)) { try { SaveLoadManager.instance.SaveGame(true); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[Coop] Forced host save failed: " + ex.Message)); break; } started = SaveTransferSync.HostSaveBusy(); } if (!started) { yield return null; } t += Time.unscaledDeltaTime; } if (started) { t = 0f; while (SaveTransferSync.HostSaveBusy() && t < 10f) { yield return null; t += Time.unscaledDeltaTime; } yield return (object)new WaitForEndOfFrame(); } else { Plugin.Logger.LogWarning((object)"[Coop] Timed out waiting for a fresh save window - client will receive the last save from disk"); } } byte[] array = SaveTransferSync.ReadHostSaveBytes(); if (array == null) { Plugin.Logger.LogError((object)"[Coop] No host save available to send to client"); Pause.Release(netId); } else if (peer == null || (int)peer.ConnectionState != 4) { Plugin.Logger.LogWarning((object)"[Coop] Client disconnected before save transfer"); Pause.Release(netId); } else { SaveTransfer.SendSaveTo(peer, array); } } private void HandleAvatarChange(AvatarChangeMsg msg, NetPeer fromPeer) { Plugin.Logger.LogInfo((object)("[Coop] AvatarChange NetId=" + msg.NetId + " -> '" + msg.BundleFile + "'")); Players.ApplyAvatarChange(msg.NetId, msg.BundleFile); } private void OnGUI() { if (_menuUI != null) { _menuUI.Draw(); } if (_overlayVisible) { _overlay.Draw(); } if (Plugin.Cfg.EnableDebugPanel.Value) { _debugPanel.Draw(); } if (_avatarUI != null) { _avatarUI.Draw(); } } private void OnDestroy() { SaveClientProfileBeforeStop("destroy"); Missions?.Clear(); Sleep?.Clear(); Shop?.Clear(); WindTotem?.Clear(); Interactions?.Clear(); Items?.Clear(); Lights?.Clear(); Damage?.Clear(); Mooring?.Clear(); Anchor?.Clear(); Controls?.Clear(); Env?.Clear(); Boats?.Clear(); Players?.Clear(); Pause?.Clear(); Net?.Stop(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void OnApplicationQuit() { SaveClientProfileBeforeStop("quit"); Net?.Stop(); } private void SaveClientProfileBeforeStop(string reason) { if (_clientProfileSavedOnShutdown) { return; } try { if (Net != null && Net.Role == Role.Client && Net.State == LinkState.Connected && CoopProfile.SaveFromGame()) { _clientProfileSavedOnShutdown = true; Plugin.Logger.LogInfo((object)("[Coop] Client profile saved before session stop: " + reason)); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[Coop] Failed to save client profile before stop: " + ex.Message)); } } public void ToggleAvatarMenu() { if (_avatarUI == null) { _avatarUI = new AvatarSelectUI(AvatarCatalog.CurrentSelection); } _avatarUI.Visible = !_avatarUI.Visible; if (_avatarUI.Visible) { AvatarCatalog.Scan(); } } public void ToggleDebugPanel() { if (Plugin.Cfg.EnableDebugPanel.Value) { _debugPanel.Visible = !_debugPanel.Visible; } } public void CloseCompanionMenus() { if (_avatarUI != null) { _avatarUI.Visible = false; } if (_debugPanel != null) { _debugPanel.Visible = false; } } public void StartHostSession(int port) { Plugin.Logger.LogInfo((object)"[Coop] Starting host via UI"); Net.StartHost(port); } public void StartClientSession(string ip, int port) { Plugin.Logger.LogInfo((object)("[Coop] Joining via UI to " + ip)); _clientProfileSavedOnShutdown = false; Net.StartClient(ip, port); } public void DisconnectSession(string reason) { Plugin.Logger.LogInfo((object)("[Coop] Disconnect via UI: " + reason)); SaveClientProfileBeforeStop("disconnect:" + reason); SaveTransfer.Reset(); Pause.Clear(); Net.Stop(); Missions.Clear(); Sleep.Clear(); Shop.Clear(); WindTotem.Clear(); Interactions.Clear(); Items.Clear(); Lights.Clear(); Damage.Clear(); Mooring.Clear(); Anchor.Clear(); Controls.Clear(); Env.Clear(); Boats.Clear(); Players.Clear(); } } public sealed class CoopMenuUI { private const float ButtonWidth = 116f; private const float ButtonHeight = 30f; private readonly CoopBehaviour _coop; private readonly CoopNet _net; private bool _visible; private bool _cursorCaptured; private bool _previousCursorVisible; private bool _previousMouseLookEnabled; private bool _previousInCursorMenu; private CursorLockMode _previousLockState; private string _joinIp; private string _port; private string _playerName; private string _status = ""; private GUIStyle _window; private GUIStyle _title; private GUIStyle _label; private GUIStyle _muted; private GUIStyle _button; private GUIStyle _dangerButton; private GUIStyle _smallButton; private GUIStyle _textField; private GUIStyle _pill; private GUIStyle _backdrop; private Texture2D _backdropTex; private Texture2D _shadowTex; private Texture2D _windowTex; private Texture2D _borderTex; public bool Visible { get { return _visible; } set { SetVisible(value); } } public CoopMenuUI(CoopBehaviour coop, CoopNet net) { _coop = coop; _net = net; _joinIp = Plugin.Cfg.JoinIp.Value; _port = Plugin.Cfg.Port.Value.ToString(); _playerName = Plugin.Cfg.PlayerName.Value; } public void Toggle() { SetVisible(!_visible); } public void Draw() { EnsureStyles(); if (_visible) { ApplyCursorState(); DrawWindow(); } } private void DrawWindow() { //IL_005a: 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) float num = 430f; float num2 = 382f; float num3 = Mathf.Clamp((float)Screen.width - num - 18f, 10f, (float)Screen.width - num - 10f); float num4 = 60f; Rect val = default(Rect); ((Rect)(ref val))..ctor(num3, num4, num, num2); GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)_backdropTex, (ScaleMode)0); GUI.DrawTexture(new Rect(((Rect)(ref val)).x + 8f, ((Rect)(ref val)).y + 8f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), (Texture)(object)_shadowTex, (ScaleMode)0); GUI.DrawTexture(new Rect(((Rect)(ref val)).x - 2f, ((Rect)(ref val)).y - 2f, ((Rect)(ref val)).width + 4f, ((Rect)(ref val)).height + 4f), (Texture)(object)_borderTex, (ScaleMode)0); GUI.DrawTexture(val, (Texture)(object)_windowTex, (ScaleMode)0); GUI.Box(val, GUIContent.none, _window); GUILayout.BeginArea(new Rect(num3 + 14f, num4 + 12f, num - 28f, num2 - 24f)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Sailwind Co-op", _title, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label(StateText(), _pill, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(128f), GUILayout.Height(24f) }); GUILayout.EndHorizontal(); GUILayout.Space(8f); DrawIdentity(); GUILayout.Space(8f); DrawConnection(); GUILayout.Space(10f); DrawActions(); GUILayout.Space(10f); DrawTools(); GUILayout.FlexibleSpace(); if (!string.IsNullOrEmpty(_net.LastError)) { GUILayout.Label(_net.LastError, _muted, Array.Empty()); } if (!string.IsNullOrEmpty(_status)) { GUILayout.Label(_status, _muted, Array.Empty()); } GUILayout.EndArea(); } private void DrawIdentity() { GUILayout.Label("Player", _label, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Name", _muted, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(72f) }); _playerName = GUILayout.TextField(_playerName, _textField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }); if (GUILayout.Button("Apply", _smallButton, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(26f) })) { string text = (string.IsNullOrWhiteSpace(_playerName) ? "Player" : _playerName.Trim()); Plugin.Cfg.PlayerName.Value = text; _net.PlayerName = text; _playerName = text; _status = "Player name updated"; } GUILayout.EndHorizontal(); } private void DrawConnection() { GUILayout.Label("Connection", _label, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Host IP", _muted, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(72f) }); _joinIp = GUILayout.TextField(_joinIp, _textField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }); GUILayout.Label("Port", _muted, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(38f) }); _port = GUILayout.TextField(_port, _textField, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(66f), GUILayout.Height(26f) }); GUILayout.EndHorizontal(); if (_net.Role == Role.Client || _net.State == LinkState.Connecting || _net.State == LinkState.Handshaking) { GUILayout.Label("Target: " + _joinIp + ":" + _port, _muted, Array.Empty()); } else if (_net.Role == Role.Host) { GUILayout.Label("Hosting on port " + Plugin.Cfg.Port.Value + "; clients: " + _net.PeerCount, _muted, Array.Empty()); } else { GUILayout.Label("Load a world, then host a session or join a host.", _muted, Array.Empty()); } } private void DrawActions() { GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = _net.State != LinkState.Connecting && _net.State != LinkState.Handshaking; if (GUILayout.Button("Host", _button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(116f), GUILayout.Height(30f) })) { StartHost(); } if (GUILayout.Button("Join", _button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(116f), GUILayout.Height(30f) })) { Join(); } GUI.enabled = _net.State != LinkState.Idle || _net.Role != Role.None; if (GUILayout.Button("Disconnect", _dangerButton, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(116f), GUILayout.Height(30f) })) { _coop.DisconnectSession("menu"); _status = "Session stopped"; } GUI.enabled = true; GUILayout.EndHorizontal(); } private void DrawTools() { GUILayout.Label("Tools", _label, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Avatar", _button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(116f), GUILayout.Height(30f) })) { AvatarCatalog.Scan(); _coop.ToggleAvatarMenu(); } if (GUILayout.Button(_coop.OverlayVisible ? "Hide Status" : "Show Status", _button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(116f), GUILayout.Height(30f) })) { _coop.OverlayVisible = !_coop.OverlayVisible; } GUI.enabled = Plugin.Cfg.EnableDebugPanel.Value; if (GUILayout.Button("Debug", _button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(116f), GUILayout.Height(30f) })) { _coop.ToggleDebugPanel(); } GUI.enabled = true; GUILayout.EndHorizontal(); if (!Plugin.Cfg.EnableDebugPanel.Value) { GUILayout.Label("Debug tools are disabled in public mode.", _muted, Array.Empty()); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Model: " + AvatarCatalog.DisplayNameFor(AvatarCatalog.CurrentSelection), _muted, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Close", _smallButton, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(26f) })) { SetVisible(visible: false); } GUILayout.EndHorizontal(); } private void StartHost() { if (TryApplyConnectionFields(out var port)) { _coop.StartHostSession(port); _status = "Host started"; } } private void Join() { if (TryApplyConnectionFields(out var port)) { _coop.StartClientSession(_joinIp.Trim(), port); _status = "Connecting to " + _joinIp.Trim() + ":" + port; } } private bool TryApplyConnectionFields(out int port) { port = 0; string text = (string.IsNullOrWhiteSpace(_joinIp) ? "127.0.0.1" : _joinIp.Trim()); if (!int.TryParse(_port, out port) || port < 1 || port > 65535) { _status = "Invalid port"; return false; } string text2 = (string.IsNullOrWhiteSpace(_playerName) ? "Player" : _playerName.Trim()); Plugin.Cfg.JoinIp.Value = text; Plugin.Cfg.Port.Value = port; Plugin.Cfg.PlayerName.Value = text2; _net.PlayerName = text2; _joinIp = text; _port = port.ToString(); _playerName = text2; return true; } private string StateText() { switch (_net.State) { case LinkState.Idle: return "offline"; case LinkState.Connecting: return "connecting"; case LinkState.Handshaking: return "handshake"; case LinkState.Connected: if (_net.Role != Role.Host) { return "client"; } return "host"; case LinkState.Rejected: return "rejected"; case LinkState.Failed: return "failed"; default: return _net.State.ToString(); } } private void SetVisible(bool visible) { if (_visible != visible) { if (!visible) { _coop.CloseCompanionMenus(); } _visible = visible; ApplyCursorState(); } } private void ApplyCursorState() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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) if (_visible) { if (!_cursorCaptured) { _previousCursorVisible = Cursor.visible; _previousLockState = Cursor.lockState; _previousMouseLookEnabled = MouseLook.MouseLookIsEnabled(); _previousInCursorMenu = GameState.inCursorMenu; _cursorCaptured = true; } MouseLook.ToggleMouseLookAndCursor(false); MouseLook.ToggleMouseLook(false); Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; GameState.inCursorMenu = true; } else if (_cursorCaptured) { if (GameState.playing && !GameState.currentlyLoading) { MouseLook.ToggleMouseLookAndCursor(true); MouseLook.ToggleMouseLook(true); GameState.inCursorMenu = false; } else if (_previousCursorVisible || (int)_previousLockState == 0 || _previousInCursorMenu) { Cursor.visible = _previousCursorVisible; Cursor.lockState = _previousLockState; GameState.inCursorMenu = _previousInCursorMenu; MouseLook.ToggleMouseLook(_previousMouseLookEnabled); } else { MouseLook.ToggleMouseLookAndCursor(true); MouseLook.ToggleMouseLook(_previousMouseLookEnabled); GameState.inCursorMenu = _previousInCursorMenu; } _cursorCaptured = false; } } private Texture2D MakeBg(Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_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_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, c); val.Apply(); return val; } private void EnsureStyles() { //IL_001f: 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_0069: 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_00b2: 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_00f2: 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_0132: 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_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_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected O, but got Unknown //IL_01a2: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Expected O, but got Unknown //IL_01e6: Expected O, but got Unknown //IL_01f1: 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_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0205: 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_0229: Expected O, but got Unknown //IL_0234: 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_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0248: 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_026c: Expected O, but got Unknown //IL_0277: 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_0284: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Expected O, but got Unknown //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0315: 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_0336: Expected O, but got Unknown //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0389: 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_039b: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Expected O, but got Unknown //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Expected O, but got Unknown //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Expected O, but got Unknown //IL_0425: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_0446: Unknown result type (might be due to invalid IL or missing references) //IL_045b: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Expected O, but got Unknown if (_window == null) { _backdropTex = MakeBg(new Color(0f, 0f, 0f, 0.62f)); _shadowTex = MakeBg(new Color(0f, 0f, 0f, 0.72f)); _windowTex = MakeBg(new Color(0.025f, 0.022f, 0.018f, 0.98f)); _borderTex = MakeBg(new Color(0.62f, 0.48f, 0.3f, 0.95f)); Texture2D background = MakeBg(new Color(0.32f, 0.24f, 0.16f, 0.96f)); Texture2D background2 = MakeBg(new Color(0.42f, 0.31f, 0.2f, 0.98f)); Texture2D background3 = MakeBg(new Color(0.4f, 0.14f, 0.11f, 0.96f)); Texture2D background4 = MakeBg(new Color(0.52f, 0.18f, 0.14f, 0.98f)); Texture2D background5 = MakeBg(new Color(0.08f, 0.075f, 0.06f, 0.95f)); Texture2D background6 = MakeBg(new Color(0.13f, 0.2f, 0.18f, 0.95f)); GUIStyle val = new GUIStyle(GUI.skin.box); val.normal.background = null; val.border = new RectOffset(8, 8, 8, 8); val.padding = new RectOffset(12, 12, 12, 12); _window = val; GUIStyle val2 = new GUIStyle(GUI.skin.box); val2.normal.background = _backdropTex; val2.border = new RectOffset(0, 0, 0, 0); val2.padding = new RectOffset(0, 0, 0, 0); _backdrop = val2; GUIStyle val3 = new GUIStyle(GUI.skin.label) { fontSize = 20, fontStyle = (FontStyle)1 }; val3.normal.textColor = new Color(0.96f, 0.88f, 0.72f); _title = val3; GUIStyle val4 = new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = (FontStyle)1 }; val4.normal.textColor = new Color(0.92f, 0.84f, 0.68f); _label = val4; GUIStyle val5 = new GUIStyle(GUI.skin.label) { fontSize = 12, wordWrap = true }; val5.normal.textColor = new Color(0.74f, 0.7f, 0.62f); _muted = val5; GUIStyle val6 = new GUIStyle(GUI.skin.button) { fontSize = 13, fontStyle = (FontStyle)1 }; val6.normal.background = background; val6.normal.textColor = new Color(0.98f, 0.92f, 0.8f); val6.hover.background = background2; val6.hover.textColor = Color.white; val6.active.background = background2; val6.active.textColor = Color.white; _button = val6; GUIStyle val7 = new GUIStyle(_button); val7.normal.background = background3; val7.normal.textColor = new Color(1f, 0.88f, 0.82f); val7.hover.background = background4; val7.hover.textColor = Color.white; val7.active.background = background4; val7.active.textColor = Color.white; _dangerButton = val7; _smallButton = new GUIStyle(_button) { fontSize = 12 }; GUIStyle val8 = new GUIStyle(GUI.skin.textField) { fontSize = 13 }; val8.normal.background = background5; val8.normal.textColor = Color.white; val8.focused.background = background5; val8.focused.textColor = Color.white; _textField = val8; GUIStyle val9 = new GUIStyle(GUI.skin.label) { fontSize = 12, alignment = (TextAnchor)4 }; val9.normal.background = background6; val9.normal.textColor = new Color(0.74f, 1f, 0.84f); _pill = val9; } } } public sealed class DebugOverlay { private readonly CoopNet _net; private GUIStyle _box; private GUIStyle _label; private static GoPointer _gp; private static FieldInfo _fPointed; private static FieldInfo _fHit; private static FieldInfo _fSticky; private static PlayerEmbarkerNew _emb; private static FieldInfo _fEmbarked; public DebugOverlay(CoopNet net) { _net = net; } public void Draw() { //IL_0021: 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) EnsureStyles(); Rect val = default(Rect); ((Rect)(ref val))..ctor(12f, 12f, 360f, 800f); GUI.Box(val, "Sailwind Co-op 0.1.3", _box); GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 26f, 340f, 766f)); Line("Role", _net.Role.ToString()); Line("State", StateText(_net.State)); if (_net.Role == Role.Client || _net.State == LinkState.Connected) { Line("RTT", _net.Clock.HasSample ? (_net.Clock.RttMs.ToString("0") + " ms") : "—"); Line("Clock offset", _net.Clock.HasSample ? (_net.Clock.OffsetMs.ToString("0") + " ms") : "—"); } if (_net.Role == Role.Host) { Line("Clients", _net.PeerCount.ToString()); } Line("Objects (NetId)", CountAll().ToString()); Line("Coordinates", CoordText()); Line("Patch health", PatchHealth.Summary); CoopBehaviour instance = CoopBehaviour.Instance; if ((Object)(object)instance != (Object)null && instance.Players != null && _net.State == LinkState.Connected) { float nearestRemoteDistance = instance.Players.NearestRemoteDistance; if (nearestRemoteDistance >= 0f) { Line("Avatar distance", nearestRemoteDistance.ToString("0.0") + " m"); } Line("Environment", EnvText()); if (_net.Role == Role.Client && instance.Env != null && instance.Env.WaveClockValid) { Line("Wave clock", EnvironmentSync.WaveClock.ToString("0.0") + " s, Δ " + (instance.Env.WaveClockError * 1000f).ToString("0") + " ms, host ts " + instance.Env.HostTimeScale.ToString("0.00")); } if (instance.Controls != null) { Line("ControlRequest", instance.Controls.LastControlRequestText); } } if (!string.IsNullOrEmpty(_net.LastError)) { Line("Error", _net.LastError); } GUILayout.EndArea(); } private static string InteractText() { //IL_00f3: 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) try { if ((Object)(object)_gp == (Object)null) { _gp = Object.FindObjectOfType(); } if ((Object)(object)_gp == (Object)null) { return "GoPointer not found"; } Type typeFromHandle = typeof(GoPointer); if (_fPointed == null) { _fPointed = typeFromHandle.GetField("pointedAtButton", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fHit == null) { _fHit = typeFromHandle.GetField("hit", BindingFlags.Instance | BindingFlags.NonPublic); } GoPointerButton val = (GoPointerButton)((_fPointed != null) ? /*isinst with value type is only supported in some contexts*/: null); string text = ((!((Object)(object)val != (Object)null)) ? "—" : (string.IsNullOrEmpty(val.lookText) ? ((Object)val).name : val.lookText)); if (_fHit != null && _fHit.GetValue(_gp) is RaycastHit val2 && (Object)(object)((RaycastHit)(ref val2)).collider != (Object)null) { _ = ((Object)((RaycastHit)(ref val2)).collider).name; } if (_fSticky == null) { _fSticky = typeFromHandle.GetField("stickyClickedButton", BindingFlags.Instance | BindingFlags.NonPublic); } string text2 = (((Object)((_fSticky != null) ? /*isinst with value type is only supported in some contexts*/: null) != (Object)null) ? "YES" : "—"); if ((Object)(object)_emb == (Object)null) { _emb = Object.FindObjectOfType(); } string text3 = "?"; if ((Object)(object)_emb != (Object)null) { if (_fEmbarked == null) { _fEmbarked = typeof(PlayerEmbarkerNew).GetField("embarked", BindingFlags.Instance | BindingFlags.NonPublic); } if (_fEmbarked != null) { text3 = ((bool)_fEmbarked.GetValue(_emb)).ToString(); } } return "btn:" + text + " holding:" + text2 + " emb:" + text3; } catch (Exception ex) { return "err " + ex.Message; } } private static string CoordText() { //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_0037: 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_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) try { FloatingOriginManager instance = FloatingOriginManager.instance; PlayerControllerMirror observerMirror = Refs.observerMirror; if ((Object)(object)instance == (Object)null || (Object)(object)observerMirror == (Object)null) { return "—"; } Vector3 globeCoords = instance.GetGlobeCoords(((Component)observerMirror).transform); string text = ((globeCoords.z < 0f) ? "S" : "N"); string text2 = ((globeCoords.x < 0f) ? "W" : "E"); return Mathf.Abs(globeCoords.z).ToString("0.00") + "° " + text + ", " + Mathf.Abs(globeCoords.x).ToString("0.00") + "° " + text2; } catch (Exception ex) { return "err " + ex.Message; } } private static string EnvText() { float magnitude = ((Vector3)(ref Wind.currentWind)).magnitude; string text = (((Object)(object)Sun.sun != (Object)null) ? Sun.sun.localTime.ToString("0.00") : "—"); return "wind " + magnitude.ToString("0.0") + ", t " + text; } private int CountAll() { int num = 0; foreach (NetEntry item in _net.Registry.All) { _ = item; num++; } return num; } private static string StateText(LinkState s) { return s switch { LinkState.Idle => "idle", LinkState.Connecting => "connecting...", LinkState.Handshaking => "handshaking...", LinkState.Connected => "connected", LinkState.Rejected => "rejected by host", LinkState.Failed => "failed", _ => s.ToString(), }; } private void Line(string key, string val) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(key + ":", _label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); GUILayout.Label(val, _label, Array.Empty()); GUILayout.EndHorizontal(); } private void EnsureStyles() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003e: 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) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (_box == null) { _box = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)0, fontStyle = (FontStyle)1, fontSize = 13 }; _box.normal.textColor = Color.white; } if (_label == null) { _label = new GUIStyle(GUI.skin.label) { fontSize = 12, wordWrap = true }; _label.normal.textColor = Color.white; } } } public sealed class DebugPanel { private readonly CoopNet _net; public bool Visible; private GUIStyle _box; private GUIStyle _label; private string _goldAmount = "10000"; private string _itemFilter = ""; private Vector2 _scroll; private string _status = "—"; private List> _prefabs; private PlayerEmbarkerNew _emb; private FieldInfo _fStorms; private List _recoveryPorts; private Vector2 _islandScroll; private FieldInfo _fEmbarked; public DebugPanel(CoopNet net) { _net = net; } public void Draw() { //IL_002a: 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) if (Visible) { EnsureStyles(); Rect val = default(Rect); ((Rect)(ref val))..ctor(12f, 12f, 380f, 820f); GUI.Box(val, "Debug Panel - Test Scenarios", _box); GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 28f, 360f, 784f)); string text = ((_net.State == LinkState.Connected) ? _net.Role.ToString() : "offline"); GUILayout.Label("Role: " + text + " · " + _status, _label, Array.Empty()); GUILayout.Space(4f); DrawGold(); GUILayout.Space(6f); DrawReputation(); GUILayout.Space(6f); DrawWorld(); GUILayout.Space(6f); DrawIslandTeleport(); GUILayout.Space(6f); DrawItemSpawn(); GUILayout.EndArea(); } } private void DrawGold() { GUILayout.Label("- Gold (local) -", _label, Array.Empty()); GUILayout.Label("Current: " + CurrentGoldText(), _label, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("+1000", Array.Empty())) { GiveGold(1000); } if (GUILayout.Button("+10000", Array.Empty())) { GiveGold(10000); } _goldAmount = GUILayout.TextField(_goldAmount, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); if (GUILayout.Button("Give", Array.Empty()) && int.TryParse(_goldAmount, out var result)) { GiveGold(result); } GUILayout.EndHorizontal(); } private static string CurrentGoldText() { try { if (PlayerGold.currency != null && PlayerGold.currency.Length != 0) { string[] array = new string[PlayerGold.currency.Length]; for (int i = 0; i < PlayerGold.currency.Length; i++) { array[i] = PlayerGold.GetCurrencySymbol(i) + PlayerGold.currency[i]; } return string.Join(" ", array); } return "gold=" + PlayerGold.gold; } catch (Exception ex) { return "?(" + ex.Message + ")"; } } private void GiveGold(int amount) { try { PlayerGold.gold += amount; if (PlayerGold.currency != null) { for (int i = 0; i < PlayerGold.currency.Length; i++) { PlayerGold.currency[i] += amount; } } _status = "given " + amount + " gold"; Plugin.Logger.LogInfo((object)("[DebugPanel] +" + amount + " gold (local)")); } catch (Exception ex) { _status = "gold: " + ex.Message; Plugin.Logger.LogWarning((object)("[DebugPanel] gold: " + ex)); } } private void DrawReputation() { GUILayout.Label("- Reputation (local) -", _label, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GiveRepButton("Al'Ankh", (PortRegion)0); GiveRepButton("Emerald", (PortRegion)1); GiveRepButton("Aestrin", (PortRegion)2); GUILayout.EndHorizontal(); } private unsafe void GiveRepButton(string title, PortRegion region) { //IL_0000: 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) int num; try { num = PlayerReputation.GetRepLevel(region); } catch { num = -1; } if (GUILayout.Button(title + " +100 (lvl " + num + ")", Array.Empty())) { try { PlayerReputation.ChangeReputation(100, region); _status = "+100 rep " + title; Plugin.Logger.LogInfo((object)("[DebugPanel] +100 reputation " + ((object)(*(PortRegion*)(®ion))/*cast due to .constrained prefix*/).ToString() + " (local)")); } catch (Exception ex) { _status = "rep: " + ex.Message; Plugin.Logger.LogWarning((object)("[DebugPanel] rep: " + ex)); } } } private void DrawWorld() { GUILayout.Label("- World: time / storm / teleport (host) -", _label, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("+1 hour", Array.Empty())) { AdvanceTime(1f); } if (GUILayout.Button("+1 day", Array.Empty())) { AdvanceTime(24f); } if (GUILayout.Button("Storm here", Array.Empty())) { ForceStorm(); } GUILayout.EndHorizontal(); if (GUILayout.Button("Teleport to nearest port (experimental)", Array.Empty())) { TeleportNearestPort(); } } private bool HostGateFail() { if (_net.State == LinkState.Connected && _net.Role != Role.Host) { _status = "host only (this is replicated)"; return true; } return false; } private void AdvanceTime(float hours) { if (HostGateFail()) { return; } try { Sun sun = Sun.sun; if ((Object)(object)sun != (Object)null) { sun.globalTime += hours; sun.localTime += hours; while (sun.localTime >= 24f) { sun.localTime -= 24f; } } if (hours >= 24f) { GameState.day += Mathf.RoundToInt(hours / 24f); } _status = "+" + hours + " h"; Plugin.Logger.LogInfo((object)("[DebugPanel] time +" + hours + " h")); } catch (Exception ex) { _status = "time: " + ex.Message; Plugin.Logger.LogWarning((object)("[DebugPanel] time: " + ex)); } } private void ForceStorm() { //IL_00ab: 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_00da: 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_0125: Unknown result type (might be due to invalid IL or missing references) if (HostGateFail()) { return; } try { WeatherStorms instance = WeatherStorms.instance; if ((Object)(object)instance == (Object)null) { _status = "WeatherStorms missing"; return; } if (_fStorms == null) { _fStorms = typeof(WeatherStorms).GetField("storms", BindingFlags.Instance | BindingFlags.NonPublic); } WanderingStorm[] array = ((_fStorms != null) ? (_fStorms.GetValue(instance) as WanderingStorm[]) : null); if (array == null || array.Length == 0) { _status = "no storms in scene"; return; } Vector3 val = (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform.position : Vector3.zero); WanderingStorm val2 = null; float num = float.MaxValue; WanderingStorm[] array2 = array; foreach (WanderingStorm val3 in array2) { if (!((Object)(object)val3 == (Object)null)) { float num2 = Vector3.Distance(((Component)val3).transform.position, val); if (num2 < num) { num = num2; val2 = val3; } } } if ((Object)(object)val2 == (Object)null) { _status = "storms list is empty"; return; } val2.active = true; ((Component)val2).transform.position = val; _status = "storm moved to player"; Plugin.Logger.LogInfo((object)("[DebugPanel] storm '" + ((Object)val2).name + "' moved to player")); } catch (Exception ex) { _status = "storm: " + ex.Message; Plugin.Logger.LogWarning((object)("[DebugPanel] storm: " + ex)); } } private void TeleportNearestPort() { //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_0048: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_011b: 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_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) if (HostGateFail()) { return; } try { PlayerEmbarkerNew val = Embarker(); Transform val2 = (((Object)(object)val != (Object)null) ? val.debugOutCurrentBoat : null); Vector3 val3 = (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform.position : (((Object)(object)val != (Object)null && (Object)(object)val.playerObserver != (Object)null) ? val.playerObserver.position : Vector3.zero)); IslandMarket[] array = Object.FindObjectsOfType(); IslandMarket val4 = null; float num = float.MaxValue; IslandMarket[] array2 = array; foreach (IslandMarket val5 in array2) { if (!((Object)(object)val5 == (Object)null)) { float num2 = Vector3.Distance(((Component)val5).transform.position, val3); if (num2 < num) { num = num2; val4 = val5; } } } if ((Object)(object)val4 == (Object)null) { _status = "port not found (far ports not loaded)"; return; } if ((Object)(object)val2 == (Object)null) { _status = "no boat to teleport"; return; } Vector3 val6 = ((Component)val4).transform.position + Vector3.up * 2f; val2.position += val6 - val3; _status = "boat -> port " + SafePortName(val4) + " (" + num.ToString("0") + " m)"; Plugin.Logger.LogInfo((object)("[DebugPanel] boat teleported to port " + SafePortName(val4))); } catch (Exception ex) { _status = "teleport: " + ex.Message; Plugin.Logger.LogWarning((object)("[DebugPanel] teleport: " + ex)); } } private static string SafePortName(IslandMarket m) { try { return m.GetPortName(); } catch { return ((Object)m).name; } } private void DrawIslandTeleport() { //IL_008f: 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) GUILayout.Label("- Island teleport (host moves the boat) -", _label, Array.Empty()); if (_recoveryPorts == null) { RefreshRecoveryPorts(); } if (GUILayout.Button("Refresh port list (" + ((_recoveryPorts != null) ? _recoveryPorts.Count : 0) + ")", Array.Empty())) { RefreshRecoveryPorts(); } if (_recoveryPorts == null || _recoveryPorts.Count == 0) { GUILayout.Label("Recovery ports not found (world still loading?)", _label, Array.Empty()); return; } _islandScroll = GUILayout.BeginScrollView(_islandScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(140f) }); foreach (RecoveryPort recoveryPort in _recoveryPorts) { if (!((Object)(object)recoveryPort == (Object)null) && GUILayout.Button(RecoveryPortName(recoveryPort), Array.Empty())) { TeleportToIsland(recoveryPort); } } GUILayout.EndScrollView(); } private void RefreshRecoveryPorts() { try { _recoveryPorts = new List(Object.FindObjectsOfType()); _recoveryPorts.Sort((RecoveryPort a, RecoveryPort b) => string.CompareOrdinal(RecoveryPortName(a), RecoveryPortName(b))); } catch (Exception ex) { _recoveryPorts = new List(); Plugin.Logger.LogWarning((object)("[DebugPanel] recovery port list: " + ex.Message)); } } private static string RecoveryPortName(RecoveryPort rp) { try { if ((Object)(object)rp != (Object)null && (Object)(object)rp.parentPort != (Object)null) { string portName = rp.parentPort.GetPortName(); if (!string.IsNullOrEmpty(portName)) { return portName; } } } catch { } if (!((Object)(object)rp != (Object)null)) { return "?"; } return ((Object)rp).name; } private void TeleportToIsland(RecoveryPort rp) { //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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: 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_0177: Unknown result type (might be due to invalid IL or missing references) //IL_018e: 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_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_00f3: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)rp == (Object)null) { return; } bool flag = _net.State != LinkState.Connected || _net.Role == Role.Host; bool flag2 = IsEmbarked(); if (!flag && flag2) { _status = "client on boat: host teleports"; return; } if (flag) { Transform val = (((Object)(object)GameState.lastOwnedBoat != (Object)null) ? GameState.lastOwnedBoat : GameState.currentBoat); if ((Object)(object)val != (Object)null) { try { BoatMooringRopes component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { component.UnmoorAllRopes(); RopeControllerAnchor anchorController = component.GetAnchorController(); if ((Object)(object)anchorController != (Object)null) { anchorController.ResetAnchor(); } } Rigidbody component2 = ((Component)val).GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.velocity = Vector3.zero; component2.angularVelocity = Vector3.zero; } val.position = rp.GetBoatPos(); if ((Object)(object)rp.boatPos != (Object)null) { val.rotation = rp.boatPos.rotation; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[DebugPanel] boat teleport: " + ex.Message)); } } } if (!flag2) { Vector3 position = ((Component)rp).transform.position + Vector3.up * 1f; if ((Object)(object)Refs.charController != (Object)null) { ((Component)Refs.charController).transform.position = position; } if ((Object)(object)Refs.observerMirror != (Object)null) { ((Component)Refs.observerMirror).transform.position = position; ((Component)Refs.observerMirror).transform.rotation = ((Component)rp).transform.rotation; } } _status = "teleport -> " + RecoveryPortName(rp) + (flag2 ? " (on boat)" : " (on foot)"); Plugin.Logger.LogInfo((object)("[DebugPanel] teleport -> " + RecoveryPortName(rp) + " embarked=" + flag2 + " hostAuthority=" + flag)); } catch (Exception ex2) { _status = "teleport: " + ex2.Message; Plugin.Logger.LogWarning((object)("[DebugPanel] teleport to island: " + ex2)); } } private bool IsEmbarked() { try { PlayerEmbarkerNew val = Embarker(); if ((Object)(object)val == (Object)null) { return false; } if (_fEmbarked == null) { _fEmbarked = typeof(PlayerEmbarkerNew).GetField("embarked", BindingFlags.Instance | BindingFlags.NonPublic); } return _fEmbarked != null && (bool)_fEmbarked.GetValue(val); } catch { return false; } } private void DrawItemSpawn() { //IL_00b8: 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_00d5: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("- Item Spawn (host) -", _label, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("filter:", _label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); _itemFilter = GUILayout.TextField(_itemFilter, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); if (GUILayout.Button("Reset", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _itemFilter = ""; } GUILayout.EndHorizontal(); EnsurePrefabs(); string text = ((_itemFilter != null) ? _itemFilter.ToLowerInvariant() : ""); _scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(220f) }); int num = 0; foreach (KeyValuePair prefab in _prefabs) { if (text.Length <= 0 || prefab.Value.ToLowerInvariant().IndexOf(text, StringComparison.Ordinal) >= 0) { if (++num > 60) { GUILayout.Label("... narrow the filter (>60 matches)", _label, Array.Empty()); break; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(prefab.Value, _label, Array.Empty()); if (GUILayout.Button("Spawn", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { SpawnItem(prefab.Key); } GUILayout.EndHorizontal(); } } if (num == 0) { GUILayout.Label("no matches", _label, Array.Empty()); } GUILayout.EndScrollView(); } private void EnsurePrefabs() { if (_prefabs != null) { return; } _prefabs = new List>(); try { PrefabsDirectory instance = PrefabsDirectory.instance; if ((Object)(object)instance != (Object)null && instance.shipItems != null) { for (int i = 0; i < instance.shipItems.Length; i++) { ShipItem val = instance.shipItems[i]; if ((Object)(object)val != (Object)null) { _prefabs.Add(new KeyValuePair(i, val.name)); } } } Plugin.Logger.LogInfo((object)("[DebugPanel] item catalog: " + _prefabs.Count)); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[DebugPanel] catalog: " + ex.Message)); } } private void SpawnItem(int prefabIndex) { //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_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_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_00a7: 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_00b8: 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_00c2: 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_00d1: 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_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) if (HostGateFail()) { return; } try { PrefabsDirectory instance = PrefabsDirectory.instance; if ((Object)(object)instance == (Object)null || instance.directory == null || prefabIndex < 0 || prefabIndex >= instance.directory.Length) { _status = "missing prefab #" + prefabIndex; return; } GameObject val = instance.directory[prefabIndex]; if ((Object)(object)val == (Object)null) { _status = "prefab #" + prefabIndex + " is empty"; return; } PlayerEmbarkerNew val2 = Embarker(); Vector3 val3; Quaternion val4; if ((Object)(object)val2 != (Object)null && (Object)(object)val2.playerObserver != (Object)null) { Transform playerObserver = val2.playerObserver; val3 = playerObserver.position + playerObserver.forward * 1.5f + Vector3.up * 0.5f; val4 = playerObserver.rotation; } else { val3 = Vector3.zero; val4 = Quaternion.identity; } GameObject obj = Object.Instantiate(val, val3, val4); SaveablePrefab component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { component.RegisterToSave(); } ShipItem component2 = obj.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.sold = true; try { component2.OnLoad(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[DebugPanel] OnLoad '" + ((Object)val).name + "': " + ex.Message)); } } _status = "spawned " + ((Object)val).name; Plugin.Logger.LogInfo((object)("[DebugPanel] spawned '" + ((Object)val).name + "' idx=" + prefabIndex)); } catch (Exception ex2) { _status = "spawn: " + ex2.Message; Plugin.Logger.LogWarning((object)("[DebugPanel] spawn: " + ex2)); } } private PlayerEmbarkerNew Embarker() { if ((Object)(object)_emb == (Object)null) { _emb = Object.FindObjectOfType(); } return _emb; } private void EnsureStyles() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003e: 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) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (_box == null) { _box = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)0, fontStyle = (FontStyle)1, fontSize = 13 }; _box.normal.textColor = Color.white; } if (_label == null) { _label = new GUIStyle(GUI.skin.label) { fontSize = 12, wordWrap = true }; _label.normal.textColor = Color.white; } } } public enum PatchHealthState { Unknown, Ok, Partial, Failed } public static class PatchHealth { private sealed class Entry { public PatchHealthState State; public string Detail; } private static readonly Dictionary Entries = new Dictionary(); public static string Summary { get { if (Entries.Count == 0) { return "not initialized"; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; using (Dictionary.ValueCollection.Enumerator enumerator = Entries.Values.GetEnumerator()) { while (enumerator.MoveNext()) { switch (enumerator.Current.State) { case PatchHealthState.Ok: num++; break; case PatchHealthState.Partial: num2++; break; case PatchHealthState.Failed: num3++; break; default: num4++; break; } } } if (num3 == 0 && num2 == 0 && num4 == 0) { return "ok (" + num + ")"; } return "ok " + num + ", partial " + num2 + ", failed " + num3 + ", unknown " + num4; } } public static string Details { get { if (Entries.Count == 0) { return "not initialized"; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair entry in Entries) { if (stringBuilder.Length > 0) { stringBuilder.Append("; "); } stringBuilder.Append(entry.Key).Append("=").Append(entry.Value.State); if (!string.IsNullOrEmpty(entry.Value.Detail)) { stringBuilder.Append("(").Append(entry.Value.Detail).Append(")"); } } return stringBuilder.ToString(); } } public static void Report(string domain, int ok, int total, string detail = null) { PatchHealthState state = ((total <= 0) ? ((ok > 0) ? PatchHealthState.Ok : PatchHealthState.Unknown) : ((ok >= total) ? PatchHealthState.Ok : ((ok <= 0) ? PatchHealthState.Failed : PatchHealthState.Partial))); Set(domain, state, string.IsNullOrEmpty(detail) ? (ok + "/" + total) : detail); } public static void Set(string domain, PatchHealthState state, string detail = null) { if (!string.IsNullOrEmpty(domain)) { Entries[domain] = new Entry { State = state, Detail = (detail ?? "") }; } } } } namespace SailwindCoop.Net { public enum Role { None, Host, Client } public enum LinkState { Idle, Connecting, Handshaking, Connected, Rejected, Failed } public sealed class PeerSession { public NetPeer Peer; public bool HandshakeDone; public uint PlayerNetId; public string PlayerName = ""; public string SelectedAvatar = ""; } public sealed class CoopNet { private readonly Action _log; private readonly EventBasedNetListener _listener = new EventBasedNetListener(); private NetManager _net; public readonly NetClock Clock = new NetClock(); public readonly NetRegistry Registry = new NetRegistry(); private readonly Dictionary _sessions = new Dictionary(); private readonly Dictionary _playerNames = new Dictionary(); private NetPeer _hostPeer; private long _lastTimeSyncTick; public string ModVersion = "0.0.1"; public Func WorldIdProvider = () => ""; public string PlayerName = "Player"; public int MaxClients = 4; public int DisconnectTimeoutMs = 5000; public int UpdateTimeMs = 15; public int PingIntervalMs = 1000; public string ListenIp = "0.0.0.0"; public int ConnectAttempts = 10; public int ReconnectDelayMs = 500; private static string ConnKey => "SailwindCoop:" + 47; public Role Role { get; private set; } public LinkState State { get; private set; } public string LastError { get; private set; } = ""; public uint MyNetId { get; private set; } = 1u; public int PeerCount => _sessions.Count; public double RttMs => Clock.RttMs; public event Action OnGameMessage; public event Action OnClientReady; public event Action OnAccepted; public event Action OnPlayerLeft; public CoopNet(Action log) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown _log = log ?? ((Action)delegate { }); _listener.ConnectionRequestEvent += new OnConnectionRequest(OnConnectionRequest); _listener.PeerConnectedEvent += new OnPeerConnected(OnPeerConnected); _listener.PeerDisconnectedEvent += new OnPeerDisconnected(OnPeerDisconnected); _listener.NetworkReceiveEvent += new OnNetworkReceive(OnNetworkReceive); _listener.NetworkErrorEvent += new OnNetworkError(OnNetworkError); } public string GetPlayerName(uint netId) { if (_playerNames.TryGetValue(netId, out var value) && !string.IsNullOrEmpty(value)) { return value; } if (netId == MyNetId && !string.IsNullOrEmpty(PlayerName)) { return PlayerName; } return "Player " + netId; } public uint PlayerNetIdForPeer(NetPeer peer) { if (peer != null && _sessions.TryGetValue(peer.Id, out var value) && value.HandshakeDone) { return value.PlayerNetId; } return 0u; } public void StartHost(int port) { Stop(); _net = NewManager(); bool flag = string.IsNullOrEmpty(ListenIp) || ListenIp == "0.0.0.0"; if (!(flag ? _net.Start(port) : _net.Start(ListenIp, "::", port))) { State = LinkState.Failed; LastError = "Failed to open UDP port " + port + (flag ? "" : (" on interface " + ListenIp)); _log("[CoopNet] " + LastError); return; } Role = Role.Host; State = LinkState.Connected; MyNetId = 1u; Registry.Register(1u, NetObjKind.Player, 1u); _log("[CoopNet] Host listening on " + (flag ? "all interfaces" : ListenIp) + " port " + port + " (protocol " + 47 + ")"); } public void StartClient(string ip, int port) { Stop(); _net = NewManager(); if (!_net.Start()) { State = LinkState.Failed; LastError = "Failed to start network manager"; _log("[CoopNet] " + LastError); return; } Role = Role.Client; State = LinkState.Connecting; _log("[CoopNet] Connecting to " + ip + ":" + port + " ..."); _net.Connect(ip, port, ConnKey); } public void Stop() { if (_net != null) { _net.Stop(); _net = null; } _sessions.Clear(); _hostPeer = null; Registry.Clear(); Clock.Reset(); Role = Role.None; State = LinkState.Idle; } private NetManager NewManager() { //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_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_003b: 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_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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown return new NetManager((INetEventListener)(object)_listener, (PacketLayerBase)null) { AutoRecycle = true, UpdateTime = Math.Max(1, UpdateTimeMs), DisconnectTimeout = Math.Max(1000, DisconnectTimeoutMs), PingInterval = Math.Max(100, PingIntervalMs), MaxConnectAttempts = Math.Max(1, ConnectAttempts), ReconnectDelay = Math.Max(100, ReconnectDelayMs), UnconnectedMessagesEnabled = false, IPv6Enabled = false }; } public void PollEvents() { if (_net == null) { return; } _net.PollEvents(0); if (Role == Role.Client && _hostPeer != null && State == LinkState.Connected) { long localTick = Clock.LocalTick; if (localTick - _lastTimeSyncTick >= 333) { _lastTimeSyncTick = localTick; _hostPeer.Send(new TimeSyncMsg { IsReply = false, ClientSendTick = localTick }, (DeliveryMethod)4); } } } private void OnConnectionRequest(ConnectionRequest request) { if (Role != Role.Host) { request.Reject(); } else if (_sessions.Count >= Math.Max(1, MaxClients)) { request.Reject(); } else { request.AcceptIfKey(ConnKey); } } private void OnPeerConnected(NetPeer peer) { if (Role == Role.Client) { _hostPeer = peer; State = LinkState.Handshaking; string text = ""; try { text = AvatarCatalog.CurrentSelection; } catch { text = ""; } _log("[CoopNet] Connected, sending Hello (avatar=" + text + ")"); peer.Send(new HelloMsg { ProtocolVersion = 47, ModVersion = ModVersion, WorldId = WorldIdProvider(), PlayerName = PlayerName, SelectedAvatar = text }, (DeliveryMethod)2); } else { _sessions[peer.Id] = new PeerSession { Peer = peer }; Action log = _log; int id = peer.Id; log("[CoopNet] Client connected (peer " + id + "), waiting for Hello"); } } private unsafe void OnPeerDisconnected(NetPeer peer, DisconnectInfo info) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (Role == Role.Host) { if (_sessions.TryGetValue(peer.Id, out var value) && value.HandshakeDone) { Registry.Remove(value.PlayerNetId); _playerNames.Remove(value.PlayerNetId); this.OnPlayerLeft?.Invoke(value.PlayerNetId); } _sessions.Remove(peer.Id); Action log = _log; int id = peer.Id; log("[CoopNet] Client disconnected (peer " + id + "): " + ((object)(*(DisconnectReason*)(&info.Reason))/*cast due to .constrained prefix*/).ToString()); } else { _hostPeer = null; TryReadRejectFromDisconnect(info); if (State != LinkState.Rejected) { State = LinkState.Failed; LastError = "Disconnected: " + ((object)(*(DisconnectReason*)(&info.Reason))/*cast due to .constrained prefix*/).ToString(); } _log("[CoopNet] Disconnected from host: " + ((object)(*(DisconnectReason*)(&info.Reason))/*cast due to .constrained prefix*/).ToString()); } } private void TryReadRejectFromDisconnect(DisconnectInfo info) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { NetPacketReader additionalData = info.AdditionalData; if (additionalData != null && !((NetDataReader)additionalData).EndOfData) { MsgType msgType = Protocol.PeekType(additionalData); if (msgType == MsgType.Reject && Protocol.ReadBody(msgType, (NetDataReader)(object)additionalData) is RejectMsg rej) { HandleReject(rej); } } } catch { } } private void OnNetworkError(IPEndPoint endPoint, SocketError error) { LastError = "Network error: " + error; _log("[CoopNet] " + LastError + " (" + endPoint?.ToString() + ")"); } private void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod method) { MsgType msgType = Protocol.PeekType(reader); INetMessage netMessage = Protocol.ReadBody(msgType, (NetDataReader)(object)reader); if (netMessage == null) { Action log = _log; byte b = (byte)msgType; log("[CoopNet] Unknown msgType " + b + " - skipped"); return; } switch (msgType) { case MsgType.Hello: HandleHello(peer, (HelloMsg)netMessage); break; case MsgType.HelloAck: HandleHelloAck((HelloAckMsg)netMessage); break; case MsgType.Reject: HandleReject((RejectMsg)netMessage); break; case MsgType.TimeSync: HandleTimeSync(peer, (TimeSyncMsg)netMessage); break; case MsgType.AvatarChange: HandleAvatarChange(peer, (AvatarChangeMsg)netMessage); break; case MsgType.Disconnect: _log("[CoopNet] Disconnect: " + ((DisconnectMsg)netMessage).Reason); break; default: this.OnGameMessage?.Invoke(msgType, netMessage, peer); break; } } private void HandleHello(NetPeer peer, HelloMsg hello) { if (Role == Role.Host && _sessions.TryGetValue(peer.Id, out var value)) { string detail; RejectReason rejectReason = ValidateHello(hello, out detail); if (rejectReason != RejectReason.None) { _log("[CoopNet] Rejecting client: " + rejectReason.ToString() + " " + detail); RejectMsg msg = new RejectMsg { Reason = rejectReason, Detail = detail }; peer.Send(msg, (DeliveryMethod)2); peer.Disconnect(Protocol.Write(msg)); _sessions.Remove(peer.Id); return; } uint num = Registry.AllocateId(); Registry.Register(num, NetObjKind.Player, num); value.HandshakeDone = true; value.PlayerNetId = num; value.PlayerName = (string.IsNullOrEmpty(hello.PlayerName) ? ("Player" + num) : hello.PlayerName); value.SelectedAvatar = (string.IsNullOrWhiteSpace(hello.SelectedAvatar) ? "" : hello.SelectedAvatar.Trim()); _playerNames[num] = value.PlayerName; _playerNames[1u] = PlayerName; peer.Send(new HelloAckMsg { AssignedNetId = num, ServerTick = Clock.ServerTick, HostPlayerName = PlayerName }, (DeliveryMethod)2); _log("[CoopNet] Client accepted: " + value.PlayerName + " -> NetId " + num); this.OnClientReady?.Invoke(value); SendKnownAvatarsTo(peer); } } private void SendKnownAvatarsTo(NetPeer peer) { try { string text = ""; try { text = AvatarCatalog.CurrentSelection; } catch { } if (!string.IsNullOrWhiteSpace(text)) { peer.Send(new AvatarChangeMsg { NetId = 1u, BundleFile = text.Trim() }, (DeliveryMethod)2); } foreach (PeerSession value in _sessions.Values) { if (value.HandshakeDone && value.Peer != peer && !string.IsNullOrWhiteSpace(value.SelectedAvatar)) { peer.Send(new AvatarChangeMsg { NetId = value.PlayerNetId, BundleFile = value.SelectedAvatar }, (DeliveryMethod)2); } } Action log = _log; string[] obj2 = new string[5] { "[CoopNet] Sent known avatar selections to peer ", null, null, null, null }; int id = peer.Id; obj2[1] = id.ToString(); obj2[2] = " (host='"; obj2[3] = text; obj2[4] = "')"; log(string.Concat(obj2)); } catch (Exception ex) { _log("[CoopNet] SendKnownAvatarsTo: " + ex.Message); } } private RejectReason ValidateHello(HelloMsg h, out string detail) { detail = ""; if (h.ProtocolVersion != 47) { detail = "server " + 47 + ", client " + h.ProtocolVersion; return RejectReason.ProtocolMismatch; } if (!string.IsNullOrEmpty(ModVersion) && !string.IsNullOrEmpty(h.ModVersion) && h.ModVersion != ModVersion) { detail = "server " + ModVersion + ", client " + h.ModVersion; return RejectReason.ModVersionMismatch; } string text = WorldIdProvider() ?? ""; if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(h.WorldId) && h.WorldId != text) { detail = "different worlds/saves"; return RejectReason.WorldMismatch; } return RejectReason.None; } private void HandleHelloAck(HelloAckMsg ack) { if (Role == Role.Client) { State = LinkState.Connected; MyNetId = ack.AssignedNetId; Clock.OnReply(Clock.LocalTick, ack.ServerTick); Registry.Register(ack.AssignedNetId, NetObjKind.Player, ack.AssignedNetId); Registry.Register(1u, NetObjKind.Player, 1u); _playerNames[ack.AssignedNetId] = PlayerName; _playerNames[1u] = (string.IsNullOrEmpty(ack.HostPlayerName) ? "Host" : ack.HostPlayerName); _log("[CoopNet] Accepted by host. My NetId=" + ack.AssignedNetId + ", host='" + ack.HostPlayerName + "'"); this.OnAccepted?.Invoke(ack); } } private void HandleReject(RejectMsg rej) { if (Role == Role.Client) { State = LinkState.Rejected; LastError = "Host rejected: " + rej.Reason.ToString() + " (" + rej.Detail + ")"; _log("[CoopNet] " + LastError); } } private void HandleTimeSync(NetPeer peer, TimeSyncMsg ts) { if (!ts.IsReply) { peer.Send(new TimeSyncMsg { IsReply = true, ClientSendTick = ts.ClientSendTick, ServerTick = Clock.ServerTick }, (DeliveryMethod)4); } else { Clock.OnReply(ts.ClientSendTick, ts.ServerTick); } } public void SendAvatarChange(string bundleFile) { if (!string.IsNullOrWhiteSpace(bundleFile)) { AvatarChangeMsg msg = new AvatarChangeMsg { NetId = MyNetId, BundleFile = bundleFile.Trim() }; if (Role == Role.Host) { Broadcast(msg, (DeliveryMethod)2); } else if (_hostPeer != null) { _hostPeer.Send(msg, (DeliveryMethod)2); } } } private void HandleAvatarChange(NetPeer peer, AvatarChangeMsg msg) { if (Role == Role.Host) { if (_sessions.TryGetValue(peer.Id, out var value) && value.HandshakeDone) { value.SelectedAvatar = msg.BundleFile ?? ""; } RelayExcept(msg, peer, (DeliveryMethod)2); } this.OnGameMessage?.Invoke(MsgType.AvatarChange, msg, peer); } public void Broadcast(INetMessage msg, DeliveryMethod method) { //IL_0061: 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) if (Role == Role.Host) { foreach (PeerSession value in _sessions.Values) { if (value.HandshakeDone) { value.Peer.Send(msg, method); } } return; } if (_hostPeer != null) { _hostPeer.Send(msg, method); } } public void RelayExcept(INetMessage msg, NetPeer except, DeliveryMethod method) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (Role != Role.Host) { return; } foreach (PeerSession value in _sessions.Values) { if (value.HandshakeDone && value.Peer != except) { value.Peer.Send(msg, method); } } } } public enum MsgType : byte { Hello = 1, HelloAck = 2, Reject = 3, Disconnect = 4, TimeSync = 10, SpawnObject = 20, DespawnObject = 21, PlayerState = 30, BoatState = 31, EnvState = 32, ControlState = 33, AnchorState = 34, MooringState = 35, BoatDamageState = 36, ControlEvent = 40, InteractRequest = 50, ControlRequest = 51, SteerRequest = 52, MooringRequest = 53, HoldRequest = 54, DamageRequest = 55, PushRequest = 56, LightState = 57, LightRequest = 58, ItemState = 59, ItemRequest = 60, ItemExtra = 62, WindRequest = 63, ShopRequest = 64, ShopResult = 65, FishCatch = 66, CargoResult = 67, StormState = 68, SleepState = 69, MissionJournal = 70, MissionReward = 71, MissionAccept = 72, MissionAbandon = 73, BoatPurchase = 74, AvatarChange = 75, SaveSnapshotBegin = 76, SaveSnapshotChunk = 77, SaveSnapshotEnd = 78, ClientWorldLoaded = 79, RodState = 80 } public enum ShopKind : byte { Buy, Sell } public enum ShopFailReason : byte { None, NotEnoughMoney, ItemNotFound, ShopNotFound } public enum RejectReason : byte { None, ProtocolMismatch, ModVersionMismatch, WorldMismatch, ServerFull, AlreadyConnected } public interface INetMessage { MsgType Type { get; } void Serialize(NetDataWriter w); void Deserialize(NetDataReader r); } public sealed class HelloMsg : INetMessage { public int ProtocolVersion; public string ModVersion = ""; public string WorldId = ""; public string PlayerName = ""; public string SelectedAvatar = ""; public MsgType Type => MsgType.Hello; public void Serialize(NetDataWriter w) { w.Put(ProtocolVersion); w.Put(ModVersion); w.Put(WorldId); w.Put(PlayerName); w.Put(SelectedAvatar ?? ""); } public void Deserialize(NetDataReader r) { ProtocolVersion = r.GetInt(); ModVersion = r.GetString(); WorldId = r.GetString(); PlayerName = r.GetString(); SelectedAvatar = r.GetString(); } } public sealed class HelloAckMsg : INetMessage { public uint AssignedNetId; public long ServerTick; public string HostPlayerName = ""; public MsgType Type => MsgType.HelloAck; public void Serialize(NetDataWriter w) { w.Put(AssignedNetId); w.Put(ServerTick); w.Put(HostPlayerName); } public void Deserialize(NetDataReader r) { AssignedNetId = r.GetUInt(); ServerTick = r.GetLong(); HostPlayerName = r.GetString(); } } public sealed class RejectMsg : INetMessage { public RejectReason Reason; public string Detail = ""; public MsgType Type => MsgType.Reject; public void Serialize(NetDataWriter w) { w.Put((byte)Reason); w.Put(Detail); } public void Deserialize(NetDataReader r) { Reason = (RejectReason)r.GetByte(); Detail = r.GetString(); } } public sealed class DisconnectMsg : INetMessage { public string Reason = ""; public MsgType Type => MsgType.Disconnect; public void Serialize(NetDataWriter w) { w.Put(Reason); } public void Deserialize(NetDataReader r) { Reason = r.GetString(); } } public sealed class TimeSyncMsg : INetMessage { public bool IsReply; public long ClientSendTick; public long ServerTick; public MsgType Type => MsgType.TimeSync; public void Serialize(NetDataWriter w) { w.Put(IsReply); w.Put(ClientSendTick); w.Put(ServerTick); } public void Deserialize(NetDataReader r) { IsReply = r.GetBool(); ClientSendTick = r.GetLong(); ServerTick = r.GetLong(); } } public enum CoordFrame : byte { World, Boat } public sealed class PlayerStateMsg : INetMessage { public uint NetId; public long Tick; public CoordFrame Frame; public ushort BoatIndex; public Vector3 Pos; public Quaternion Rot; public Quaternion HeadRot; public Vector3 Vel; public bool Crouch; public MsgType Type => MsgType.PlayerState; public void Serialize(NetDataWriter w) { //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_004a: 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) w.Put(NetId); w.Put(Tick); w.Put((byte)Frame); w.Put(BoatIndex); w.PutVector3(Pos); w.PutQuaternion(Rot); w.PutQuaternion(HeadRot); w.PutVector3(Vel); w.Put(Crouch); } public void Deserialize(NetDataReader r) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_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) NetId = r.GetUInt(); Tick = r.GetLong(); Frame = (CoordFrame)r.GetByte(); BoatIndex = r.GetUShort(); Pos = r.GetVector3(); Rot = r.GetQuaternion(); HeadRot = r.GetQuaternion(); Vel = r.GetVector3(); Crouch = r.GetBool(); } } public sealed class BoatStateMsg : INetMessage { public uint NetId; public ushort BoatIndex; public long Tick; public Vector3 RealPos; public Quaternion Rot; public Vector3 RealVel; public MsgType Type => MsgType.BoatState; public void Serialize(NetDataWriter w) { //IL_0026: 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) w.Put(NetId); w.Put(BoatIndex); w.Put(Tick); w.PutVector3(RealPos); w.PutQuaternion(Rot); w.PutVector3(RealVel); } public void Deserialize(NetDataReader r) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) NetId = r.GetUInt(); BoatIndex = r.GetUShort(); Tick = r.GetLong(); RealPos = r.GetVector3(); Rot = r.GetQuaternion(); RealVel = r.GetVector3(); } } public sealed class EnvStateMsg : INetMessage { public long Tick; public Vector3 Wind; public Vector3 BaseWind; public Quaternion WindRot; public float GlobalTime; public float LocalTime; public float Timescale; public int Day; public float MoonPhase; public bool HasWaves; public Quaternion WavesRot; public float WavesInertia; public float WavesMagnitude; public float WaveTime; public float HostTimeScale; public MsgType Type => MsgType.EnvState; public void Serialize(NetDataWriter w) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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) w.Put(Tick); w.PutVector3(Wind); w.PutVector3(BaseWind); w.PutQuaternion(WindRot); w.Put(GlobalTime); w.Put(LocalTime); w.Put(Timescale); w.Put(Day); w.Put(MoonPhase); w.Put(HasWaves); w.PutQuaternion(WavesRot); w.Put(WavesInertia); w.Put(WavesMagnitude); w.Put(WaveTime); w.Put(HostTimeScale); } public void Deserialize(NetDataReader r) { //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_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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //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) Tick = r.GetLong(); Wind = r.GetVector3(); BaseWind = r.GetVector3(); WindRot = r.GetQuaternion(); GlobalTime = r.GetFloat(); LocalTime = r.GetFloat(); Timescale = r.GetFloat(); Day = r.GetInt(); MoonPhase = r.GetFloat(); HasWaves = r.GetBool(); WavesRot = r.GetQuaternion(); WavesInertia = r.GetFloat(); WavesMagnitude = r.GetFloat(); WaveTime = r.GetFloat(); HostTimeScale = r.GetFloat(); } } public sealed class AnchorStateMsg : INetMessage { public long Tick; public CoordFrame Frame; public Vector3 Pos; public Quaternion Rot; public Vector3 Vel; public bool Set; public MsgType Type => MsgType.AnchorState; public void Serialize(NetDataWriter w) { //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_0032: Unknown result type (might be due to invalid IL or missing references) w.Put(Tick); w.Put((byte)Frame); w.PutVector3(Pos); w.PutQuaternion(Rot); w.PutVector3(Vel); w.Put(Set); } public void Deserialize(NetDataReader r) { //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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) Tick = r.GetLong(); Frame = (CoordFrame)r.GetByte(); Pos = r.GetVector3(); Rot = r.GetQuaternion(); Vel = r.GetVector3(); Set = r.GetBool(); } } public sealed class ControlStateMsg : INetMessage { public long Tick; public float[] Lengths = Array.Empty(); public Quaternion[] Rotations = Array.Empty(); public MsgType Type => MsgType.ControlState; public void Serialize(NetDataWriter w) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) w.Put(Tick); w.Put((ushort)Lengths.Length); for (int i = 0; i < Lengths.Length; i++) { w.Put(Lengths[i]); } w.Put((ushort)Rotations.Length); for (int j = 0; j < Rotations.Length; j++) { w.PutQuaternion(Rotations[j]); } } public void Deserialize(NetDataReader r) { //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) Tick = r.GetLong(); int uShort = r.GetUShort(); Lengths = new float[uShort]; for (int i = 0; i < uShort; i++) { Lengths[i] = r.GetFloat(); } int uShort2 = r.GetUShort(); Rotations = (Quaternion[])(object)new Quaternion[uShort2]; for (int j = 0; j < uShort2; j++) { Rotations[j] = r.GetQuaternion(); } } } public sealed class ControlRequestMsg : INetMessage { public ushort Index; public float Length; public bool HasWinchRotation; public Quaternion WinchRotation; public MsgType Type => MsgType.ControlRequest; public void Serialize(NetDataWriter w) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) w.Put(Index); w.Put(Length); w.Put(HasWinchRotation); if (HasWinchRotation) { w.PutQuaternion(WinchRotation); } } public void Deserialize(NetDataReader r) { //IL_0035: 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_003a: Unknown result type (might be due to invalid IL or missing references) Index = r.GetUShort(); Length = r.GetFloat(); HasWinchRotation = r.GetBool(); WinchRotation = (HasWinchRotation ? r.GetQuaternion() : Quaternion.identity); } } public sealed class SteerRequestMsg : INetMessage { public ushort Index; public float Input; public MsgType Type => MsgType.SteerRequest; public void Serialize(NetDataWriter w) { w.Put(Index); w.Put(Input); } public void Deserialize(NetDataReader r) { Index = r.GetUShort(); Input = r.GetFloat(); } } public enum MooringKind : byte { Unmoor, Moor, Length } public sealed class MooringStateMsg : INetMessage { public ushort Index; public MooringKind Kind; public Vector3 DockReal; public float LengthSq; public MsgType Type => MsgType.MooringState; public void Serialize(NetDataWriter w) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) w.Put(Index); w.Put((byte)Kind); w.PutVector3(DockReal); w.Put(LengthSq); } public void Deserialize(NetDataReader r) { //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) Index = r.GetUShort(); Kind = (MooringKind)r.GetByte(); DockReal = r.GetVector3(); LengthSq = r.GetFloat(); } } public sealed class MooringRequestMsg : INetMessage { public ushort Index; public MooringKind Kind; public Vector3 DockReal; public float LengthSq; public MsgType Type => MsgType.MooringRequest; public void Serialize(NetDataWriter w) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) w.Put(Index); w.Put((byte)Kind); w.PutVector3(DockReal); w.Put(LengthSq); } public void Deserialize(NetDataReader r) { //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) Index = r.GetUShort(); Kind = (MooringKind)r.GetByte(); DockReal = r.GetVector3(); LengthSq = r.GetFloat(); } } public sealed class BoatDamageStateMsg : INetMessage { public long Tick; public float WaterLevel; public float HullDamage; public float Oakum; public float WaterIntakeChunk; public bool Sunk; public MsgType Type => MsgType.BoatDamageState; public void Serialize(NetDataWriter w) { w.Put(Tick); w.Put(WaterLevel); w.Put(HullDamage); w.Put(Oakum); w.Put(WaterIntakeChunk); w.Put(Sunk); } public void Deserialize(NetDataReader r) { Tick = r.GetLong(); WaterLevel = r.GetFloat(); HullDamage = r.GetFloat(); Oakum = r.GetFloat(); WaterIntakeChunk = r.GetFloat(); Sunk = r.GetBool(); } } public enum InteractKind : byte { Activate, AltActivate, ActivateNoArg } public sealed class ControlEventMsg : INetMessage { public ushort Index; public InteractKind Kind; public MsgType Type => MsgType.ControlEvent; public void Serialize(NetDataWriter w) { w.Put(Index); w.Put((byte)Kind); } public void Deserialize(NetDataReader r) { Index = r.GetUShort(); Kind = (InteractKind)r.GetByte(); } } public sealed class HoldRequestMsg : INetMessage { public ushort Index; public InteractKind Kind; public bool Down; public MsgType Type => MsgType.HoldRequest; public void Serialize(NetDataWriter w) { w.Put(Index); w.Put((byte)Kind); w.Put(Down); } public void Deserialize(NetDataReader r) { Index = r.GetUShort(); Kind = (InteractKind)r.GetByte(); Down = r.GetBool(); } } public enum DamageAction : byte { AddOakum, BailWater } public sealed class DamageRequestMsg : INetMessage { public DamageAction Action; public float Amount; public MsgType Type => MsgType.DamageRequest; public void Serialize(NetDataWriter w) { w.Put((byte)Action); w.Put(Amount); } public void Deserialize(NetDataReader r) { Action = (DamageAction)r.GetByte(); Amount = r.GetFloat(); } } public sealed class PushRequestMsg : INetMessage { public ushort Index; public Vector3 RealPos; public Vector3 Force; public float DeltaTime; public MsgType Type => MsgType.PushRequest; public void Serialize(NetDataWriter w) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) w.Put(Index); w.PutVector3(RealPos); w.PutVector3(Force); w.Put(DeltaTime); } public void Deserialize(NetDataReader r) { //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_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) Index = r.GetUShort(); RealPos = r.GetVector3(); Force = r.GetVector3(); DeltaTime = r.GetFloat(); } } public sealed class LightStateMsg : INetMessage { public ushort Index; public bool On; public float Health; public MsgType Type => MsgType.LightState; public void Serialize(NetDataWriter w) { w.Put(Index); w.Put(On); w.Put(Health); } public void Deserialize(NetDataReader r) { Index = r.GetUShort(); On = r.GetBool(); Health = r.GetFloat(); } } public sealed class LightRequestMsg : INetMessage { public ushort Index; public bool On; public float Health; public MsgType Type => MsgType.LightRequest; public void Serialize(NetDataWriter w) { w.Put(Index); w.Put(On); w.Put(Health); } public void Deserialize(NetDataReader r) { Index = r.GetUShort(); On = r.GetBool(); Health = r.GetFloat(); } } public enum ItemAction : byte { Pose, Pickup, Drop, AltHeld, AltActivate, State, Consume, Nail, Crate, Unseal, Cargo, Inventory, RodHook, LampHook } public sealed class ItemStateMsg : INetMessage { public ushort Index; public int InstanceId; public int PrefabIndex; public long Tick; public CoordFrame Frame; public ushort BoatIndex; public Vector3 Pos; public Quaternion Rot; public Vector3 Vel; public uint HolderNetId; public float Amount; public float Health; public bool Sold; public bool Nailed; public int CrateId; public int CargoPort = -1; public int InventorySlot = -1; public bool Attached; public MsgType Type => MsgType.ItemState; public void Serialize(NetDataWriter w) { //IL_004a: 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) w.Put(Index); w.Put(InstanceId); w.Put(PrefabIndex); w.Put(Tick); w.Put((byte)Frame); w.Put(BoatIndex); w.PutVector3(Pos); w.PutQuaternion(Rot); w.PutVector3(Vel); w.Put(HolderNetId); w.Put(Amount); w.Put(Health); w.Put(Sold); w.Put(Nailed); w.Put(CrateId); w.Put(CargoPort); w.Put(InventorySlot); w.Put(Attached); } public void Deserialize(NetDataReader r) { //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_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_0067: Unknown result type (might be due to invalid IL or missing references) Index = r.GetUShort(); InstanceId = r.GetInt(); PrefabIndex = r.GetInt(); Tick = r.GetLong(); Frame = (CoordFrame)r.GetByte(); BoatIndex = r.GetUShort(); Pos = r.GetVector3(); Rot = r.GetQuaternion(); Vel = r.GetVector3(); HolderNetId = r.GetUInt(); Amount = r.GetFloat(); Health = r.GetFloat(); Sold = r.GetBool(); Nailed = r.GetBool(); CrateId = r.GetInt(); CargoPort = r.GetInt(); InventorySlot = r.GetInt(); Attached = r.GetBool(); } } public sealed class ItemRequestMsg : INetMessage { public ItemAction Action; public ushort Index; public int InstanceId; public int PrefabIndex; public long Tick; public CoordFrame Frame; public ushort BoatIndex; public Vector3 Pos; public Quaternion Rot; public Vector3 Vel; public float Amount; public float Health; public bool Sold; public bool Nailed; public int CrateId; public int CargoPort = -1; public int CargoIndex = -1; public int InventorySlot = -1; public bool Attached; public MsgType Type => MsgType.ItemRequest; public void Serialize(NetDataWriter w) { //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_006e: Unknown result type (might be due to invalid IL or missing references) w.Put((byte)Action); w.Put(Index); w.Put(InstanceId); w.Put(PrefabIndex); w.Put(Tick); w.Put((byte)Frame); w.Put(BoatIndex); w.PutVector3(Pos); w.PutQuaternion(Rot); w.PutVector3(Vel); w.Put(Amount); w.Put(Health); w.Put(Sold); w.Put(Nailed); w.Put(CrateId); w.Put(CargoPort); w.Put(CargoIndex); w.Put(InventorySlot); w.Put(Attached); } public void Deserialize(NetDataReader r) { //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_0067: 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_0073: Unknown result type (might be due to invalid IL or missing references) Action = (ItemAction)r.GetByte(); Index = r.GetUShort(); InstanceId = r.GetInt(); PrefabIndex = r.GetInt(); Tick = r.GetLong(); Frame = (CoordFrame)r.GetByte(); BoatIndex = r.GetUShort(); Pos = r.GetVector3(); Rot = r.GetQuaternion(); Vel = r.GetVector3(); Amount = r.GetFloat(); Health = r.GetFloat(); Sold = r.GetBool(); Nailed = r.GetBool(); CrateId = r.GetInt(); CargoPort = r.GetInt(); CargoIndex = r.GetInt(); InventorySlot = r.GetInt(); Attached = r.GetBool(); } } public sealed class SpawnObjectMsg : INetMessage { public byte Kind; public int InstanceId; public int PrefabIndex; public CoordFrame Frame; public ushort BoatIndex; public Vector3 Pos; public Quaternion Rot; public Vector3 Vel; public uint HolderNetId; public float Amount; public float Health; public bool Sold; public bool Nailed; public int CrateId; public int CargoPort = -1; public int InventorySlot = -1; public MsgType Type => MsgType.SpawnObject; public void Serialize(NetDataWriter w) { //IL_003e: 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_0056: Unknown result type (might be due to invalid IL or missing references) w.Put(Kind); w.Put(InstanceId); w.Put(PrefabIndex); w.Put((byte)Frame); w.Put(BoatIndex); w.PutVector3(Pos); w.PutQuaternion(Rot); w.PutVector3(Vel); w.Put(HolderNetId); w.Put(Amount); w.Put(Health); w.Put(Sold); w.Put(Nailed); w.Put(CrateId); w.Put(CargoPort); w.Put(InventorySlot); } public void Deserialize(NetDataReader r) { //IL_003e: 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_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_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) Kind = r.GetByte(); InstanceId = r.GetInt(); PrefabIndex = r.GetInt(); Frame = (CoordFrame)r.GetByte(); BoatIndex = r.GetUShort(); Pos = r.GetVector3(); Rot = r.GetQuaternion(); Vel = r.GetVector3(); HolderNetId = r.GetUInt(); Amount = r.GetFloat(); Health = r.GetFloat(); Sold = r.GetBool(); Nailed = r.GetBool(); CrateId = r.GetInt(); CargoPort = r.GetInt(); InventorySlot = r.GetInt(); } } public sealed class DespawnObjectMsg : INetMessage { public byte Kind; public int InstanceId; public MsgType Type => MsgType.DespawnObject; public void Serialize(NetDataWriter w) { w.Put(Kind); w.Put(InstanceId); } public void Deserialize(NetDataReader r) { Kind = r.GetByte(); InstanceId = r.GetInt(); } } public sealed class ItemExtraStateMsg : INetMessage { public int InstanceId; public int PrefabIndex; public float[] Values = Array.Empty(); public MsgType Type => MsgType.ItemExtra; public void Serialize(NetDataWriter w) { w.Put(InstanceId); w.Put(PrefabIndex); w.Put((byte)Values.Length); for (int i = 0; i < Values.Length; i++) { w.Put(Values[i]); } } public void Deserialize(NetDataReader r) { InstanceId = r.GetInt(); PrefabIndex = r.GetInt(); int num = r.GetByte(); Values = new float[num]; for (int i = 0; i < num; i++) { Values[i] = r.GetFloat(); } } } public sealed class WindRequestMsg : INetMessage { public Vector3 Wind; public MsgType Type => MsgType.WindRequest; public void Serialize(NetDataWriter w) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) w.PutVector3(Wind); } public void Deserialize(NetDataReader r) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Wind = r.GetVector3(); } } public sealed class CargoResultMsg : INetMessage { public bool Ok; public bool IsWithdraw; public ShopFailReason Reason; public int InstanceId; public MsgType Type => MsgType.CargoResult; public void Serialize(NetDataWriter w) { w.Put(Ok); w.Put(IsWithdraw); w.Put((byte)Reason); w.Put(InstanceId); } public void Deserialize(NetDataReader r) { Ok = r.GetBool(); IsWithdraw = r.GetBool(); Reason = (ShopFailReason)r.GetByte(); InstanceId = r.GetInt(); } } public sealed class FishCatchMsg : INetMessage { public int PrefabIndex; public CoordFrame Frame; public ushort BoatIndex; public Vector3 Pos; public Quaternion Rot; public MsgType Type => MsgType.FishCatch; public void Serialize(NetDataWriter w) { //IL_0026: 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) w.Put(PrefabIndex); w.Put((byte)Frame); w.Put(BoatIndex); w.PutVector3(Pos); w.PutQuaternion(Rot); } public void Deserialize(NetDataReader r) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) PrefabIndex = r.GetInt(); Frame = (CoordFrame)r.GetByte(); BoatIndex = r.GetUShort(); Pos = r.GetVector3(); Rot = r.GetQuaternion(); } } public sealed class RodStateMsg : INetMessage { public int InstanceId; public int PrefabIndex; public Vector3 RealPos; public float Limit; public float Bend; public MsgType Type => MsgType.RodState; public void Serialize(NetDataWriter w) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) w.Put(InstanceId); w.Put(PrefabIndex); w.PutVector3(RealPos); w.Put(Limit); w.Put(Bend); } public void Deserialize(NetDataReader r) { //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) InstanceId = r.GetInt(); PrefabIndex = r.GetInt(); RealPos = r.GetVector3(); Limit = r.GetFloat(); Bend = r.GetFloat(); } } public sealed class ShopRequestMsg : INetMessage { public ShopKind Kind; public ushort KeeperIndex; public int InstanceId; public int PrefabIndex; public Vector3 RealPos; public MsgType Type => MsgType.ShopRequest; public void Serialize(NetDataWriter w) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) w.Put((byte)Kind); w.Put(KeeperIndex); w.Put(InstanceId); w.Put(PrefabIndex); w.PutVector3(RealPos); } public void Deserialize(NetDataReader r) { //IL_0032: 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) Kind = (ShopKind)r.GetByte(); KeeperIndex = r.GetUShort(); InstanceId = r.GetInt(); PrefabIndex = r.GetInt(); RealPos = r.GetVector3(); } } public sealed class ShopResultMsg : INetMessage { public ShopKind Kind; public bool Ok; public ShopFailReason Reason; public int InstanceId; public MsgType Type => MsgType.ShopResult; public void Serialize(NetDataWriter w) { w.Put((byte)Kind); w.Put(Ok); w.Put((byte)Reason); w.Put(InstanceId); } public void Deserialize(NetDataReader r) { Kind = (ShopKind)r.GetByte(); Ok = r.GetBool(); Reason = (ShopFailReason)r.GetByte(); InstanceId = r.GetInt(); } } public sealed class StormStateMsg : INetMessage { public Vector3[] Pos = Array.Empty(); public bool[] Active = Array.Empty(); public float Distance; public MsgType Type => MsgType.StormState; public void Serialize(NetDataWriter w) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) w.Put((byte)Pos.Length); for (int i = 0; i < Pos.Length; i++) { w.PutVector3(Pos[i]); w.Put(Active[i]); } w.Put(Distance); } public void Deserialize(NetDataReader r) { //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) int num = r.GetByte(); Pos = (Vector3[])(object)new Vector3[num]; Active = new bool[num]; for (int i = 0; i < num; i++) { Pos[i] = r.GetVector3(); Active[i] = r.GetBool(); } Distance = r.GetFloat(); } } public sealed class SleepStateMsg : INetMessage { public bool Sleeping; public MsgType Type => MsgType.SleepState; public void Serialize(NetDataWriter w) { w.Put(Sleeping); } public void Deserialize(NetDataReader r) { Sleeping = r.GetBool(); } } public struct MissionEntry { public byte MissionIndex; public int OriginPort; public int DestinationPort; public int GoodPrefabIndex; public int GoodCount; public int TotalPrice; public float InsuranceLevel; public float Distance; public int DeliveredGoods; public int DueDay; } public sealed class MissionJournalMsg : INetMessage { public MissionEntry[] Missions = Array.Empty(); public MsgType Type => MsgType.MissionJournal; public void Serialize(NetDataWriter w) { w.Put((byte)Missions.Length); for (int i = 0; i < Missions.Length; i++) { MissionEntry missionEntry = Missions[i]; w.Put(missionEntry.MissionIndex); w.Put(missionEntry.OriginPort); w.Put(missionEntry.DestinationPort); w.Put(missionEntry.GoodPrefabIndex); w.Put(missionEntry.GoodCount); w.Put(missionEntry.TotalPrice); w.Put(missionEntry.InsuranceLevel); w.Put(missionEntry.Distance); w.Put(missionEntry.DeliveredGoods); w.Put(missionEntry.DueDay); } } public void Deserialize(NetDataReader r) { int num = r.GetByte(); Missions = new MissionEntry[num]; for (int i = 0; i < num; i++) { Missions[i] = new MissionEntry { MissionIndex = r.GetByte(), OriginPort = r.GetInt(), DestinationPort = r.GetInt(), GoodPrefabIndex = r.GetInt(), GoodCount = r.GetInt(), TotalPrice = r.GetInt(), InsuranceLevel = r.GetFloat(), Distance = r.GetFloat(), DeliveredGoods = r.GetInt(), DueDay = r.GetInt() }; } } } public sealed class MissionRewardMsg : INetMessage { public int Region; public int Amount; public int OriginRegion = -1; public int DestinationRegion = -1; public int RepAmount; public int GoodPrefabIndex; public string DestinationName = ""; public int ExpectedReward; public MsgType Type => MsgType.MissionReward; public void Serialize(NetDataWriter w) { w.Put(Region); w.Put(Amount); w.Put(OriginRegion); w.Put(DestinationRegion); w.Put(RepAmount); w.Put(GoodPrefabIndex); w.Put(DestinationName ?? ""); w.Put(ExpectedReward); } public void Deserialize(NetDataReader r) { Region = r.GetInt(); Amount = r.GetInt(); OriginRegion = r.GetInt(); DestinationRegion = r.GetInt(); RepAmount = r.GetInt(); GoodPrefabIndex = r.GetInt(); DestinationName = r.GetString(); ExpectedReward = r.GetInt(); } } public sealed class MissionAcceptMsg : INetMessage { public MissionEntry Mission; public MsgType Type => MsgType.MissionAccept; public void Serialize(NetDataWriter w) { MissionEntry mission = Mission; w.Put(mission.OriginPort); w.Put(mission.DestinationPort); w.Put(mission.GoodPrefabIndex); w.Put(mission.GoodCount); w.Put(mission.TotalPrice); w.Put(mission.InsuranceLevel); w.Put(mission.Distance); w.Put(mission.DueDay); } public void Deserialize(NetDataReader r) { Mission = new MissionEntry { OriginPort = r.GetInt(), DestinationPort = r.GetInt(), GoodPrefabIndex = r.GetInt(), GoodCount = r.GetInt(), TotalPrice = r.GetInt(), InsuranceLevel = r.GetFloat(), Distance = r.GetFloat(), DueDay = r.GetInt() }; } } public sealed class MissionAbandonMsg : INetMessage { public byte MissionIndex; public MsgType Type => MsgType.MissionAbandon; public void Serialize(NetDataWriter w) { w.Put(MissionIndex); } public void Deserialize(NetDataReader r) { MissionIndex = r.GetByte(); } } public sealed class BoatPurchaseMsg : INetMessage { public int SceneIndex; public MsgType Type => MsgType.BoatPurchase; public void Serialize(NetDataWriter w) { w.Put(SceneIndex); } public void Deserialize(NetDataReader r) { SceneIndex = r.GetInt(); } } public sealed class AvatarChangeMsg : INetMessage { public uint NetId; public string BundleFile = ""; public MsgType Type => MsgType.AvatarChange; public void Serialize(NetDataWriter w) { w.Put(NetId); w.Put(BundleFile ?? ""); } public void Deserialize(NetDataReader r) { NetId = r.GetUInt(); BundleFile = r.GetString(); } } public sealed class SaveSnapshotBeginMsg : INetMessage { public int TotalBytes; public int ChunkCount; public int GameVersion; public MsgType Type => MsgType.SaveSnapshotBegin; public void Serialize(NetDataWriter w) { w.Put(TotalBytes); w.Put(ChunkCount); w.Put(GameVersion); } public void Deserialize(NetDataReader r) { TotalBytes = r.GetInt(); ChunkCount = r.GetInt(); GameVersion = r.GetInt(); } } public sealed class SaveSnapshotChunkMsg : INetMessage { public int Index; public byte[] Data = Array.Empty(); public MsgType Type => MsgType.SaveSnapshotChunk; public void Serialize(NetDataWriter w) { w.Put(Index); w.PutBytesWithLength(Data); } public void Deserialize(NetDataReader r) { Index = r.GetInt(); Data = r.GetBytesWithLength(); } } public sealed class SaveSnapshotEndMsg : INetMessage { public bool Ok; public MsgType Type => MsgType.SaveSnapshotEnd; public void Serialize(NetDataWriter w) { w.Put(Ok); } public void Deserialize(NetDataReader r) { Ok = r.GetBool(); } } public sealed class ClientWorldLoadedMsg : INetMessage { public bool Ok; public MsgType Type => MsgType.ClientWorldLoaded; public void Serialize(NetDataWriter w) { w.Put(Ok); } public void Deserialize(NetDataReader r) { Ok = r.GetBool(); } } public static class WireExt { public static void PutVector3(this NetDataWriter w, Vector3 v) { //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) //IL_0019: Unknown result type (might be due to invalid IL or missing references) w.Put(v.x); w.Put(v.y); w.Put(v.z); } public static Vector3 GetVector3(this NetDataReader r) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(r.GetFloat(), r.GetFloat(), r.GetFloat()); } public static void PutQuaternion(this NetDataWriter w, Quaternion q) { //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) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) w.Put(q.x); w.Put(q.y); w.Put(q.z); w.Put(q.w); } public static Quaternion GetQuaternion(this NetDataReader r) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(r.GetFloat(), r.GetFloat(), r.GetFloat(), r.GetFloat()); } } public sealed class NetClock { private static readonly Stopwatch _sw = Stopwatch.StartNew(); private double _offsetMs; private double _rttMs; private bool _hasSample; public long LocalTick => _sw.ElapsedMilliseconds; public long ServerTick => LocalTick + (long)_offsetMs; public double RttMs => _rttMs; public double OffsetMs => _offsetMs; public bool HasSample => _hasSample; public void OnReply(long clientSendTick, long serverTick) { long localTick = LocalTick; double num = localTick - clientSendTick; if (num < 0.0) { num = 0.0; } double num2 = (double)serverTick + num * 0.5 - (double)localTick; if (!_hasSample) { _rttMs = num; _offsetMs = num2; _hasSample = true; } else { _rttMs = _rttMs * 0.9 + num * 0.1; _offsetMs = _offsetMs * 0.9 + num2 * 0.1; } } public void Reset() { _offsetMs = 0.0; _rttMs = 0.0; _hasSample = false; } } public enum NetObjKind : byte { Player = 1, Boat, Sail, SteeringWheel, Rudder, Anchor, Rope, Item } public sealed class NetEntry { public uint NetId; public NetObjKind Kind; public uint OwnerNetId; public object Target; } public sealed class NetRegistry { public const uint HostAuthority = 0u; public const uint HostPlayerNetId = 1u; private readonly Dictionary _byId = new Dictionary(); private readonly Dictionary _players = new Dictionary(); private uint _nextId = 2u; public IEnumerable Players => _players.Values; public int PlayerCount => _players.Count; public IEnumerable All => _byId.Values; public uint AllocateId() { return _nextId++; } public NetEntry Register(uint netId, NetObjKind kind, uint ownerNetId = 0u, object target = null) { return RegisterInternal(netId, kind, ownerNetId, target, advanceAllocator: true); } public NetEntry RegisterFixed(uint netId, NetObjKind kind, uint ownerNetId = 0u, object target = null) { return RegisterInternal(netId, kind, ownerNetId, target, advanceAllocator: false); } private NetEntry RegisterInternal(uint netId, NetObjKind kind, uint ownerNetId, object target, bool advanceAllocator) { NetEntry netEntry = new NetEntry { NetId = netId, Kind = kind, OwnerNetId = ownerNetId, Target = target }; _byId[netId] = netEntry; if (kind == NetObjKind.Player) { _players[netId] = netEntry; } if (advanceAllocator && netId >= _nextId) { _nextId = netId + 1; } return netEntry; } public bool TryGet(uint netId, out NetEntry entry) { return _byId.TryGetValue(netId, out entry); } public void Remove(uint netId) { _byId.Remove(netId); _players.Remove(netId); } public bool HasAuthority(uint objectNetId, uint actorNetId, bool actorIsHost) { if (actorIsHost) { return true; } if (!_byId.TryGetValue(objectNetId, out var value)) { return false; } return value.OwnerNetId == actorNetId; } public void Clear() { _byId.Clear(); _players.Clear(); _nextId = 2u; } } public static class Protocol { public const int Version = 47; public static NetDataWriter Write(INetMessage msg) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown NetDataWriter val = new NetDataWriter(); val.Put((byte)msg.Type); msg.Serialize(val); return val; } public static MsgType PeekType(NetPacketReader reader) { return (MsgType)((NetDataReader)reader).GetByte(); } public static INetMessage ReadBody(MsgType type, NetDataReader r) { INetMessage netMessage = Create(type); if (netMessage == null) { return null; } netMessage.Deserialize(r); return netMessage; } private static INetMessage Create(MsgType type) { return type switch { MsgType.Hello => new HelloMsg(), MsgType.HelloAck => new HelloAckMsg(), MsgType.Reject => new RejectMsg(), MsgType.Disconnect => new DisconnectMsg(), MsgType.TimeSync => new TimeSyncMsg(), MsgType.PlayerState => new PlayerStateMsg(), MsgType.BoatState => new BoatStateMsg(), MsgType.EnvState => new EnvStateMsg(), MsgType.ControlState => new ControlStateMsg(), MsgType.AnchorState => new AnchorStateMsg(), MsgType.MooringState => new MooringStateMsg(), MsgType.BoatDamageState => new BoatDamageStateMsg(), MsgType.ControlRequest => new ControlRequestMsg(), MsgType.ControlEvent => new ControlEventMsg(), MsgType.SteerRequest => new SteerRequestMsg(), MsgType.MooringRequest => new MooringRequestMsg(), MsgType.HoldRequest => new HoldRequestMsg(), MsgType.DamageRequest => new DamageRequestMsg(), MsgType.PushRequest => new PushRequestMsg(), MsgType.LightState => new LightStateMsg(), MsgType.LightRequest => new LightRequestMsg(), MsgType.ItemState => new ItemStateMsg(), MsgType.ItemRequest => new ItemRequestMsg(), MsgType.SpawnObject => new SpawnObjectMsg(), MsgType.DespawnObject => new DespawnObjectMsg(), MsgType.ItemExtra => new ItemExtraStateMsg(), MsgType.WindRequest => new WindRequestMsg(), MsgType.ShopRequest => new ShopRequestMsg(), MsgType.ShopResult => new ShopResultMsg(), MsgType.FishCatch => new FishCatchMsg(), MsgType.CargoResult => new CargoResultMsg(), MsgType.StormState => new StormStateMsg(), MsgType.SleepState => new SleepStateMsg(), MsgType.MissionJournal => new MissionJournalMsg(), MsgType.MissionReward => new MissionRewardMsg(), MsgType.MissionAccept => new MissionAcceptMsg(), MsgType.MissionAbandon => new MissionAbandonMsg(), MsgType.BoatPurchase => new BoatPurchaseMsg(), MsgType.AvatarChange => new AvatarChangeMsg(), MsgType.SaveSnapshotBegin => new SaveSnapshotBeginMsg(), MsgType.SaveSnapshotChunk => new SaveSnapshotChunkMsg(), MsgType.SaveSnapshotEnd => new SaveSnapshotEndMsg(), MsgType.ClientWorldLoaded => new ClientWorldLoadedMsg(), MsgType.RodState => new RodStateMsg(), _ => null, }; } } public static class PeerExt { public static void Send(this NetPeer peer, INetMessage msg, DeliveryMethod method) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) peer.Send(Protocol.Write(msg), method); } } } namespace SailwindCoop.Avatar { public static class AvatarCatalog { public sealed class Entry { public string FileName; public string DisplayName; public string FullPath; public bool Exists; public bool IsNpc; } public const string DefaultBundleFile = "avatar.bundle"; private const string SelectionFile = "selected_avatar.txt"; private static List _entries = new List(); private static string _modDir = ""; public static IReadOnlyList Entries => _entries; public static string CurrentSelection { get; private set; } = "avatar.bundle"; public static event Action OnSelectionChanged; public static void Initialize() { try { _modDir = Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location) ?? ""; } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("[AvatarCatalog] Failed to determine mod folder: " + ex.Message)); } _modDir = ""; } Scan(); LoadSelection(); } public static void Scan() { List list = new List(); try { if (!string.IsNullOrEmpty(_modDir) && Directory.Exists(_modDir)) { string[] files = Directory.GetFiles(_modDir, "avatar*.bundle", SearchOption.TopDirectoryOnly); for (int i = 0; i < files.Length; i++) { FileInfo fileInfo = new FileInfo(files[i]); list.Add(new Entry { FileName = fileInfo.Name, DisplayName = MakeDisplayName(fileInfo.Name), FullPath = fileInfo.FullName, Exists = true }); } } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("[AvatarCatalog] Folder scan error: " + ex.Message)); } } list = list.OrderBy((Entry e) => e.FileName, StringComparer.OrdinalIgnoreCase).ToList(); if (!list.Any((Entry e) => string.Equals(e.FileName, "avatar.bundle", StringComparison.OrdinalIgnoreCase))) { list.Insert(0, new Entry { FileName = "avatar.bundle", DisplayName = MakeDisplayName("avatar.bundle"), FullPath = (string.IsNullOrEmpty(_modDir) ? "" : Path.Combine(_modDir, "avatar.bundle")), Exists = (!string.IsNullOrEmpty(_modDir) && File.Exists(Path.Combine(_modDir, "avatar.bundle"))) }); } try { NpcSkinLibrary.Scan(); foreach (NpcSkinLibrary.Skin skin in NpcSkinLibrary.Skins) { list.Add(new Entry { FileName = skin.Key, DisplayName = skin.DisplayName, FullPath = "", Exists = NpcSkinLibrary.CanBuild, IsNpc = true }); } } catch (Exception ex2) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogWarning((object)("[AvatarCatalog] NPC skin scan: " + ex2.Message)); } } _entries = list; ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogInfo((object)("[AvatarCatalog] Bundles found: " + _entries.Count((Entry e) => !e.IsNpc) + ", NPC skins: " + _entries.Count((Entry e) => e.IsNpc))); } } public static string DisplayNameFor(string selection) { if (string.IsNullOrEmpty(selection)) { return "avatar.bundle"; } Entry entry = _entries.FirstOrDefault((Entry x) => string.Equals(x.FileName, selection, StringComparison.OrdinalIgnoreCase)); if (entry != null) { if (!entry.IsNpc) { return entry.FileName; } return entry.DisplayName; } if (!NpcSkinLibrary.IsNpcKey(selection)) { return selection; } return "NPC (unknown skin)"; } public static string ResolvePath(string fileName) { if (string.IsNullOrEmpty(_modDir) || string.IsNullOrEmpty(fileName)) { return ""; } if (NpcSkinLibrary.IsNpcKey(fileName)) { return ""; } string text = Path.Combine(_modDir, fileName); if (!File.Exists(text)) { return ""; } return text; } public static bool IsAvailable(string fileName) { if (string.IsNullOrEmpty(fileName)) { return false; } if (NpcSkinLibrary.IsNpcKey(fileName)) { return NpcSkinLibrary.CanBuild; } return !string.IsNullOrEmpty(ResolvePath(fileName)); } public static void SetSelection(string fileName) { string text = (string.IsNullOrWhiteSpace(fileName) ? "avatar.bundle" : fileName.Trim()); if (string.Equals(CurrentSelection, text, StringComparison.OrdinalIgnoreCase)) { SaveSelection(); return; } CurrentSelection = text; SaveSelection(); ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("[AvatarCatalog] Model selection: " + CurrentSelection)); } try { AvatarCatalog.OnSelectionChanged?.Invoke(CurrentSelection); } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogWarning((object)("[AvatarCatalog] OnSelectionChanged: " + ex.Message)); } } } private static void LoadSelection() { try { if (string.IsNullOrEmpty(_modDir)) { return; } string path = Path.Combine(_modDir, "selected_avatar.txt"); if (!File.Exists(path)) { CurrentSelection = "avatar.bundle"; return; } string text = File.ReadAllText(path).Trim(); CurrentSelection = (string.IsNullOrEmpty(text) ? "avatar.bundle" : text); ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("[AvatarCatalog] Loaded selection: " + CurrentSelection)); } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogWarning((object)("[AvatarCatalog] Failed to read selection: " + ex.Message)); } CurrentSelection = "avatar.bundle"; } } private static void SaveSelection() { try { if (!string.IsNullOrEmpty(_modDir)) { File.WriteAllText(Path.Combine(_modDir, "selected_avatar.txt"), CurrentSelection ?? "avatar.bundle"); } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("[AvatarCatalog] Failed to save selection: " + ex.Message)); } } } private static string MakeDisplayName(string fileName) { if (string.IsNullOrEmpty(fileName)) { return "(unknown)"; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); if (string.Equals(fileNameWithoutExtension, "avatar", StringComparison.OrdinalIgnoreCase)) { return "Default (avatar.bundle)"; } if (fileNameWithoutExtension.StartsWith("avatar", StringComparison.OrdinalIgnoreCase) && fileNameWithoutExtension.Length > 6) { return fileNameWithoutExtension.Substring(6).TrimStart('-', '_', ' ', '.'); } return fileNameWithoutExtension; } } public sealed class AvatarSelectUI { private const float PanelWidth = 360f; private const float PanelHeight = 280f; private const int FontSize = 14; public bool Visible; private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _entryStyle; private GUIStyle _entrySelectedStyle; private GUIStyle _hintStyle; private Vector2 _scroll; private string _lastDrawnSelection = ""; public AvatarSelectUI(string currentSelection) { _lastDrawnSelection = currentSelection; } public void Draw() { //IL_003d: 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_00dc: 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) if (!Visible) { return; } EnsureStyles(); float num = 360f; float num2 = 280f; float num3 = ((float)Screen.width - num) * 0.5f; float num4 = ((float)Screen.height - num2) * 0.5f; GUI.Box(new Rect(num3, num4, num, num2), GUIContent.none, _windowStyle); GUILayout.BeginArea(new Rect(num3 + 10f, num4 + 8f, num - 20f, num2 - 16f)); GUILayout.Label("Character Model Selection", _headerStyle, Array.Empty()); GUILayout.Label("Bundles come from the mod folder (avatar*.bundle); NPC skins come from loaded islands (approach an island and reopen the window).", _hintStyle, Array.Empty()); GUILayout.Space(4f); IReadOnlyList entries = AvatarCatalog.Entries; if (entries == null || entries.Count == 0) { GUILayout.Label("No bundles found.", _hintStyle, Array.Empty()); } else { _scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); string currentSelection = AvatarCatalog.CurrentSelection; for (int i = 0; i < entries.Count; i++) { AvatarCatalog.Entry entry = entries[i]; bool flag = string.Equals(entry.FileName, currentSelection, StringComparison.OrdinalIgnoreCase); string text = (entry.IsNpc ? entry.DisplayName : entry.FileName); string text2 = (entry.Exists ? "● " : "○ ") + text + (flag ? " ✓" : (entry.Exists ? "" : (entry.IsNpc ? " (template missing)" : " (file missing)"))); GUIStyle val = (flag ? _entrySelectedStyle : _entryStyle); if (GUILayout.Button(text2, val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) })) { AvatarCatalog.SetSelection(entry.FileName); } } GUILayout.EndScrollView(); } GUILayout.FlexibleSpace(); GUILayout.Label("Current: " + AvatarCatalog.DisplayNameFor(AvatarCatalog.CurrentSelection), _hintStyle, Array.Empty()); GUILayout.Label("Close this window from the co-op menu.", _hintStyle, Array.Empty()); GUILayout.EndArea(); } private void EnsureStyles() { //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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_007c: Expected O, but got Unknown //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) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown //IL_00db: 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_00e8: 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_010c: Expected O, but got Unknown if (_windowStyle == null) { GUIStyle val = new GUIStyle(GUI.skin.box); val.normal.background = MakeBg(new Color(0f, 0f, 0f, 0.85f)); _windowStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1 }; val2.normal.textColor = Color.white; _headerStyle = val2; _entryStyle = new GUIStyle(GUI.skin.button) { fontSize = 14, alignment = (TextAnchor)3 }; GUIStyle val3 = new GUIStyle(_entryStyle); val3.normal.textColor = new Color(0.6f, 1f, 0.6f); _entrySelectedStyle = val3; GUIStyle val4 = new GUIStyle(GUI.skin.label) { fontSize = 12 }; val4.normal.textColor = new Color(0.8f, 0.8f, 0.8f); _hintStyle = val4; } } private Texture2D MakeBg(Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_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_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, c); val.Apply(); return val; } } public static class NpcSkinLibrary { public sealed class Skin { public string Key; public string DisplayName; } public const string KeyPrefix = "npc:"; private static readonly Dictionary _skins = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _materials = new Dictionary(StringComparer.OrdinalIgnoreCase); private static GameObject _template; private static float _nextScanAt; public const float ScanCooldownSec = 5f; public static IReadOnlyList Skins => _skins.Values.OrderBy((Skin s) => s.DisplayName, StringComparer.OrdinalIgnoreCase).ToList(); public static bool CanBuild => (Object)(object)_template != (Object)null; public static bool IsNpcKey(string selection) { if (!string.IsNullOrEmpty(selection)) { return selection.StartsWith("npc:", StringComparison.OrdinalIgnoreCase); } return false; } public static void Scan() { Scan(force: true); } public static void Scan(bool force) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!force && realtimeSinceStartup < _nextScanAt) { return; } _nextScanAt = realtimeSinceStartup + 5f; int count = _skins.Count; try { CharacterCustomizer[] array = Object.FindObjectsOfType(); foreach (CharacterCustomizer val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && HasLiveSkinnedBody(((Component)val).gameObject)) { CacheMaterial(val.mat); EnsureTemplate(val); string text = BuildKey(val); if (text != null && !_skins.ContainsKey(text)) { _skins[text] = new Skin { Key = text, DisplayName = MakeDisplayName(val) }; } } } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("[NpcSkins] NPC scan: " + ex.Message)); } } if (_skins.Count != count || _skins.Count > 0) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)("[NpcSkins] NPC skins in catalog: " + _skins.Count + ", template " + (((Object)(object)_template != (Object)null) ? "present" : "NONE") + ", materials " + _materials.Count)); } } } public static GameObject BuildModel(string key) { try { if (!IsNpcKey(key)) { return null; } if ((Object)(object)_template == (Object)null) { Scan(force: false); } if ((Object)(object)_template == (Object)null) { return null; } string text = key.Substring("npc:".Length); int num = text.IndexOf('|'); if (num < 0) { return null; } string text2 = text.Substring(0, num); HashSet hashSet = new HashSet(text.Substring(num + 1).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase); if (hashSet.Count == 0) { return null; } GameObject val = Object.Instantiate(_template); ((Object)val).name = "NpcSkin"; Material value = null; _materials.TryGetValue(text2, out value); int num2 = 0; SkinnedMeshRenderer[] componentsInChildren = val.GetComponentsInChildren(true); foreach (SkinnedMeshRenderer val2 in componentsInChildren) { bool flag = hashSet.Contains(((Object)((Component)val2).gameObject).name); ((Component)val2).gameObject.SetActive(flag); if (flag) { num2++; if ((Object)(object)value != (Object)null) { ((Renderer)val2).sharedMaterial = value; } } } if (num2 == 0) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("[NpcSkins] No parts from key were found in template: " + key)); } Object.Destroy((Object)(object)val); return null; } val.SetActive(true); ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)("[NpcSkins] Model built: parts " + num2 + "/" + hashSet.Count + ", material " + (((Object)(object)value != (Object)null) ? ("'" + text2 + "'") : "default"))); } return val; } catch (Exception ex) { ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogWarning((object)("[NpcSkins] BuildModel: " + ex.Message)); } return null; } } private static string BuildKey(CharacterCustomizer cc) { List list = new List(); SkinnedMeshRenderer[] componentsInChildren = ((Component)cc).GetComponentsInChildren(false); foreach (SkinnedMeshRenderer val in componentsInChildren) { if (((Component)val).gameObject.activeSelf) { list.Add(((Object)((Component)val).gameObject).name); } } if (list.Count == 0) { return null; } list.Sort(StringComparer.OrdinalIgnoreCase); StringBuilder stringBuilder = new StringBuilder("npc:"); stringBuilder.Append(CleanMatName(((Object)(object)cc.mat != (Object)null) ? ((Object)cc.mat).name : "")); stringBuilder.Append('|'); stringBuilder.Append(string.Join(",", list)); return stringBuilder.ToString(); } private static void EnsureTemplate(CharacterCustomizer cc) { //IL_013f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_template != (Object)null) { return; } try { GameObject val = Object.Instantiate(((Component)cc).gameObject); ((Object)val).name = "CoopNpcSkinTemplate"; val.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)1; MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren(true); foreach (MonoBehaviour val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)val2); } } Collider[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (Collider val3 in componentsInChildren2) { if ((Object)(object)val3 != (Object)null) { Object.DestroyImmediate((Object)(object)val3); } } Rigidbody[] componentsInChildren3 = val.GetComponentsInChildren(true); foreach (Rigidbody val4 in componentsInChildren3) { if ((Object)(object)val4 != (Object)null) { Object.DestroyImmediate((Object)(object)val4); } } Animator[] componentsInChildren4 = val.GetComponentsInChildren(true); foreach (Animator val5 in componentsInChildren4) { if ((Object)(object)val5 != (Object)null) { Object.DestroyImmediate((Object)(object)val5); } } MeshRenderer component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } MeshFilter component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.DestroyImmediate((Object)(object)component2); } val.transform.localScale = ((Component)cc).transform.lossyScale; _template = val; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("[NpcSkins] NPC template captured from '" + ((Object)((Component)cc).gameObject).name + "'")); } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogWarning((object)("[NpcSkins] Failed to capture template: " + ex.Message)); } } } private static bool HasLiveSkinnedBody(GameObject go) { Transform[] componentsInChildren = go.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (((Object)componentsInChildren[i]).name.ToLowerInvariant().Contains("combiner")) { return false; } } SkinnedMeshRenderer[] componentsInChildren2 = go.GetComponentsInChildren(true); foreach (SkinnedMeshRenderer val in componentsInChildren2) { if (((Renderer)val).enabled && (Object)(object)val.sharedMesh != (Object)null) { return true; } } return false; } private static void CacheMaterial(Material mat) { if (!((Object)(object)mat == (Object)null)) { string text = CleanMatName(((Object)mat).name); if (!string.IsNullOrEmpty(text) && !_materials.ContainsKey(text)) { _materials[text] = mat; } } } private static string CleanMatName(string raw) { if (string.IsNullOrEmpty(raw)) { return ""; } string text = raw; while (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (Instance)".Length); } return text.Replace('|', '_').Replace(',', '_').Trim(); } private static string MakeDisplayName(CharacterCustomizer cc) { string name = ((Object)((Component)cc).gameObject).name; Transform parent = ((Component)cc).transform.parent; if ((Object)(object)parent != (Object)null && (Object)(object)((Component)parent).GetComponent() != (Object)null) { name = ((Object)parent).name; } name = name.Replace("(Clone)", "").Trim(); string text = "NPC: " + name; int num = 2; string candidate = text; while (_skins.Values.Any((Skin s) => string.Equals(s.DisplayName, candidate, StringComparison.OrdinalIgnoreCase))) { candidate = text + " #" + num++; } return candidate; } } }