Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Hearthlink v0.1.9
patchers/Hearthlink.Patcher.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Hearthlink.Patcher")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.9.0")] [assembly: AssemblyInformationalVersion("0.1.9+4b1c64c9e7d1f5c827b9e557add22f9590df6a41")] [assembly: AssemblyProduct("Hearthlink.Patcher")] [assembly: AssemblyTitle("Hearthlink.Patcher")] [assembly: AssemblyVersion("0.1.9.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Hearthlink.Preload { public static class Patcher { private static readonly ManualLogSource Log = Logger.CreateLogSource("Hearthlink Preload"); private static string plugin; public static IEnumerable<string> TargetDLLs { get { plugin = Directory.GetFiles(Paths.PluginPath, "Hearthlink.dll", SearchOption.AllDirectories).SingleOrDefault(); if (plugin == null) { Log.LogMessage((object)"Hearthlink plugin absent; preloader idle."); return Array.Empty<string>(); } return new string[2] { "assembly_valheim.dll", "com.rlabrecque.steamworks.net.dll" }; } } public static void Patch(AssemblyDefinition assembly) { if (plugin == null) { return; } string name = "Hearthlink.Preload." + ((AssemblyNameReference)assembly.Name).Name; AppDomain.CurrentDomain.SetData(name, false); if (((AssemblyNameReference)assembly.Name).Name == "assembly_valheim") { Instruction obj = QueueLimitInstruction(assembly); ExposeReferencedMembers(assembly, plugin); obj.Operand = 102400; } else { if (!(((AssemblyNameReference)assembly.Name).Name == "com.rlabrecque.steamworks.net")) { return; } AddSteamEnums(assembly); } AppDomain.CurrentDomain.SetData(name, true); } public static void AddSteamEnums(AssemblyDefinition assembly) { //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown TypeDefinition type = assembly.MainModule.GetType("Steamworks.ESteamNetworkingConfigValue"); if (type == null) { throw new InvalidOperationException("Required Steam enum type is unavailable"); } string[] array = new string[4] { "RecvBufferSize", "RecvBufferMessages", "RecvMaxMessageSize", "RecvMaxSegmentsPerPacket" }; for (int i = 0; i < array.Length; i++) { string name = "k_ESteamNetworkingConfig_" + array[i]; FieldDefinition val = ((IEnumerable<FieldDefinition>)type.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == name)); if (val != null && (!val.HasConstant || Convert.ToInt32(val.Constant) != 47 + i)) { throw new InvalidOperationException("Steam enum value mismatch: " + name); } } for (int num = 0; num < array.Length; num++) { string name2 = "k_ESteamNetworkingConfig_" + array[num]; if (!((IEnumerable<FieldDefinition>)type.Fields).Any((FieldDefinition f) => ((MemberReference)f).Name == name2)) { type.Fields.Add(new FieldDefinition(name2, (FieldAttributes)32854, (TypeReference)(object)type) { Constant = 47 + num }); } } } public static void RaiseQueueLimit(AssemblyDefinition assembly) { QueueLimitInstruction(assembly).Operand = 102400; } private static Instruction QueueLimitInstruction(AssemblyDefinition assembly) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) TypeDefinition type = assembly.MainModule.GetType("ZDOMan"); MethodDefinition[] obj = ((type != null) ? ((IEnumerable<MethodDefinition>)type.Methods).Where((MethodDefinition m) => ((MemberReference)m).Name == "SendZDOs" && m.HasBody).ToArray() : null) ?? Array.Empty<MethodDefinition>(); List<Instruction> list = new List<Instruction>(); MethodDefinition[] array = obj; for (int num = 0; num < array.Length; num++) { Collection<Instruction> instructions = array[num].Body.Instructions; for (int num2 = 0; num2 < instructions.Count; num2++) { object operand = instructions[num2].Operand; MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); if (val == null || !(((MemberReference)val).Name == "GetSendQueueSize")) { continue; } for (int num3 = num2 + 1; num3 < Math.Min(instructions.Count, num2 + 7); num3++) { if (instructions[num3].OpCode == OpCodes.Ldc_I4 && instructions[num3].Operand is int num4 && (num4 == 10240 || num4 == 102400)) { list.Add(instructions[num3]); } } } } if (list.Count != 1) { throw new InvalidOperationException("Unexpected SendZDOs queue layout; refusing broad IL replacement"); } return list[0]; } private static IEnumerable<TypeDefinition> Types(IEnumerable<TypeDefinition> types) { foreach (TypeDefinition t in types) { yield return t; foreach (TypeDefinition item in Types((IEnumerable<TypeDefinition>)t.NestedTypes)) { yield return item; } } } public static void ExposeReferencedMembers(AssemblyDefinition game, string file) { AssemblyDefinition val = AssemblyDefinition.ReadAssembly(file); try { Dictionary<string, TypeDefinition> dictionary = Types((IEnumerable<TypeDefinition>)game.MainModule.Types).ToDictionary((TypeDefinition t) => ((MemberReference)t).FullName); List<Action> list = new List<Action>(); foreach (TypeReference typeReference in val.MainModule.GetTypeReferences()) { if (!(typeReference.Scope.Name != ((AssemblyNameReference)game.Name).Name)) { if (!dictionary.TryGetValue(((MemberReference)typeReference).FullName, out var type)) { throw new InvalidOperationException("Required game type unavailable: " + ((MemberReference)typeReference).FullName); } list.Add(delegate { Public(type); }); } } foreach (MemberReference memberReference in val.MainModule.GetMemberReferences()) { if (memberReference.DeclaringType.Scope.Name != ((AssemblyNameReference)game.Name).Name) { continue; } if (!dictionary.TryGetValue(((MemberReference)memberReference.DeclaringType).FullName, out var type2)) { throw new InvalidOperationException("Required declaring type unavailable: " + ((MemberReference)memberReference.DeclaringType).FullName); } list.Add(delegate { Public(type2); }); FieldReference field = (FieldReference)(object)((memberReference is FieldReference) ? memberReference : null); if (field != null) { FieldDefinition target = ((IEnumerable<FieldDefinition>)type2.Fields).SingleOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == ((MemberReference)field).Name)); if (target == null || ((MemberReference)((FieldReference)target).FieldType).FullName != ((MemberReference)field.FieldType).FullName) { throw new InvalidOperationException("Required game field unavailable: " + ((MemberReference)field).FullName); } list.Add(delegate { target.IsPublic = true; }); continue; } MethodReference val2 = (MethodReference)(object)((memberReference is MethodReference) ? memberReference : null); if (val2 == null) { continue; } GenericInstanceMethod val3 = (GenericInstanceMethod)(object)((val2 is GenericInstanceMethod) ? val2 : null); MethodReference signature = ((val3 != null) ? ((MethodSpecification)val3).ElementMethod : val2); MethodDefinition[] targets = ((IEnumerable<MethodDefinition>)type2.Methods).Where((MethodDefinition m) => ((MemberReference)m).Name == ((MemberReference)signature).Name && ((MethodReference)m).GenericParameters.Count == signature.GenericParameters.Count && Signature(((MethodReference)m).ReturnType) == Signature(signature.ReturnType) && ((MethodReference)m).Parameters.Count == signature.Parameters.Count && ((IEnumerable<ParameterDefinition>)((MethodReference)m).Parameters).Select((ParameterDefinition p) => Signature(((ParameterReference)p).ParameterType)).SequenceEqual(((IEnumerable<ParameterDefinition>)signature.Parameters).Select((ParameterDefinition p) => Signature(((ParameterReference)p).ParameterType)))).ToArray(); if (targets.Length != 1) { throw new InvalidOperationException("Required game method missing/ambiguous: " + ((MemberReference)val2).FullName); } list.Add(delegate { targets[0].IsPublic = true; }); } foreach (Action item in list) { item(); } } finally { ((IDisposable)val)?.Dispose(); } } private static string Signature(TypeReference type) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 GenericParameter val = (GenericParameter)(object)((type is GenericParameter) ? type : null); if (val != null) { return (((int)val.Type == 1) ? "!!" : "!") + val.Position; } GenericInstanceType val2 = (GenericInstanceType)(object)((type is GenericInstanceType) ? type : null); if (val2 != null) { return ((MemberReference)((TypeSpecification)val2).ElementType).FullName + "<" + string.Join(",", ((IEnumerable<TypeReference>)val2.GenericArguments).Select(Signature)) + ">"; } ArrayType val3 = (ArrayType)(object)((type is ArrayType) ? type : null); if (val3 != null) { return Signature(((TypeSpecification)val3).ElementType) + "[" + val3.Rank + "]"; } ByReferenceType val4 = (ByReferenceType)(object)((type is ByReferenceType) ? type : null); if (val4 != null) { return Signature(((TypeSpecification)val4).ElementType) + "&"; } PointerType val5 = (PointerType)(object)((type is PointerType) ? type : null); if (val5 != null) { return Signature(((TypeSpecification)val5).ElementType) + "*"; } return ((MemberReference)type).FullName; } private static void Public(TypeDefinition t) { if (((TypeReference)t).IsNested) { t.IsNestedPublic = true; if (t.DeclaringType != null) { Public(t.DeclaringType); } } else { t.IsPublic = true; } } } }
plugins/Hearthlink.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using FiresGhettoNetworkMod; using FiresGhettoNetworkMod.AutoTune; using HarmonyLib; using Hearthlink; using MagicaCloth2; using Meshheim.Compatibility; using Meshheim.EasyBake; using Meshheim.Patches; using Meshheim.Standalone.Async; using Meshheim.Standalone.Bridge; using Meshheim.Standalone.Config; using Meshheim.Standalone.Help; using Meshheim.Standalone.Logging; using Meshheim.Standalone.Pieces; using Meshheim.Utilities; using Microsoft.CodeAnalysis; using PlayFab.Party; using Steamworks; using Unity.Profiling; using UnityEngine; using UnityEngine.Rendering; using Yokko.CharacterAnchor; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Hearthlink")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.9.0")] [assembly: AssemblyInformationalVersion("0.1.9+4b1c64c9e7d1f5c827b9e557add22f9590df6a41")] [assembly: AssemblyProduct("Hearthlink")] [assembly: AssemblyTitle("Hearthlink")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.9.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Yokko.CharacterAnchor { [BepInPlugin("Yokko.Hearthlink.Reconnect", "Hearthlink Reconnect", "0.1.9")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class CharacterAnchorPlugin : BaseUnityPlugin { private sealed class Session { public bool Capable; public ZdoKey DeadCharacter; public double NextBeat; public double NextQuery; public double NextReject; public double Joined = Now; public bool Captured; public bool HasPosition; public long Character; public Vector3 Position; public ZNetPeer Peer; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class ConnectionPatch { private static void Postfix(ZNetPeer peer) { Instance?.Register(peer); } } [HarmonyPatch(typeof(Player), "OnDeath")] private static class DeathPatch { [HarmonyPriority(800)] private static void Prefix(Player __instance) { CharacterAnchorPlugin instance = Instance; if (!((Object)(object)instance != (Object)null) || !((Object)(object)__instance == (Object)(object)Player.m_localPlayer) || instance.clientRpc == null) { return; } try { instance.clientRpc.Invoke("HL_DeathFence_1", new object[1] { 2 }); instance.nextTrack = 0.0; if (instance.remoteShips) { instance.sentClear = false; instance.ClearShip(instance.clientRpc); } } catch (Exception ex) { ((BaseUnityPlugin)instance).Logger.LogError((object)("Death checkpoint/ship fence failed: " + ex.Message)); } } } [HarmonyPatch(typeof(Game), "FindSpawnPoint")] private static class SpawnPatch { private static bool Prefix(Game __instance, bool ___m_firstSpawn, bool ___m_respawnAfterDeath, ref Vector3 point, ref bool usedLogoutPoint, ref bool __result) { try { return (Object)(object)Instance == (Object)null || Instance.BeforeSpawn(__instance, ___m_firstSpawn, ___m_respawnAfterDeath, ref point, ref usedLogoutPoint, ref __result); } catch (Exception ex) { Instance?.FinishFallback("Restore exception: " + ex.Message); return true; } } } [HarmonyPatch(typeof(ZNet), "Shutdown")] private static class ShutdownPatch { private static void Prefix() { Instance?.CaptureAll(); Instance?.CloseStore(); Instance?.ResetClient(); } } [HarmonyPatch(typeof(ZNet), "Disconnect")] private static class DisconnectPatch { private static void Prefix(ZNetPeer peer) { Instance?.CaptureDisconnect(peer); } } [HarmonyPatch(typeof(Game), "Shutdown")] private static class LeavingPatch { private static void Prefix() { CharacterAnchorPlugin instance = Instance; if ((Object)(object)instance == (Object)null) { return; } if (instance.clientRpc != null && Object.op_Implicit((Object)(object)Player.m_localPlayer) && instance.configured && instance.remoteShips) { try { instance.nextTrack = 0.0; instance.Track(instance.clientRpc, Player.m_localPlayer); } catch (Exception ex) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)("Final ship registration failed: " + ex.Message)); } } instance.clientLeaving = true; instance.CaptureAll(); } } [HarmonyPatch(typeof(Ship), "OnTriggerEnter")] private static class BoardingPatch { private static void Postfix(Collider collider) { CharacterAnchorPlugin instance = Instance; if ((Object)(object)instance == (Object)null || instance.clientLeaving || !instance.configured || !instance.remoteEnabled || !instance.remoteShips || !Object.op_Implicit((Object)(object)collider) || (Object)(object)((Component)collider).GetComponent<Player>() != (Object)(object)Player.m_localPlayer || instance.clientRpc == null) { return; } try { instance.nextTrack = 0.0; instance.Track(instance.clientRpc, Player.m_localPlayer); } catch (Exception ex) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)("Boarding notification failed: " + ex.Message)); } } } [HarmonyPatch(typeof(Ship), "OnTriggerExit")] private static class DisembarkPatch { private static void Postfix() { if ((Object)(object)Instance != (Object)null && !Instance.clientLeaving) { Instance.nextTrack = 0.0; } } } public const string Guid = "Yokko.Hearthlink.Reconnect"; private const string NativeGuid = "Yokko.Hearthlink"; private const string Hello = "HL_Hello_1"; private const string Settings = "HL_Settings_1"; private const string Heartbeat = "HL_Ship_1"; private const string Cleared = "HL_ClearAck_1"; private const string Query = "HL_Query_1"; private const string Restore = "HL_Restore_1"; internal static CharacterAnchorPlugin Instance; private Harmony harmony; private ConfigEntry<bool> featureEnabled; private ConfigEntry<bool> ships; private ConfigEntry<bool> debug; private ConfigEntry<float> offset; private ConfigEntry<float> grace; private readonly Dictionary<ZRpc, Session> sessions = new Dictionary<ZRpc, Session>(); private readonly List<ZRpc> removals = new List<ZRpc>(); private StateStore store; private bool storeDirty; private double nextPersist; private ZNet net; private long world; private bool remoteEnabled; private bool remoteShips; private bool configured; private float remoteTracking = 2f; private float remoteOffset = 0.35f; private float remoteGrace = 6f; private double nextHello; private double nextTrack; private Ship lastShip; private ZdoKey acknowledgedShip; private bool clientLeaving; private Vector3 fallbackPosition; private bool hasFallback; private readonly ContactGrace contact = new ContactGrace(); private ZdoKey lastSentShip; private bool sentClear; private ZRpc clientRpc; private bool restoreStarted; private bool restoreFinished; private double restoreDeadline; private double nextQuery; private ShipRecord restoreRecord; private Vector3 restorePosition; private readonly RaycastHit[] deckHits = (RaycastHit[])(object)new RaycastHit[64]; private readonly Collider[] occupancy = (Collider[])(object)new Collider[32]; private const string Fence = "HL_DeathFence_1"; private static readonly FieldInfo DeadZdos = AccessTools.Field(typeof(ZDOMan), "m_deadZDOs"); private double nextDeckAttempt; private static double Now => Time.realtimeSinceStartupAsDouble; private static long Utc => DateTimeOffset.UtcNow.ToUnixTimeSeconds(); public ConfigFile Config => HearthlinkPlugin.Instance.ServerConfig; internal int RuntimeCount => sessions.Count + removals.Count + (Object.op_Implicit((Object)(object)lastShip) ? 1 : 0) + ((restoreRecord != null) ? 1 : 0); private void Awake() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown Instance = this; featureEnabled = Config.Bind<bool>("Reconnect", "Enabled", true, "Enable the exact-ship reconnect module. Character inventories use vanilla saves."); ships = Config.Bind<bool>("Reconnect", "ReturnToShip", true, "Protect initial reconnect spawns on networked Ship components."); debug = Config.Bind<bool>("Logging", "DebugLogging", false, "Log ship metadata. Errors and restore outcomes are always logged."); offset = Config.Bind<float>("Reconnect", "DeckSafetyOffset", 0.35f, new ConfigDescription("Vertical clearance above a verified deck surface.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); grace = Config.Bind<float>("Reconnect", "LeaveShipGraceSeconds", 6f, new ConfigDescription("Continuous ground contact before clearing metadata.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>())); Config.Save(); harmony = new Harmony("Yokko.Hearthlink.Reconnect"); try { Type[] nestedTypes = typeof(CharacterAnchorPlugin).GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic); foreach (Type type in nestedTypes) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0) { harmony.CreateClassProcessor(type).Patch(); } } } catch (Exception ex) { harmony.UnpatchSelf(); Instance = null; ((BaseUnityPlugin)this).Logger.LogError((object)("CharacterAnchor integration failed; all own patches removed: " + ex)); return; } ((MonoBehaviour)this).StartCoroutine(TickLoop()); ((BaseUnityPlugin)this).Logger.LogDebug((object)"Hearthlink exact-ship reconnect module ready."); } internal void Trace(string text) { if (debug.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)text); } } private IEnumerator TickLoop() { WaitForSecondsRealtime delay = new WaitForSecondsRealtime(0.25f); while (true) { try { Tick(); } catch (Exception ex) { SessionDiagnostics.Rejected("reconnect tick " + ex.GetType().Name); try { EndSession(); } catch (Exception) { } } yield return delay; } } private void Tick() { if (!SessionLifecycle.Observe() || ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return; } if ((Object)(object)net != (Object)(object)ZNet.instance) { CloseStore(); net = ZNet.instance; sessions.Clear(); ResetClient(); world = 0L; foreach (ZNetPeer peer in net.GetPeers()) { Register(peer); } } if (net.IsServer()) { long worldUID = net.GetWorldUID(); if (worldUID == 0L) { return; } if (store == null || world != worldUID) { CloseStore(); world = worldUID; store = new StateStore(Path.Combine(Paths.ConfigPath, "Hearthlink", "reconnect-world-" + world + ".bin"), world, Path.Combine(Paths.ConfigPath, "HeimwardNative", "reconnect-world-" + world + ".bin"), Path.Combine(Paths.ConfigPath, "CharacterAnchor", "world-" + world + ".bin")); if (store.Imported) { storeDirty = true; ((BaseUnityPlugin)this).Logger.LogMessage((object)"Imported previous ship reconnect metadata; original file preserved."); } } Exception ex = store.TakeError(); if (ex != null) { ((BaseUnityPlugin)this).Logger.LogError((object)("Metadata persistence: " + ex.Message)); storeDirty = true; } if (storeDirty && Now >= nextPersist) { store.Queue(); storeDirty = false; nextPersist = Now + 1.0; } CachePositions(); removals.Clear(); foreach (KeyValuePair<ZRpc, Session> session in sessions) { if (!session.Key.IsConnected()) { removals.Add(session.Key); } } { foreach (ZRpc removal in removals) { CaptureDisconnect(sessions[removal].Peer); sessions.Remove(removal); } return; } } ZNetPeer serverPeer = net.GetServerPeer(); if (serverPeer != null && serverPeer.IsReady() && serverPeer.m_rpc.IsConnected()) { if (clientRpc != serverPeer.m_rpc) { ResetClient(); clientRpc = serverPeer.m_rpc; } if (Now >= nextHello) { nextHello = Now + 5.0; serverPeer.m_rpc.Invoke("HL_Hello_1", new object[1] { "0.1.2" }); } if (configured && remoteEnabled && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !clientLeaving && remoteShips) { Track(serverPeer.m_rpc, Player.m_localPlayer); } } } private void ResetClient() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) clientRpc = null; configured = false; remoteEnabled = (remoteShips = false); restoreStarted = (restoreFinished = false); restoreRecord = null; lastShip = null; lastSentShip = (acknowledgedShip = ZDOID.None); sentClear = false; clientLeaving = false; hasFallback = false; fallbackPosition = Vector3.zero; restorePosition = Vector3.zero; restoreDeadline = (nextQuery = (nextDeckAttempt = 0.0)); Array.Clear(deckHits, 0, deckHits.Length); Array.Clear(occupancy, 0, occupancy.Length); contact.Reset(); nextHello = (nextTrack = 0.0); } internal void EndSession() { try { CloseStore(); } finally { store = null; storeDirty = false; net = null; world = 0L; nextPersist = 0.0; sessions.Clear(); removals.Clear(); ResetClient(); } } internal void ForgetPeer(ZRpc rpc) { if (sessions.Remove(rpc)) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Reconnect peer removed; remaining=" + sessions.Count)); } } private void RegisterSession<T>(ZRpc rpc, string name, Action<ZRpc, T> handler) { long epoch = SessionEpoch.Current; rpc.Register<T>(name, (Action<ZRpc, T>)delegate(ZRpc sender, T value) { if (!SessionEpoch.IsCurrent(epoch) || !SessionLifecycle.Active || !sessions.ContainsKey(sender)) { SessionDiagnostics.Rejected("old reconnect RPC"); } else { handler(sender, value); } }); } private void CloseStore() { if (store != null) { if (storeDirty) { store.Queue(); } store.Drain(); store = null; } storeDirty = false; } private void OnDestroy() { ((MonoBehaviour)this).StopAllCoroutines(); EndSession(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private static ZNetPeer Peer(ZRpc rpc) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return null; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.m_rpc == rpc) { return peer; } } return null; } private static string Key(ZNetPeer peer) { return peer.m_socket.GetHostName() + "\n" + peer.m_playerName.ToLowerInvariant(); } private unsafe void Register(ZNetPeer peer) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown if (!SessionLifecycle.Observe()) { return; } if ((Object)(object)net != (Object)(object)ZNet.instance) { CloseStore(); net = ZNet.instance; sessions.Clear(); ResetClient(); world = 0L; } ZRpc rpc = peer.m_rpc; if (!net.IsServer()) { clientRpc = rpc; } sessions[rpc] = new Session { Peer = peer }; RegisterSession<string>(rpc, "HL_Hello_1", OnHello); RegisterSession<ZPackage>(rpc, "HL_Settings_1", OnSettings); RegisterSession<int>(rpc, "HL_DeathFence_1", OnFence); RegisterSession<ZPackage>(rpc, "HL_Ship_1", OnHeartbeat); RegisterSession(rpc, "HL_BoardAck_1", delegate(ZRpc sender, ZDOID id) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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) if (FromServer(sender) && id == lastSentShip) { if (acknowledgedShip != id) { ManualLogSource logger = ((BaseUnityPlugin)this).Logger; ZDOID val = id; logger.LogInfo((object)("Server confirmed ship registration: " + ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString())); } acknowledgedShip = id; } }); RegisterSession(rpc, "HL_ClearAck_1", delegate(ZRpc sender, long character) { if (FromServer(sender) && Object.op_Implicit((Object)(object)Game.instance) && !Object.op_Implicit((Object)(object)lastShip) && lastSentShip.IsNone() && character == Game.instance.GetPlayerProfile().GetPlayerID()) { sentClear = true; } }); long epoch = SessionEpoch.Current; rpc.Register("HL_Query_1", (Method)delegate(ZRpc sender) { if (SessionEpoch.IsCurrent(epoch) && SessionLifecycle.Active && sessions.ContainsKey(sender)) { OnQuery(sender); } }); RegisterSession<ZPackage>(rpc, "HL_Restore_1", OnRestore); } private void OnHello(ZRpc rpc, string version) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { ZNetPeer val = Peer(rpc); if (val != null && val.IsReady() && sessions.TryGetValue(rpc, out var value) && !(version != "0.1.2")) { value.Capable = true; ZPackage val2 = new ZPackage(); val2.Write(featureEnabled.Value); val2.Write(ships.Value); val2.Write(0.25f); val2.Write(offset.Value); val2.Write(grace.Value); rpc.Invoke("HL_Settings_1", new object[1] { val2 }); } } } private bool FromServer(ZRpc rpc) { if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsServer()) { return ZNet.instance.GetServerPeer()?.m_rpc == rpc; } return false; } private void OnSettings(ZRpc rpc, ZPackage pkg) { if (!FromServer(rpc) || pkg.Size() != 14) { return; } try { bool flag = pkg.ReadBool(); bool flag2 = pkg.ReadBool(); float num = pkg.ReadSingle(); float num2 = pkg.ReadSingle(); float num3 = pkg.ReadSingle(); if (Safety.Finite(num) && !(num < 0.25f) && !(num > 10f) && Safety.Finite(num2) && !(num2 < 0.1f) && !(num2 > 1f) && Safety.Finite(num3) && !(num3 < 2f) && !(num3 > 30f)) { remoteEnabled = flag; remoteShips = flag2; remoteTracking = num; remoteOffset = num2; remoteGrace = num3; configured = true; } } catch (Exception ex) { Trace("Rejected settings: " + ex.Message); } } private unsafe void Track(ZRpc rpc, Player player) { //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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_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_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: 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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Game.instance) || Game.instance.IsShuttingDown() || clientLeaving || Game.instance.InIntro(true) || ((Character)player).IsTeleporting()) { return; } if (((Character)player).IsDead()) { ClearShip(rpc); return; } Ship val = ((Character)player).GetStandingOnShip(); if (!Object.op_Implicit((Object)(object)val)) { Ship localShip = Ship.GetLocalShip(); if (Object.op_Implicit((Object)(object)localShip) && localShip.IsPlayerInBoat(player) && !((Character)player).IsSwimming()) { val = localShip; } } if (Object.op_Implicit((Object)(object)val)) { lastShip = val; contact.OnShip(Now); sentClear = false; ZNetView component = ((Component)val).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid()) { return; } Vector3 val2 = ((Component)val).transform.InverseTransformPoint(((Component)player).transform.position); if (!Safety.Local(val2.x, val2.y, val2.z)) { return; } ZDOID uid = component.GetZDO().m_uid; if (!(uid == acknowledgedShip) && (!(uid == lastSentShip) || !(Now < nextTrack))) { nextTrack = Now + 2.0; ZPackage val3 = new ZPackage(); val3.Write(Game.instance.GetPlayerProfile().GetPlayerID()); val3.Write(uid); val3.Write(val2); rpc.Invoke("HL_Ship_1", new object[1] { val3 }); if (lastSentShip != uid) { ManualLogSource logger = ((BaseUnityPlugin)this).Logger; ZDOID val4 = uid; logger.LogInfo((object)("Registering player on ship " + ((object)(*(ZDOID*)(&val4))/*cast due to .constrained prefix*/).ToString())); } lastSentShip = uid; } } else if (contact.ShouldClear(Now, ((Character)player).IsOnGround() || ((Character)player).IsSwimming(), remoteGrace)) { ClearShip(rpc); } } private void ClearShip(ZRpc rpc) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //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_009a: Unknown result type (might be due to invalid IL or missing references) if (!sentClear && Object.op_Implicit((Object)(object)Game.instance) && !clientLeaving && !Game.instance.IsShuttingDown() && !(Now < nextTrack)) { nextTrack = Now + 2.0; ZPackage val = new ZPackage(); val.Write(Game.instance.GetPlayerProfile().GetPlayerID()); val.Write(ZDOID.None); val.Write(Vector3.zero); rpc.Invoke("HL_Ship_1", new object[1] { val }); lastShip = null; lastSentShip = (acknowledgedShip = ZDOID.None); } } private static bool WorldPoint(Vector3 point) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return Safety.World(point.x, point.y, point.z); } private ZDO Resolve(ShipRecord record) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!record.Valid(Utc)) { return null; } ZDO zDO = ZDOMan.instance.GetZDO(new ZDOID(record.ShipUser, record.ShipId)); if (zDO == null || !zDO.Persistent || zDO.GetPrefab() != record.Prefab || !WorldPoint(zDO.GetPosition())) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab()); if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab.GetComponent<Ship>())) { return null; } return zDO; } private unsafe void OnHeartbeat(ZRpc rpc, ZPackage pkg) { //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_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_00b0: 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_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_026f: 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_0273: 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_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || !featureEnabled.Value || !ships.Value || store == null || pkg.Size() != 32) { return; } ZNetPeer val = Peer(rpc); if (val == null || !val.IsReady() || !sessions.TryGetValue(rpc, out var value) || !value.Capable) { return; } bool flag = Now >= value.NextBeat; try { long num = pkg.ReadLong(); ZDOID val2 = pkg.ReadZDOID(); Vector3 val3 = pkg.ReadVector3(); if (((ZDOID)(ref val.m_characterID)).IsNone()) { return; } ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); if (zDO == null || !Safety.Owner(val.m_uid, zDO.GetOwner()) || !Safety.Character(num, zDO.GetLong(ZDOVars.s_playerID, 0L)) || !WorldPoint(zDO.GetPosition())) { RejectBoard(value, "Character identity/position not replicated yet."); return; } string text = Key(val); if (text.Length > 256) { return; } if (((ZDOID)(ref val2)).IsNone()) { if (store.Records.Remove(text)) { storeDirty = true; Trace("Ship state cleared for " + val.m_playerName); } rpc.Invoke("HL_ClearAck_1", new object[1] { num }); } else { if (!flag) { return; } value.NextBeat = Now + 0.75; if (!Safety.Local(val3.x, val3.y, val3.z)) { return; } ZDO zDO2 = ZDOMan.instance.GetZDO(val2); if (zDO2 == null) { RejectBoard(value, "Ship ZDO not available."); return; } ShipRecord record = new ShipRecord(num, ((ZDOID)(ref val2)).UserID, ((ZDOID)(ref val2)).ID, zDO2.GetPrefab(), val3.x, val3.y, val3.z, Utc); if (Resolve(record) == null) { RejectBoard(value, "Ship identity/prefab validation failed."); return; } GameObject prefab = ZNetScene.instance.GetPrefab(zDO2.GetPrefab()); Vector3 localScale = prefab.transform.localScale; Vector3 val4 = zDO2.GetPosition() + zDO2.GetRotation() * Vector3.Scale(val3, localScale); if (!WorldPoint(val4) || Vector3.Distance(val4, zDO.GetPosition()) > 8f) { RejectBoard(value, "Player and ship replication differ by more than 8 metres; registration will retry."); } else if (!NearShipGeometry(prefab, val3)) { RejectBoard(value, "Position is outside the ship collision geometry."); } else if (store.Records.ContainsKey(text) || store.Records.Count < 4096) { if (!store.Records.TryGetValue(text, out var value2) || value2.Character != num || value2.ShipUser != ((ZDOID)(ref val2)).UserID || value2.ShipId != ((ZDOID)(ref val2)).ID) { Vector3 position = zDO.GetPosition(); store.Records[text] = new ShipRecord(num, ((ZDOID)(ref val2)).UserID, ((ZDOID)(ref val2)).ID, zDO2.GetPrefab(), 0f, 0f, 0f, Utc, hasFallback: true, position.x, position.y, position.z); storeDirty = true; ManualLogSource logger = ((BaseUnityPlugin)this).Logger; string playerName = val.m_playerName; ZDOID val5 = val2; logger.LogInfo((object)("Ship registered for " + playerName + ": " + ((object)(*(ZDOID*)(&val5))/*cast due to .constrained prefix*/).ToString())); } rpc.Invoke("HL_BoardAck_1", new object[1] { val2 }); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Rejected ship metadata: " + ex.Message)); } } private void RejectBoard(Session session, string reason) { if (!(Now < session.NextReject)) { session.NextReject = Now + 10.0; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Ship registration deferred for " + session.Peer?.m_playerName + ": " + reason)); } } private static bool NearShipGeometry(GameObject prefab, Vector3 local) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true); Bounds val4 = default(Bounds); foreach (Collider val in componentsInChildren) { if (val.isTrigger) { continue; } BoxCollider val2 = (BoxCollider)(object)((val is BoxCollider) ? val : null); if (val2 != null) { Vector3 val3 = ((Component)val).transform.InverseTransformPoint(prefab.transform.TransformPoint(local)); ((Bounds)(ref val4))..ctor(val2.center, val2.size + new Vector3(2f, 5f, 2f)); if (((Bounds)(ref val4)).Contains(val3)) { return true; } continue; } MeshCollider val5 = (MeshCollider)(object)((val is MeshCollider) ? val : null); if (val5 != null && Object.op_Implicit((Object)(object)val5.sharedMesh)) { Vector3 val6 = ((Component)val).transform.InverseTransformPoint(prefab.transform.TransformPoint(local)); Bounds bounds = val5.sharedMesh.bounds; ((Bounds)(ref bounds)).Expand(new Vector3(2f, 5f, 2f)); if (((Bounds)(ref bounds)).Contains(val6)) { return true; } } } return false; } private void OnFence(ZRpc rpc, int kind) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (kind != 2 || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || !sessions.TryGetValue(rpc, out var value) || !value.Capable) { return; } ZNetPeer val = Peer(rpc); if (val != null) { value.DeadCharacter = val.m_characterID; value.HasPosition = false; if (store != null && store.Records.Remove(Key(val))) { storeDirty = true; } } } private void CachePositions() { foreach (ZNetPeer peer in net.GetPeers()) { if (sessions.TryGetValue(peer.m_rpc, out var value) && value.Capable && !value.Captured) { CachePosition(peer, value); } } } private void CachePosition(ZNetPeer peer, Session session) { //IL_002b: 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_00ad: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if (!featureEnabled.Value || peer == null || ((ZDOID)(ref peer.m_characterID)).IsNone()) { return; } ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(peer.m_characterID) : null); if (val == null || !Safety.Owner(peer.m_uid, val.GetOwner())) { return; } if (val.GetBool(ZDOVars.s_dead, false) || session.DeadCharacter == peer.m_characterID) { session.HasPosition = false; if (store != null && store.Records.Remove(Key(peer))) { storeDirty = true; } return; } long num = val.GetLong(ZDOVars.s_playerID, 0L); Vector3 position = val.GetPosition(); if (num != 0L && WorldPoint(position)) { session.Character = num; session.Position = position; session.HasPosition = true; } } private void CaptureDisconnect(ZNetPeer peer) { //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_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || peer == null || store == null || !sessions.TryGetValue(peer.m_rpc, out var value) || !value.Capable || value.Captured) { return; } try { CachePosition(peer, value); value.Captured = true; if (!featureEnabled.Value || !value.HasPosition) { return; } string text = Key(peer); if (text.Length <= 256 && (store.Records.ContainsKey(text) || store.Records.Count < 4096)) { store.Records.TryGetValue(text, out var value2); if (value2 != null && (value2.Character != value.Character || !ships.Value)) { value2 = null; } Vector3 position = value.Position; store.Records[text] = new ShipRecord(value.Character, value2?.ShipUser ?? 0, value2?.ShipId ?? 0, value2?.Prefab ?? 0, 0f, 0f, 0f, Utc, hasFallback: true, position.x, position.y, position.z); store.Queue(); store.Drain(); storeDirty = false; Exception ex = store.TakeError(); if (ex != null) { storeDirty = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Disconnect position persistence failed: " + ex)); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Disconnect position persisted for " + peer.m_playerName)); } } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("Disconnect capture failed: " + ex2)); } } private void CaptureAll() { if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer()) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { CaptureDisconnect(peer); } } private void OnQuery(ZRpc rpc) { //IL_0105: 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_010a: 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_014c: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer()) { return; } ZNetPeer val = Peer(rpc); if (val == null || !val.IsReady() || !sessions.TryGetValue(rpc, out var value) || !value.Capable || Now < value.NextQuery) { return; } value.NextQuery = Now + 1.0; ShipRecord value2 = null; int num; object obj; if (featureEnabled.Value && store != null && store.Records.TryGetValue(Key(val), out value2)) { num = (value2.Valid(Utc) ? 1 : 0); if (num != 0 && ships.Value && value2.ShipId != 0) { obj = Resolve(value2); goto IL_00c7; } } else { num = 0; } obj = null; goto IL_00c7; IL_00c7: ZDO val2 = (ZDO)obj; int num2 = ((num != 0) ? ((val2 != null) ? 2 : ((value2.ShipId == 0 || !ships.Value) ? 1 : 3)) : 0); ZDOID val3 = (ZDOID)((num != 0) ? new ZDOID(value2.ShipUser, value2.ShipId) : ZDOID.None); if (num2 == 3 && DeadZdos?.GetValue(ZDOMan.instance) is IDictionary dictionary && dictionary.Contains(val3)) { num2 = 4; } ZPackage val4 = new ZPackage(); val4.Write(num2); if (num != 0) { val4.Write(value2.Character); val4.Write(value2.HasFallback); val4.Write(new Vector3(value2.WorldX, value2.WorldY, value2.WorldZ)); val4.Write(val3); val4.Write(value2.Prefab); val4.Write(value2.Time); val4.Write((val2 != null) ? val2.GetPosition() : Vector3.zero); } rpc.Invoke("HL_Restore_1", new object[1] { val4 }); } private void OnRestore(ZRpc rpc, ZPackage pkg) { //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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: 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_00e1: 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_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) if (!FromServer(rpc) || !restoreStarted || restoreFinished || (pkg.Size() != 4 && pkg.Size() != 61)) { return; } try { int num = pkg.ReadInt(); if (num == 0 && pkg.Size() == 4) { FinishFallback("No stored reconnect state."); } else { if (num < 1 || num > 4 || pkg.Size() != 61) { return; } long num2 = pkg.ReadLong(); bool flag = pkg.ReadBool(); Vector3 point = pkg.ReadVector3(); ZDOID val = pkg.ReadZDOID(); int prefab = pkg.ReadInt(); long num3 = pkg.ReadLong(); Vector3 point2 = pkg.ReadVector3(); if (!Object.op_Implicit((Object)(object)Game.instance) || num2 != Game.instance.GetPlayerProfile().GetPlayerID() || num3 <= 0 || num3 > Utc + 60 || Utc - num3 > 604800 || (flag && !WorldPoint(point))) { return; } hasFallback = flag; fallbackPosition = point; switch (num) { case 1: case 4: FinishFallback((num == 4) ? "Ship destroyed; using disconnect position." : "Using disconnect position."); return; case 3: return; } ShipRecord shipRecord = new ShipRecord(num2, ((ZDOID)(ref val)).UserID, ((ZDOID)(ref val)).ID, prefab, 0f, 0f, 0f, num3); if (shipRecord.Valid(Utc) && WorldPoint(point2)) { restoreRecord = shipRecord; restorePosition = point2; } } } catch (Exception ex) { FinishFallback("Malformed reconnect reply: " + ex.Message); } } private void FinishFallback(string reason) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) restoreFinished = true; restoreRecord = null; if (hasFallback && Object.op_Implicit((Object)(object)Game.instance)) { Game.instance.GetPlayerProfile().SetLogoutPoint(fallbackPosition); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Reconnect fallback: " + reason)); } internal unsafe bool BeforeSpawn(Game game, bool firstSpawn, bool afterDeath, ref Vector3 point, ref bool usedLogoutPoint, ref bool result) { //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_0119: 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_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: 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_023b: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsServer() || !firstSpawn || afterDeath || game.GetPlayerProfile().m_firstSpawn || restoreFinished) { return true; } if (!restoreStarted) { restoreStarted = true; restoreDeadline = Now + 30.0; nextQuery = 0.0; } if (Now >= restoreDeadline) { FinishFallback("Ship/handshake loading timed out; using last known position."); return true; } if (configured && !remoteEnabled) { restoreFinished = true; return true; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (configured && serverPeer != null && serverPeer.IsReady() && Now >= nextQuery) { nextQuery = Now + 2.0; serverPeer.m_rpc.Invoke("HL_Query_1", Array.Empty<object>()); } point = Vector3.zero; usedLogoutPoint = false; result = false; if (restoreRecord == null) { return false; } ZNet.instance.SetReferencePosition(restorePosition); ZDOID val = default(ZDOID); ((ZDOID)(ref val))..ctor(restoreRecord.ShipUser, restoreRecord.ShipId); GameObject val2 = ZNetScene.instance.FindInstance(val); if (!Object.op_Implicit((Object)(object)val2)) { return false; } Ship component = val2.GetComponent<Ship>(); ZNetView component2 = val2.GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !component2.IsValid() || component2.GetZDO().m_uid != val || component2.GetZDO().GetPrefab() != restoreRecord.Prefab) { FinishFallback("Resolved object is not the registered ship."); return true; } ZNet.instance.SetReferencePosition(((Component)component).transform.position); if (!ZNetScene.instance.IsAreaReady(((Component)component).transform.position) || Now < nextDeckAttempt) { return false; } nextDeckAttempt = Now + 0.25; if (!TryFreeDeck(component, out point)) { return false; } result = true; usedLogoutPoint = true; restoreFinished = true; game.GetPlayerProfile().ClearLoguoutPoint(); ManualLogSource logger = ((BaseUnityPlugin)this).Logger; ZDOID val3 = val; logger.LogInfo((object)("Ship reconnect restore succeeded on " + ((object)(*(ZDOID*)(&val3))/*cast due to .constrained prefix*/).ToString())); return false; } private bool TryFreeDeck(Ship ship, out Vector3 point) { //IL_0008: 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_005d: 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) if (TryDeck(ship, ((Component)ship).transform.position, out point)) { return true; } for (int i = -12; i <= 12; i += 2) { for (int j = -3; j <= 3; j++) { if (TryDeck(ship, ((Component)ship).transform.TransformPoint(new Vector3((float)j, 0f, (float)i)), out point)) { return true; } } } point = Vector3.zero; return false; } private bool TryDeck(Ship ship, Vector3 desired, out Vector3 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //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_0020: 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_0078: 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_0098: 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_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_00d8: 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_00e4: 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_00ee: 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_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_0118: 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_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) point = Vector3.zero; int num = Physics.RaycastNonAlloc(desired + Vector3.up * 10f, Vector3.down, deckHits, 20f, -1, (QueryTriggerInteraction)1); if (num == deckHits.Length) { return false; } float num2 = float.PositiveInfinity; for (int i = 0; i < num; i++) { RaycastHit val = deckHits[i]; if ((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Ship>() != (Object)(object)ship || ((RaycastHit)(ref val)).normal.y < 0.65f) { continue; } float num3 = Mathf.Abs(((RaycastHit)(ref val)).point.y - desired.y); if (!(num3 >= num2) && !(num3 > 6f)) { Vector3 val2 = ((RaycastHit)(ref val)).point + Vector3.up * remoteOffset; if (Physics.OverlapCapsuleNonAlloc(val2 + Vector3.up * 0.4f, val2 + Vector3.up * 1.5f, 0.3f, occupancy, -1, (QueryTriggerInteraction)1) == 0 && WorldPoint(val2) && ZNetScene.instance.IsAreaReady(val2)) { num2 = num3; point = val2; } } } return num2 < float.PositiveInfinity; } } internal sealed class ShipRecord { public readonly long Character; public readonly long ShipUser; public readonly long Time; public readonly uint ShipId; public readonly int Prefab; public readonly float X; public readonly float Y; public readonly float Z; public readonly bool HasFallback; public readonly float WorldX; public readonly float WorldY; public readonly float WorldZ; public ShipRecord(long character, long user, uint id, int prefab, float x, float y, float z, long time, bool hasFallback = false, float worldX = 0f, float worldY = 0f, float worldZ = 0f) { Character = character; ShipUser = user; ShipId = id; Prefab = prefab; X = x; Y = y; Z = z; Time = time; HasFallback = hasFallback; WorldX = worldX; WorldY = worldY; WorldZ = worldZ; } public bool Valid(long now) { if (Character != 0L && ((ShipId != 0 && Prefab != 0 && Safety.Local(X, Y, Z)) || (ShipId == 0 && HasFallback)) && (!HasFallback || Safety.World(WorldX, WorldY, WorldZ)) && Time > 0 && Time <= now + 60) { return now - Time <= 604800; } return false; } } internal static class Safety { public static bool Character(long claimed, long replicated) { if (claimed != 0L) { return claimed == replicated; } return false; } public static bool Owner(long peer, long owner) { if (peer != 0L) { return peer == owner; } return false; } public static bool Finite(float v) { if (!float.IsNaN(v)) { return !float.IsInfinity(v); } return false; } public static bool Local(float x, float y, float z) { if (Finite(x) && Finite(y) && Finite(z) && Math.Abs(x) <= 50f && Math.Abs(y) <= 30f) { return Math.Abs(z) <= 50f; } return false; } public static bool World(float x, float y, float z) { if (Finite(x) && Finite(y) && Finite(z) && Math.Abs(x) < 12000f && Math.Abs(z) < 12000f && y > -500f && y < 5000f) { return x * x + y * y + z * z > 0.01f; } return false; } public static double Phase(long uid, double interval) { return (double)((ulong)uid % 997uL) / 997.0 * interval; } } internal sealed class ContactGrace { private double lastContact; private double groundSince = -1.0; public void OnShip(double now) { lastContact = now; groundSince = -1.0; } public bool ShouldClear(double now, bool grounded, double grace) { if (!grounded) { groundSince = -1.0; return false; } if (groundSince < 0.0) { groundSince = now; } if (now - groundSince >= grace) { return now - lastContact >= grace; } return false; } public void Reset() { lastContact = 0.0; groundSince = -1.0; } } internal sealed class StateStore { private const int Magic = 1212695379; private const int Version = 3; private const int MaxRecords = 4096; private readonly string path; private readonly long world; private readonly object gate = new object(); private Dictionary<string, ShipRecord> pending; private Task worker = Task.CompletedTask; private Exception error; public readonly Dictionary<string, ShipRecord> Records = new Dictionary<string, ShipRecord>(); public bool Imported { get; private set; } public StateStore(string path, long world, params string[] legacyPaths) { this.path = path; this.world = world; string text = (File.Exists(path) ? path : legacyPaths?.FirstOrDefault((string p) => p != null && File.Exists(p))); if (text == null || !File.Exists(text)) { return; } try { Load(text); Imported = text != path; } catch (Exception ex) { Records.Clear(); error = ex; if (File.Exists(text + ".bak")) { try { Load(text + ".bak"); Imported = text != path; return; } catch { Records.Clear(); return; } } } } private void Load(string file) { if (new FileInfo(file).Length > 2097152) { throw new InvalidDataException("Metadata exceeds size limit"); } Dictionary<string, ShipRecord> dictionary = new Dictionary<string, ShipRecord>(); byte[] array = File.ReadAllBytes(file); if (array.Length < 52) { throw new InvalidDataException("Truncated metadata"); } using (SHA256 sHA = SHA256.Create()) { if (!sHA.ComputeHash(array, 0, array.Length - 32).SequenceEqual(array.Skip(array.Length - 32))) { throw new InvalidDataException("Metadata checksum mismatch"); } } using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(array, 0, array.Length - 32))) { int num = binaryReader.ReadInt32(); int num2 = binaryReader.ReadInt32(); if (num != 1212695379 || (num2 != 2 && num2 != 3) || binaryReader.ReadInt64() != world) { throw new InvalidDataException("Metadata header/world mismatch"); } int num3 = binaryReader.ReadInt32(); if (num3 < 0 || num3 > 4096) { throw new InvalidDataException("Invalid record count"); } for (int i = 0; i < num3; i++) { string text = binaryReader.ReadString(); if (text.Length == 0 || text.Length > 256) { throw new InvalidDataException("Invalid identity"); } ShipRecord shipRecord = new ShipRecord(binaryReader.ReadInt64(), binaryReader.ReadInt64(), binaryReader.ReadUInt32(), binaryReader.ReadInt32(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadInt64()); if (num2 == 3) { shipRecord = new ShipRecord(shipRecord.Character, shipRecord.ShipUser, shipRecord.ShipId, shipRecord.Prefab, shipRecord.X, shipRecord.Y, shipRecord.Z, shipRecord.Time, binaryReader.ReadBoolean(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); } if (shipRecord.Valid(DateTimeOffset.UtcNow.ToUnixTimeSeconds())) { dictionary.Add(text, shipRecord); } } if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length) { throw new InvalidDataException("Trailing data"); } } Records.Clear(); foreach (KeyValuePair<string, ShipRecord> item in dictionary) { Records.Add(item.Key, item.Value); } } public void Queue() { List<string> list = new List<string>(); foreach (KeyValuePair<string, ShipRecord> record in Records) { if (!record.Value.Valid(DateTimeOffset.UtcNow.ToUnixTimeSeconds())) { list.Add(record.Key); } } foreach (string item in list) { Records.Remove(item); } if (Records.Count > 4096) { throw new InvalidDataException("Metadata capacity exceeded"); } lock (gate) { pending = new Dictionary<string, ShipRecord>(Records); if (worker.IsCompleted) { worker = Task.Run((Action)WriteLoop); } } } private void WriteLoop() { while (true) { Dictionary<string, ShipRecord> dictionary; lock (gate) { dictionary = pending; pending = null; if (dictionary == null) { worker = Task.CompletedTask; break; } } try { Directory.CreateDirectory(Path.GetDirectoryName(path)); using (FileStream fileStream = new FileStream(path + ".tmp", FileMode.Create, FileAccess.ReadWrite, FileShare.None)) { using (BinaryWriter binaryWriter = new BinaryWriter(fileStream, Encoding.UTF8, leaveOpen: true)) { binaryWriter.Write(1212695379); binaryWriter.Write(3); binaryWriter.Write(world); binaryWriter.Write(dictionary.Count); foreach (KeyValuePair<string, ShipRecord> item in dictionary) { binaryWriter.Write(item.Key); ShipRecord value = item.Value; binaryWriter.Write(value.Character); binaryWriter.Write(value.ShipUser); binaryWriter.Write(value.ShipId); binaryWriter.Write(value.Prefab); binaryWriter.Write(value.X); binaryWriter.Write(value.Y); binaryWriter.Write(value.Z); binaryWriter.Write(value.Time); binaryWriter.Write(value.HasFallback); binaryWriter.Write(value.WorldX); binaryWriter.Write(value.WorldY); binaryWriter.Write(value.WorldZ); } } fileStream.Position = 0L; byte[] array; using (SHA256 sHA = SHA256.Create()) { array = sHA.ComputeHash(fileStream); } fileStream.Write(array, 0, array.Length); fileStream.Flush(flushToDisk: true); } if (File.Exists(path)) { File.Replace(path + ".tmp", path, path + ".bak"); } else { File.Move(path + ".tmp", path); } } catch (Exception ex) { lock (gate) { error = ex; } } } } public Exception TakeError() { lock (gate) { Exception result = error; error = null; return result; } } public void Drain() { Task task; lock (gate) { task = worker; } task.GetAwaiter().GetResult(); } } } namespace FiresGhettoNetworkMod { [BepInPlugin("Yokko.Hearthlink.Network", "Hearthlink Network", "0.1.9")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class FiresGhettoNetworkMod : BaseUnityPlugin { [HarmonyPatch] public static class WackyDatabaseCompatibilityPatch { public static void Init(Harmony harmony) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown Type type = Type.GetType("wackydatabase.Util.Functions, WackysDatabase"); if (type == null) { LoggerOptions.LogInfo("WackyDatabase not detected — skipping compatibility patch."); return; } MethodInfo method = type.GetMethod("SnapshotItem", BindingFlags.Static | BindingFlags.Public); if (method == null) { LoggerOptions.LogWarning("WackyDatabase detected but SnapshotItem method not found — patch skipped."); return; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(WackyDatabaseCompatibilityPatch), "SnapshotItem_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LoggerOptions.LogInfo("WackyDatabase compatibility patch applied — will skip snapshots for invalid/broken clones."); } [HarmonyPrefix] public static bool SnapshotItem_Prefix(ref ItemDrop item) { if ((Object)(object)item == (Object)null) { LoggerOptions.LogWarning("WDB: Skipping snapshot for null ItemDrop (likely broken clone)."); return false; } if ((Object)(object)((Component)item).gameObject == (Object)null) { LoggerOptions.LogWarning("WDB: Skipping snapshot for " + ((Object)item).name + " — gameObject is null (missing prefab from removed mod)."); return false; } bool num = ((Component)item).GetComponentsInChildren<Renderer>(true).Length != 0; bool flag = ((Component)item).GetComponentsInChildren<MeshFilter>(true).Length != 0; if (!num && !flag) { LoggerOptions.LogWarning("WDB: Skipping snapshot for " + ((Object)item).name + " — no renderers or meshes (broken model)."); return false; } return true; } } public const string PluginGUID = "Yokko.Hearthlink.Network"; public const string PluginName = "Hearthlink Network"; public const string PluginVersion = "0.1.9"; public const string ValheimPerformanceOptimizationsGuid = "dev.ontrigger.vpo"; public static ManualLogSource Log; public static ConfigEntry<LogLevel> ConfigLogLevel; public static ConfigEntry<bool> ConfigEnableCompression; public static ConfigEntry<UpdateRateOptions> ConfigUpdateRate; public static ConfigEntry<int> ConfigSendRateMin; public static ConfigEntry<int> ConfigSendRateMax; public const int DefaultSendRateMinKb = 512; public const int DefaultSendRateMaxKb = 2048; public const int SendRateKbLow = 4; public const int SendRateKbHigh = 131072; public static ConfigEntry<bool> ConfigAdaptiveUpload; public static ConfigEntry<QueueSizeOptions> ConfigQueueSize; public static ConfigEntry<ForceCrossplayOptions> ConfigForceCrossplay; public static ConfigEntry<int> ConfigPlayerLimit; public static ConfigEntry<int> ConfigAdvertisedPlayerLimit; public static ConfigEntry<bool> ConfigEnableShipFixes; public static ConfigEntry<bool> ConfigEnableServerSideShipSimulation; public static ConfigEntry<int> ConfigExtendedZoneRadius; public static ConfigEntry<bool> ConfigEnableZDOThrottling; public static ConfigEntry<float> ConfigZDOThrottleDistance; public static ConfigEntry<bool> ConfigEnableAILOD; public static ConfigEntry<float> ConfigAILODNearDistance; public static ConfigEntry<float> ConfigAILODFarDistance; public static ConfigEntry<float> ConfigAILODThrottleFactor; public static ConfigEntry<bool> ConfigEnableAdaptiveThrottling; public static ConfigEntry<int> ConfigSendCongestionThresholdPct; public static ConfigEntry<bool> ConfigEnableSendHeartbeatLog; public static ConfigEntry<bool> ConfigEnableFallThroughDiagnostics; public static ConfigEntry<bool> ConfigEnableCapeCrashDiagnostics; public static ConfigEntry<int> ConfigZoneLoadBatchSize; public static ConfigEntry<int> ConfigZPackageReceiveBufferSize; public static ConfigEntry<bool> ConfigEnableTimeSliceInstantiation; public static ConfigEntry<int> ConfigInstantiationBudgetMs; public static ConfigEntry<int> ConfigMaxInstancesPerFrame; public static ConfigEntry<int> ConfigServerInstantiationBudgetMs; public static ConfigEntry<int> ConfigServerPassHz; public static ConfigEntry<bool> ConfigSafetyFallbackEnabled; public static ConfigEntry<int> ConfigSafetyFallbackThreshold; public static ConfigEntry<bool> ConfigEnablePredictiveZoneStreaming; public static ConfigEntry<float> ConfigPredictionLookaheadSec; public static ConfigEntry<float> ConfigPredictionMinVelocity; public static ConfigEntry<int> ConfigPredictionMaxLookaheadZones; public static ConfigEntry<bool> ConfigEnableInvulnerableSupportSkip; public static ConfigEntry<bool> ConfigEnableInstanceOrphanPrune; public static ConfigEntry<bool> ConfigFixTeleportGhosts; public static ConfigEntry<bool> ConfigFixSlowSleep; public static ConfigEntry<bool> ConfigKeepaliveFirst; public static ConfigEntry<bool> ConfigKeepWorldClockAtRealTime; public static ConfigEntry<bool> ConfigSmoothServerClockCorrections; public static ConfigEntry<bool> ConfigFixGroundSnapThroughFloors; public static ConfigEntry<bool> ConfigEnableRpcRouter; public static ConfigEntry<bool> ConfigEnableRpcAoI; public static ConfigEntry<float> ConfigRpcAoIRadius; public static ConfigEntry<bool> ConfigEnableZDODelta; public static ConfigEntry<bool> ConfigAllocationFreeZdoWrites; public static ConfigEntry<bool> ConfigEnableWNTServerOptimization; public static ConfigEntry<bool> ConfigEnableServerAuthority; public static ConfigEntry<bool> ConfigEnableServerOwnership; public static ConfigEntry<bool> ConfigEnableServerOwnershipSelective; public static ConfigEntry<bool> ConfigShowAILODInServerStatus; public static ConfigEntry<bool> ConfigEnableBootPatchVerification; public static ConfigEntry<bool> ConfigEnableLargeZdoDiagnostics; public static ConfigEntry<int> ConfigClientMaxDestroysPerFrame; public static ConfigEntry<string> ConfigDediFellOutRescueLayers; public static ConfigEntry<float> ConfigDiagnosticIntervalSec; public static ConfigEntry<bool> ConfigEnableBulkTransferBoost; public static ConfigEntry<int> ConfigBulkTransferBudgetPercent; public static ConfigEntry<bool> ConfigHyperBoost; private const float JoinAddressWaitSeconds = 60f; internal static Harmony Harmony { get; private set; } public static FiresGhettoNetworkMod Instance { get; private set; } public ConfigFile Config { get; private set; } private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown Config = new ConfigFile(Path.Combine(Paths.ConfigPath, "Hearthlink.Advanced.cfg"), true); Config.SaveOnConfigSet = false; Instance = this; Log = ((BaseUnityPlugin)this).Logger; Harmony = new Harmony("Yokko.Hearthlink.Network"); BindConfigs(); try { Config.Save(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to save config file immediately: " + ex.Message)); } LoggerOptions.Init(((BaseUnityPlugin)this).Logger); TierPresets.ValidateVanillaFloors(); ServerClientUtils.Detect(((BaseUnityPlugin)this).Logger); bool isDedicatedServerDetected = ServerClientUtils.IsDedicatedServerDetected; if (isDedicatedServerDetected) { LoggerOptions.LogMessage("Hearthlink Network v0.1.9 — Running on DEDICATED SERVER, enabling server-side features."); } else { LoggerOptions.LogMessage("Hearthlink Network v0.1.9 — Running on CLIENT or SINGLE-PLAYER/LISTEN SERVER, only client-safe features will be applied."); } ValheimCommunityPatchCompat.Detect(); InvokeStaticInitByTypeName("FiresGhettoNetworkMod.CompressionGroup", "InitConfig", new object[1] { Config }); InvokeStaticInitByTypeName("FiresGhettoNetworkMod.NetworkingRatesGroup", "Init", new object[1] { Config }); InvokeStaticInitByTypeName("FiresGhettoNetworkMod.DedicatedServerGroup", "Init", new object[1] { Config }); SendQueueHeadroomMonitor.InitConfig(Config); AdaptiveSendRate.InitConfig(Config); LinkController.InitConfig(Config); SendScheduler.InitConfig(Config); StationRouter.InitConfig(Config); CreatureOwnership.InitConfig(Config); HelmOwnership.InitConfig(Config); TargetSync.InitConfig(Config); SectorChangeTracker.InitConfig(Config); FireplaceFuelTicks.InitConfig(Config); TransformWriteRate.InitConfig(Config); SyncListRefPosPatches.InitConfig(Config); JoinGrace.InitConfig(Config); PlayFabZlibWorker.InitConfig(Config); LagFairDodge.InitConfig(Config); LogoutHold.InitConfig(Config); RemoteMotion.InitConfig(Config); RemoteArrows.InitConfig(Config); TryPatchAll(typeof(CompressionGroup)); TryPatchAll(typeof(NetworkingRatesGroup)); TryPatchAll(typeof(DedicatedServerGroup)); TryPatchAll(typeof(SyncListRefPosPatches)); TryPatchAll(typeof(UploadBreakdown)); TryPatchAll(typeof(DownloadBreakdown)); TryPatchAll(typeof(SendZDOsHeartbeatDiagnostic)); if (ConfigEnableFallThroughDiagnostics.Value) { TryPatchAll(typeof(FallThroughProbe)); TryPatchAll(typeof(PieceTypeAudit)); } TryPatchAll(typeof(ServerDisconnectDiagnostics)); TryPatchAll(typeof(CloseWithoutSleep)); PlayFabZlibWorker.Apply(); TryPatchAll(typeof(BulkTransferGatePatches)); TryPatchAll(typeof(BigZdoDiagnostic)); TryPatchAll(typeof(FireplaceFuelTicks)); TryPatchAll(typeof(TransformWriteRate)); ZdoWireWriter.Initialize(); TryPatchAll(typeof(ZdoWireWriter)); if (ConfigEnableZDODelta.Value) { ZDODeltaPatches.InitializeSendSafety(); TryPatchAll(typeof(ZDODeltaPatches)); LoggerOptions.LogInfo("ZDO delta compression enabled."); } TryPatchAll(typeof(DisarmOverloadTest)); TryPatchAll(typeof(CompressionRoundTripTest)); TryPatchAll(typeof(SocketStressTests)); TryPatchAll(typeof(SendQueueHeadroomMonitor)); TryPatchAll(typeof(LinkController)); TryPatchAll(typeof(NetSim)); TryPatchAll(typeof(LogoutHold)); TryPatchAll(typeof(RemoteArrows)); TryPatchAll(typeof(SendScheduler)); TryPatchAll(typeof(ConnectionEcho)); TryPatchAll(typeof(CreatureOwnership)); TryPatchAll(typeof(HelmOwnership)); TryPatchAll(typeof(TargetSync)); TryPatchAll(typeof(KeepaliveFirst)); TryPatchAll(typeof(JoinGrace)); try { TryPatchAll(typeof(RoundTripTrace)); } catch (Exception ex2) { LoggerOptions.LogWarning("[RoundTrip] could not be attached; round trips are still timed, without the per-stage breakdown. " + ex2.Message); } TryPatchAll(typeof(ZdoFloodTest)); WackyDatabaseCompatibilityPatch.Init(Harmony); TryPatchAll(typeof(PlayerPositionSyncPatches)); TryPatchAll(typeof(WearNTearClientSupportPatches)); TryPatchAll(typeof(ClientCleanupThrottle)); TryPatchAll(typeof(GroundSnapPatches)); TryPatchAll(typeof(OwnershipHandoffPatches)); try { TeleportGhostFix.Init(); TryPatchAll(typeof(TeleportGhostFix)); } catch (Exception ex3) { LoggerOptions.LogWarning("[TeleportGhostFix] could not be attached; vanilla behaviour is unchanged. " + ex3.Message); } try { TryPatchAll(typeof(TerrainCompInitRace)); } catch (Exception ex4) { LoggerOptions.LogWarning("[TerrainComp] could not be attached; vanilla behaviour is unchanged. " + ex4.Message); } TryPatchAll(typeof(SleepTimeSkipFix)); if (!isDedicatedServerDetected) { TryPatchAll(typeof(WorldClock)); } if (!isDedicatedServerDetected && ConfigEnableCapeCrashDiagnostics.Value) { TryPatchAll(typeof(CapeCrashDiagnostics)); TryPatchAll(typeof(MagicaColliderRegistrationDiagnostics)); TryPatchAll(typeof(MagicaClothLifecycleDiagnostics)); LoggerOptions.LogWarning("[CapeDiag] Cape crash diagnostics are ON; turn 'Enable Cape Crash Diagnostics' off once the crash is found."); } TryPatchAll(typeof(AutoTuneProbeHooks)); TryPatchAll(typeof(ZoneLoadPatches)); ServerAutoTune.InitServerSide(); if (isDedicatedServerDetected) { ApplyServerTrafficPatches(); if (!ConfigEnableServerAuthority.Value) { LoggerOptions.LogInfo("Server-side simulation disabled via ConfigEnableServerAuthority = false."); } else if (ValheimCommunityPatchCompat.SchedulesSceneObjects) { LoggerOptions.LogWarning("Server-Side Simulation is OFF for this session: ValheimCommunityPatch replaces the server's object create/destroy pass with its own, built around world origin, and destroys every object FGN creates for players. Remove ValheimCommunityPatch from the server to use Server-Side Simulation, or turn 'Enable Server-Side Simulation' off to silence this warning."); } else if (Chainloader.PluginInfos.ContainsKey("dev.ontrigger.vpo")) { LoggerOptions.LogWarning("Server-Side Simulation (and ZDO ownership transfer) is OFF for this session: ValheimPerformanceOptimizations replaces the server's object create/destroy pass and its ownership pass with its own, built around the server's reference position, so it would undo what FGN does for players. Remove ValheimPerformanceOptimizations from the server to use Server-Side Simulation, or turn 'Enable Server-Side Simulation' off to silence this warning."); } else { Type[] features = FgnHooks.CaptureFeatures(); try { using (PatchTransaction patchTransaction = new PatchTransaction(Harmony)) { ApplyServerSideSimulationPatches(); patchTransaction.Commit(); } ServerAuthorityPatches.SimulationActive = true; } catch (Exception ex5) { FgnHooks.RestoreFeatures(features); ServerAuthorityPatches.SimulationActive = false; CreatureOwnership.ServerOwnsCreatures = (HelmOwnership.ServerOwnsShips = false); LoggerOptions.LogError("Server simulation startup failed; native simulation retained: " + ex5.Message); } } } else { LoggerOptions.LogInfo("Server-side features skipped — not running on a dedicated server."); } try { using PatchTransaction patchTransaction2 = new PatchTransaction(Harmony); Type[] array = FgnHooks.ReadAttachedFeatures(); for (int i = 0; i < array.Length; i++) { RequirePatch(array[i]); } patchTransaction2.Commit(); } catch (Exception ex6) { Harmony.UnpatchSelf(); FgnHooks.RestoreFeatures(Array.Empty<Type>()); FgnHooks.ReadAttachedFeatures(); ServerAuthorityPatches.SimulationActive = false; CreatureOwnership.ServerOwnsCreatures = (HelmOwnership.ServerOwnsShips = false); LoggerOptions.LogError("Shared network hooks failed; network module disabled: " + ex6.Message); } if (isDedicatedServerDetected) { EmitServerFeatureSummary(ServerAuthorityPatches.SimulationActive); } } private static void ApplyServerTrafficPatches() { TryPatchAll(typeof(ServerFrameProfile)); TryPatchAll(typeof(SectorChangeTracker)); TryPatchAll(typeof(ZDOThrottlingPatches)); TryPatchAll(typeof(AILODPatches)); TryPatchAll(typeof(RpcRouterPatches)); TryPatchAll(typeof(StationRouter)); DamageTextHandler.Register(); if (ConfigEnableRpcRouter.Value || StationRouter.ConfigEnabled.Value) { VAGhettoLoadSummary.EmitRpcRouter(RoutedRpcManager.HandlerCount, RoutedRpcManager.PositionRadius(), RoutedRpcManager.FilteringEnabled()); if (VAGhettoLoadSummary.VerboseEnabled) { LoggerOptions.LogInfo("RPC Router enabled — handlers: " + string.Join(", ", RoutedRpcManager.HandlerMethodNames) + "."); } } if (ConfigEnableWNTServerOptimization.Value) { TryPatchAll(typeof(WearNTearServerPatches)); LoggerOptions.LogInfo("WearNTear server optimization enabled."); } } private static void ApplyServerSideSimulationPatches() { if (ConfigEnableShipFixes.Value) { RequirePatch(typeof(ShipFixesGroup)); } RequirePatch(typeof(ServerShipSimulationPatches)); RequirePatch(typeof(ServerAuthorityPatches)); RequirePatch(typeof(ServerStabilityPatches)); RequirePatch(typeof(MonsterAIPatches)); if (ConfigEnableServerOwnershipSelective.Value) { if (ConfigEnableServerOwnership.Value) { LoggerOptions.LogWarning("Both 'Server ZDO Ownership Transfer' flags are ENABLED — selective (V3) takes precedence; broad (V2) is being ignored. Disable one to silence this warning."); } RequirePatch(typeof(ServerOwnershipPatchesV3)); CreatureOwnership.ServerOwnsCreatures = true; HelmOwnership.ServerOwnsShips = ConfigEnableServerSideShipSimulation.Value; LoggerOptions.LogMessage("Server ZDO ownership (V3 SELECTIVE) ENABLED — Character/Ship only; drops/voxel/interactables/carts stay peer-owned."); } else if (ConfigEnableServerOwnership.Value) { RequirePatch(typeof(ServerOwnershipPatches)); CreatureOwnership.ServerOwnsCreatures = true; HelmOwnership.ServerOwnsShips = true; LoggerOptions.LogMessage("Server ZDO ownership (V2 BROAD SSS-exact) ENABLED — every persistent ZDO in any peer's active area will be claimed by the server."); if (!ConfigEnableServerSideShipSimulation.Value) { LoggerOptions.LogWarning("V2 BROAD claims SHIPS as well, regardless of 'Server-Side Ship Simulation' being off — it is a deliberate verbatim port with no per-prefab exclusions. A server-owned hull runs its own physics, and ImpactEffect only fires for the owner, so boats can take phantom damage on calm water. Use the Selective (V3) toggle instead if your players sail; it honours that setting."); } } else { LoggerOptions.LogInfo("Server ZDO ownership transfer disabled (both V2 and V3 flags = false). Vanilla peer ownership in effect."); } if (ConfigEnableBootPatchVerification.Value) { DumpPatchInfo(typeof(BaseAI), "UpdateAI"); DumpPatchInfo(typeof(BaseAI), "Awake"); DumpPatchInfo(typeof(MonsterAI), "UpdateAI"); DumpPatchInfo(typeof(Character), "CustomFixedUpdate"); DumpPatchInfo(typeof(MonoUpdaters), "FixedUpdate"); DumpPatchInfo(typeof(ZNetScene), "CreateDestroyObjects"); DumpPatchInfo(typeof(ZNetScene), "CreateObject"); DumpPatchInfo(typeof(ZoneSystem), "IsActiveAreaLoaded"); DumpPatchInfo(typeof(SpawnSystem), "UpdateSpawning"); DumpPatchInfo(typeof(ShieldDomeImageEffect), "Awake"); DumpPatchInfo(typeof(Heightmap), "RebuildRenderMesh"); } if (VAGhettoLoadSummary.VerboseEnabled) { LoggerOptions.LogInfo("All server-side simulation patches enabled."); } } private static void EmitServerFeatureSummary(bool simulationActive) { string ownership = ((simulationActive && FgnHooks.Attached(typeof(ServerOwnershipPatchesV3))) ? "V3 selective" : ((simulationActive && FgnHooks.Attached(typeof(ServerOwnershipPatches))) ? "V2 broad" : "vanilla peer")); VAGhettoLoadSummary.EmitServerAuthority(simulationActive, ownership, FgnHooks.Attached(typeof(ZDODeltaPatches)) && (ConfigEnableZDODelta?.Value ?? false), FgnHooks.Attached(typeof(ZDOThrottlingPatches)) && (ConfigEnableZDOThrottling?.Value ?? false), FgnHooks.Attached(typeof(AILODPatches)) && (ConfigEnableAILOD?.Value ?? false), FgnHooks.Attached(typeof(WearNTearServerPatches)) && (ConfigEnableWNTServerOptimization?.Value ?? false)); } private IEnumerator EmitCompactBannerWhenZNetReady() { while ((Object)(object)ZNetScene.instance == (Object)null || ZNetScene.instance.m_prefabs == null || ZNetScene.instance.m_prefabs.Count == 0) { yield return null; } yield return (object)new WaitForEndOfFrame(); string address = null; bool dedicated = ServerClientUtils.IsDedicatedServerDetected; if (dedicated) { float giveUpAt = Time.realtimeSinceStartup + 60f; while (!TryGetJoinAddress(out address) && Time.realtimeSinceStartup < giveUpAt) { yield return (object)new WaitForSecondsRealtime(0.5f); } } try { VAGhettoBanner.Print(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Hearthlink Network v0.1.9 loaded. (banner failed: " + ex.Message + ")")); } if (dedicated) { ((BaseUnityPlugin)this).Logger.LogInfo((object)((address != null) ? ("Join address: " + address) : $"Join address: this server's public IP, port {ServerPort()} (Steam did not report the public IP within {60f:0} s).")); } } private static bool TryGetJoinAddress(out string address) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) address = null; int num = ServerPort(); if (num <= 0) { return false; } try { if (!SteamGameServer.BLoggedOn()) { return false; } SteamIPAddress_t publicIP = SteamGameServer.GetPublicIP(); if (!((SteamIPAddress_t)(ref publicIP)).IsSet()) { return false; } IPAddress iPAddress = ((SteamIPAddress_t)(ref publicIP)).ToIPAddress(); address = ((iPAddress.AddressFamily == AddressFamily.InterNetworkV6) ? $"[{iPAddress}]:{num}" : $"{iPAddress}:{num}"); return true; } catch (Exception) { return false; } } private static int ServerPort() { string[] commandLineArgs = Environment.GetCommandLineArgs(); for (int i = 0; i < commandLineArgs.Length - 1; i++) { if (string.Equals(commandLineArgs[i], "-port", StringComparison.OrdinalIgnoreCase) && int.TryParse(commandLineArgs[i + 1], out var result) && result > 0) { return result; } } return 2456; } private static bool TryPatchAll(Type type) { if (type == null) { ManualLogSource log = Log; if (log != null) { log.LogError((object)"Tried to patch a null type!"); } return false; } try { PatchTransaction.Install(Harmony, type); FgnHooks.NoteAttached(type); return true; } catch (Exception ex) { Exception ex2 = ex.InnerException ?? ex; ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)("[Hearthlink Network] couldn't attach " + type.Name + " (" + ex2.GetType().Name + ": " + ex2.Message + "); that feature is off, the rest of FGN loads normally.")); } return false; } } private static void RequirePatch(Type type) { if (!TryPatchAll(type)) { throw new InvalidOperationException("Required simulation/network patch failed: " + type.FullName); } } private static void DumpPatchInfo(Type type, string methodName) { MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { LoggerOptions.LogMessage("[PatchVerify] " + type.FullName + "." + methodName + " → method NOT FOUND by AccessTools."); return; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo == null) { LoggerOptions.LogMessage("[PatchVerify] " + type.FullName + "." + methodName + " → NO patches attached (info=null)."); return; } int num = (patchInfo.Prefixes?.Count ?? 0) + (patchInfo.Postfixes?.Count ?? 0) + (patchInfo.Transpilers?.Count ?? 0) + (patchInfo.Finalizers?.Count ?? 0); LoggerOptions.LogMessage($"[PatchVerify] {type.FullName}.{methodName} → total={num} (prefixes={patchInfo.Prefixes?.Count ?? 0}, postfixes={patchInfo.Postfixes?.Count ?? 0}, transpilers={patchInfo.Transpilers?.Count ?? 0}, finalizers={patchInfo.Finalizers?.Count ?? 0})."); DumpList("Prefix", patchInfo.Prefixes); DumpList("Postfix", patchInfo.Postfixes); DumpList("Transpiler", patchInfo.Transpilers); DumpList("Finalizer", patchInfo.Finalizers); static void DumpList(string kind, IEnumerable<Patch> ps) { if (ps == null) { return; } foreach (Patch p in ps) { LoggerOptions.LogMessage($"[PatchVerify] [{kind}] owner='{p.owner}' method={p.PatchMethod.DeclaringType?.FullName}.{p.PatchMethod.Name} prio={p.priority}"); } } } private void InvokeStaticInitByTypeName(string typeName, string methodName, object[] args) { try { Type type = Type.GetType(typeName); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Type " + typeName + " not found; skipping " + methodName + ".")); return; } MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Method " + methodName + " not found on " + typeName + ".")); } else { method.Invoke(null, args); } } catch (TypeLoadException arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"TypeLoadException while invoking {typeName}.{methodName}: {arg}"); } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Exception while invoking {typeName}.{methodName}: {arg2}"); } } private int MigrateLegacyKb(string section, string key, int defaultKb) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown int num = -1; try { string configFilePath = Config.ConfigFilePath; if (!string.IsNullOrEmpty(configFilePath) && File.Exists(configFilePath)) { string text = null; string[] array = File.ReadAllLines(configFilePath); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2).Trim(); } else { if (text != section || text2.Length == 0 || text2.StartsWith("#")) { continue; } int num2 = text2.IndexOf('='); if (num2 > 0 && !(text2.Substring(0, num2).Trim() != key)) { Match match = Regex.Match(text2.Substring(num2 + 1).Trim(), "^_(\\d+)KB$"); if (match.Success) { num = int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); } break; } } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Config] Could not read " + key + " for migration: " + ex.Message)); } if (num <= 0) { return defaultKb; } try { if (typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(Config) is Dictionary<ConfigDefinition, string> dictionary) { ConfigDefinition key2 = new ConfigDefinition(section, key); if (dictionary.ContainsKey(key2)) { dictionary[key2] = num.ToString(CultureInfo.InvariantCulture); } } } catch { } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Config] {key}: migrated from the old option _{num}KB to {num} KB/s."); return Mathf.Clamp(num, 4, 131072); } private void BindConfigs() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Expected O, but got Unknown //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Expected O, but got Unknown //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Expected O, but got Unknown //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Expected O, but got Unknown //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Expected O, but got Unknown //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Expected O, but got Unknown //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_03f9: Expected O, but got Unknown //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Expected O, but got Unknown //IL_0457: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Expected O, but got Unknown //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_048c: Expected O, but got Unknown //IL_04df: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Expected O, but got Unknown //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_0514: Expected O, but got Unknown //IL_053e: Unknown result type (might be due to invalid IL or missing references) //IL_0548: Expected O, but got Unknown //IL_0569: Unknown result type (might be due to invalid IL or missing references) //IL_0573: Expected O, but got Unknown //IL_059b: Unknown result type (might be due to invalid IL or missing references) //IL_05a5: Expected O, but got Unknown //IL_05ce: Unknown result type (might be due to invalid IL or missing references) //IL_05d8: Expected O, but got Unknown //IL_05f9: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Expected O, but got Unknown //IL_0624: Unknown result type (might be due to invalid IL or missing references) //IL_062e: Expected O, but got Unknown //IL_0655: Unknown result type (might be due to invalid IL or missing references) //IL_065f: Expected O, but got Unknown //IL_06b2: Unknown result type (might be due to invalid IL or missing references) //IL_06bc: Expected O, but got Unknown //IL_06ef: Unknown result type (might be due to invalid IL or missing references) //IL_06f9: Expected O, but got Unknown //IL_0722: Unknown result type (might be due to invalid IL or missing references) //IL_072c: Expected O, but got Unknown //IL_077f: Unknown result type (might be due to invalid IL or missing references) //IL_0789: Expected O, but got Unknown //IL_07dc: Unknown result type (might be due to invalid IL or missing references) //IL_07e6: Expected O, but got Unknown //IL_0819: Unknown result type (might be due to invalid IL or missing references) //IL_0823: Expected O, but got Unknown //IL_0856: Unknown result type (might be due to invalid IL or missing references) //IL_0860: Expected O, but got Unknown //IL_08aa: Unknown result type (might be due to invalid IL or missing references) //IL_08b4: Expected O, but got Unknown //IL_08f5: Unknown result type (might be due to invalid IL or missing references) //IL_08ff: Expected O, but got Unknown //IL_0920: Unknown result type (might be due to invalid IL or missing references) //IL_092a: Expected O, but got Unknown ConfigLogLevel = Config.Bind<LogLevel>("01 - General", "Log Level", LogLevel.Message, "Hearthlink prints the normal startup summary. Info adds integrated network details; Debug adds timed reports. Warnings and errors are always visible."); ConfigZoneLoadBatchSize = Config.Bind<int>("02 - Client Performance", "Zone Load Batch Size", 1, new ConfigDescription("Multiplies how many objects the game creates per frame while an area loads in (1 = vanilla).\nHigher loads areas faster but with bigger hitches. Used when 'Enable Time-Slice Instantiation' is off\nor behind the loading screen; Client Auto-Tune replaces it with its own value. Client only.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>())); PlayerPositionSyncPatches.Init(Config); ConfigEnableCompression = Config.Bind<bool>("04 - Networking", "Enable Compression", true, "Compresses network traffic (recommended). It is agreed per connection, so it is only used with players and\nservers that also run FGN with compression on. Install on both server and clients."); ConfigUpdateRate = Config.Bind<UpdateRateOptions>("04 - Networking", "ZDO Send Rate", UpdateRateOptions._100, "How often world updates are sent; 100% (20/s) is vanilla. Lower helps a slow upload, higher looks smoother if\nbandwidth allows. While Auto-Tune is on it writes this value and your edits are replaced."); ConfigSendRateMin = Config.Bind<int>("05 - Networking - Steamworks", "Send Rate Min", MigrateLegacyKb("05 - Networking - Steamworks", "Send Rate Min", 512), new ConfigDescription("Lowest Steam send rate in KB/s, held even when the connection struggles. Keep it well below your real upload\nso there is room to back off. While Auto-Tune is on it writes this value and your edits are replaced.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(4, 131072), Array.Empty<object>())); ConfigSendRateMax = Config.Bind<int>("05 - Networking - Steamworks", "Send Rate Max", MigrateLegacyKb("05 - Networking - Steamworks", "Send Rate Max", 2048), new ConfigDescription("Highest Steam send rate in KB/s for this PC (on a client, your upload ceiling). Adaptive Upload and Adaptive\nSend Rate move the live rate between Min and Max. While Auto-Tune is on it writes this value and your edits are replaced.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(4, 131072), Array.Empty<object>())); ConfigAdaptiveUpload = Config.Bind<bool>("04 - Networking", "Adaptive Upload", true, "Keeps your live send rate (between Send Rate Min and Max) under what your connection actually delivers, so a\nbusy line backs off instead of flooding. Off = Steam's own rate control. Steam connections only, not crossplay."); ConfigHyperBoost = Config.Bind<bool>("05 - Networking - Steamworks", "HYPERBOOST", false, "Maximum-throughput mode: overrides Auto-Tune and Send Rate Min/Max and lifts Steam's send rate and buffers to\ntheir limits, live. Set it on both server and client; the receive side needs FiresSteamworksPatcher.\nLeave off for normal play; it is meant for very fast links and benchmarking."); AutoTuneConfig.Init(Config); ConfigQueueSize = Config.Bind<QueueSizeOptions>("04 - Networking", "Queue Size", QueueSizeOptions._32KB, "Largest batch of world updates sent to one player at a time, and each player's starting send window (the fixed\nlimit when 'Adaptive Send Window' is off). Crossplay players use 'Crossplay In-Flight KB' instead.\nWhile Auto-Tune is on it writes this value and your edits are replaced."); ConfigForceCrossplay = Config.Bind<ForceCrossplayOptions>("09 - Dedicated Server", "Force Crossplay", ForceCrossplayOptions.vanilla, "Which network backend a dedicated server uses. vanilla = follow the -crossplay launch flag; steamworks = Steam only\n(no Xbox / Game Pass / PlayStation players); playfab = crossplay on, players join with the printed join code.\nRequires a restart. Leave clients on vanilla."); ConfigPlayerLimit = Config.Bind<int>("09 - Dedicated Server", "Player Limit", 10, new ConfigDescription("Maximum players on a dedicated server. Ignored when the MaxPlayerCount mod is installed. Requires a restart.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); ConfigAdvertisedPlayerLimit = Config.Bind<int>("09 - Dedicated Server", "Advertised Player Limit", 0, new ConfigDescription("Player cap shown in server listings (Steam server browser, crossplay). Does not change the real limit set by 'Player Limit'. 0 = show 'Player Limit'. Ignored when the MaxPlayerCount mod is installed. Requires a restart.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 9999), Array.Empty<object>())); ConfigZPackageReceiveBufferSize = Config.Bind<int>("02 - Client Performance", "ZPackage Receive Buffer Bytes", 262144, new ConfigDescription("Steam receive buffer size in bytes, used when Auto-Tune is off. A bigger buffer rides out short stalls without\nlosing data, at the cost of memory. Values below 2 MB (including the default) are raised to 2 MB.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(65536, 4194304), Array.Empty<object>())); ConfigEnableTimeSliceInstantiation = Config.Bind<bool>("02 - Client Performance", "Enable Time-Slice Instantiation", false, "Creates incoming objects within a time budget per frame instead of vanilla's fixed count, so walking into a busy\narea spreads the work over several frames instead of one big hitch. Off = 'Zone Load Batch Size' is used.\nClient only; the loading screen still uses vanilla."); ConfigInstantiationBudgetMs = Config.Bind<int>("02 - Client Performance", "Instantiation Budget Ms", 3, new ConfigDescription("Milliseconds per frame spent creating objects when 'Enable Time-Slice Instantiation' is on. Lower = smoother\nframes while an area loads in; higher = it loads faster. Client Auto-Tune replaces it with its own value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16), Array.Empty<object>())); ConfigMaxInstancesPerFrame = Config.Bind<int>("02 - Client Performance", "Max Instances Per Frame", 100, new ConfigDescription("Most objects created in one frame under time-slicing, however much of the budget is left.\nClient Auto-Tune replaces it with its own value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 500), Array.Empty<object>())); ConfigSafetyFallbackEnabled = Config.Bind<bool>("02 - Client Performance", "Safety Fallback Enabled", true, "Under time-slicing, triples the per-frame budget (up to 16 ms) while more than 'Safety Fallback Threshold'\nobjects are waiting, such as after teleporting into a large base. Client Auto-Tune replaces it with its own value."); ConfigSafetyFallbackThreshold = Config.Bind<int>("02 - Client Performance", "Safety Fallback Threshold", 5000, new ConfigDescription("Number of objects waiting to be created at which 'Safety Fallback Enabled' widens the budget.\nClient Auto-Tune replaces it with its own value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 50000), Array.Empty<object>())); ConfigEnableRpcRouter = Config.Bind<bool>("10 - Server Authority", "Enable RPC Router", true, "Lets the server relay the messages players broadcast (hits, effects, damage numbers) instead of vanilla sending\neach one to everyone. Needed for 'Enable RPC Area-of-Interest'. Dedicated server only; requires a restart."); ConfigEnableShipFixes = Config.Bind<bool>("11 - Ship Fixes", "Enable Universal Ship Fixes", true, "When the dedicated server simulates ships, keeps the speed and rudder the steering player set instead of\nresetting them. Only applies with Server-Side Simulation on; requires a restart."); ConfigEnableServerSideShipSimulation = Config.Bind<bool>("11 - Ship Fixes", "Server-Side Ship Simulation", false, "Lets Selective server ownership claim ships as well as creatures, so the dedicated server runs their physics.\nOff = ships stay with the players as in vanilla. Broad ownership claims ships either way. Requires a restart."); Conf