using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("TerramizerServer")] [assembly: AssemblyFileVersion("0.4.7")] [assembly: AssemblyCompany("R4V9N1")] [assembly: AssemblyDescription("Created by R4V9N1")] [assembly: AssemblyProduct("TerramizerServer")] [assembly: AssemblyCopyright("Created by R4V9N1")] [assembly: AssemblyMetadata("Creator", "Created by R4V9N1")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.4.7.0")] namespace TerramizerServer; [BepInPlugin("r4v9n1.terramizerserver", "TerramizerServer", "0.4.7")] public sealed class TerramizerServerPlugin : BaseUnityPlugin { private struct PeerMotionSample { public Vector3 Position; public Vector3 Velocity; public float Time; public PeerMotionSample(Vector3 position, Vector3 velocity, float time) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Position = position; Velocity = velocity; Time = time; } } private struct PeerStreamingState { public bool HasZone; public Vector2i CurrentZone; public Vector2i PredictedZone; public float NextBoostTime; } private delegate void CreateObjectsDelegate(ZNetScene scene, List currentObjects, List distantObjects); private delegate void RemoveObjectsDelegate(ZNetScene scene, List currentObjects, List distantObjects); private delegate bool CreateLocalZonesDelegate(ZoneSystem zoneSystem, Vector3 refPoint); private delegate void WearNTearRemoveRpcDelegate(WearNTear instance, long sender, bool blockDrop); public const string PluginGuid = "r4v9n1.terramizerserver"; public const string PluginName = "TerramizerServer"; public const string PluginVersion = "0.4.7"; public const string CreatorCredit = "Created by R4V9N1"; internal const int VanillaMaxSendQueueBytes = 10240; internal const int VanillaMinFreeSendQueueBytes = 2048; internal const float VanillaPeerSendIntervalSeconds = 0.05f; private static ConfigEntry _enabled; private static ConfigEntry _serverOnly; private static ConfigEntry _dedicatedOnly; private static ConfigEntry _safetyDefaultsVersion; private static ConfigEntry _maxSendQueueBytes; private static ConfigEntry _minFreeSendQueueBytes; private static ConfigEntry _peerSendIntervalSeconds; private static ConfigEntry _enableServerZoneStreamingBoost; private static ConfigEntry _zoneStreamingBoostExtraRadius; private static ConfigEntry _maxZoneStreamingBoostZdosPerPeer; private static ConfigEntry _zoneStreamingBoostCooldownSeconds; private static ConfigEntry _zoneStreamingBoostMaxQueuePercent; private static ConfigEntry _zoneStreamingBoostIncludeDistantZdos; private static ConfigEntry _logZoneStreamingBoosts; private static ConfigEntry _logDenseSyncs; private static ConfigEntry _denseSyncThreshold; private static ConfigEntry _denseSyncLogIntervalSeconds; private static ConfigEntry _pauseExtraStreamingDuringDenseSync; private static ConfigEntry _denseSyncPauseSeconds; private static ConfigEntry _extendedZoneRadius; private static ConfigEntry _createDestroyIntervalSeconds; private static ConfigEntry _removeObjectsIntervalSeconds; private static ConfigEntry _enablePredictiveZoneStreaming; private static ConfigEntry _predictionLookaheadSec; private static ConfigEntry _predictionMinVelocity; private static ConfigEntry _predictionMaxLookaheadZones; private static ConfigEntry _enableZDOThrottling; private static ConfigEntry _zdoThrottleDistance; private static ConfigEntry _enablePlayerPriority; private static ConfigEntry _enablePeerZoneCreation; private static ConfigEntry _peerZoneUpdateIntervalSeconds; private static ConfigEntry _maxPeerZoneCreationsPerPass; private static ConfigEntry _enableMultiPeerOutsideActiveArea; private static ConfigEntry _enableServerOwnershipForPersistentZdos; private static ConfigEntry _allowExperimentalServerOwnershipForPersistentZdos; private static ConfigEntry _enableHeadlessVisualGuards; private static ConfigEntry _pauseExtraZoneWorkDuringSleep; private static ConfigEntry _speedUpSleepFastForward; private static ConfigEntry _sleepFastForwardSeconds; private static ConfigEntry _skipSleepWorldSave; private static ConfigEntry _preserveAutosaveTimer; private static ConfigEntry _logSkippedSleepSaves; private static ConfigEntry _logSleepOptimizations; private static ConfigEntry _repairStaleWearNTearRemoveOwnership; private static ConfigEntry _maxRemoveRepairDistance; private static ConfigEntry _logRemoveOwnershipRepairs; private static ConfigEntry _repairStaleItemDropOwnership; private static ConfigEntry _logItemDropOwnershipRepairs; private static ConfigEntry _diagnosticIntervalSec; private static readonly List _nearScratch = new List(); private static readonly List _distantScratch = new List(); private static readonly List _nearFiltered = new List(); private static readonly List _distantFiltered = new List(); private static readonly List _streamBoostNearScratch = new List(); private static readonly List _streamBoostDistantScratch = new List(); private static readonly List _streamBoostCandidates = new List(); private static readonly HashSet _streamBoostSeenZdos = new HashSet(); private static readonly HashSet _readyPeerIds = new HashSet(); private static readonly List _stalePeerIds = new List(); private static readonly HashSet _seenZdos = new HashSet(); private static readonly HashSet _syncSeenZdos = new HashSet(); private static readonly HashSet _coverageZones = new HashSet(); private static readonly HashSet _ttlRefreshZones = new HashSet(); private static readonly List _zoneCreationCenters = new List(); private static readonly Dictionary _lastDenseSyncLogTimes = new Dictionary(); private static readonly Dictionary _netPeerFields = new Dictionary(); private static readonly Dictionary _peerMotion = new Dictionary(); private static readonly Dictionary _peerStreamingStates = new Dictionary(); private static readonly Dictionary _terrainCompilerPrefabCache = new Dictionary(); private static readonly Dictionary _forceSendFields = new Dictionary(); private static readonly Dictionary _densePrefabCounts = new Dictionary(); private static readonly List> _densePrefabCountsSorted = new List>(); private static readonly int PlayerPrefabHash = StringExtensionMethods.GetStableHashCode("Player"); private static ManualLogSource _log; private static CreateObjectsDelegate _createObjects; private static RemoveObjectsDelegate _removeObjects; private static CreateLocalZonesDelegate _createLocalZones; private static FieldInfo _routedRpcTargetPeerIdField; private static FieldInfo _routedRpcSenderPeerIdField; private static FieldInfo _routedRpcTargetZdoField; private static FieldInfo _routedRpcMethodHashField; private static FieldInfo _routedRpcParametersField; private static FieldInfo _envManSkipToTimeField; private static FieldInfo _envManTimeSkipSpeedField; private static FieldInfo _gameSaveTimerField; private static FieldInfo _zoneSystemZonesField; private static FieldInfo _zoneDataTtlField; private static FieldInfo _zNetSceneInstancesField; private static readonly List _staleSceneInstanceKeys = new List(); private static long _diagnosticStaleSceneInstancesRemoved; private static WearNTearRemoveRpcDelegate _wearNTearRemoveRpc; private Harmony _harmony; private static float _nextDiagnosticLogTime; private static float _nextPeerZoneCreateTime; private static float _nextCreateDestroyTime; private static float _nextRemoveObjectsTime; private static float _nextWarningLogTime; private static float _nextRemoveRepairWarningTime; private static float _nextSleepPauseLogTime; private static float _nextSleepBedCheckTime; private static float _denseSyncPauseUntil; private static bool _sleepBedPauseActive; private static int _nextZoneCreationCenter; private static long _diagnosticCreateDestroyPasses; private static long _diagnosticZoneCreateAttempts; private static long _diagnosticZoneCreateSuccesses; private static long _diagnosticBoostQueued; private static long _diagnosticBoostBackpressureSkips; private static long _diagnosticSyncDuplicatesRemoved; private static long _diagnosticItemDropOwnershipRepairs; private static long _diagnosticItemDropOwnershipRefreshes; private static long _diagnosticDensePressurePauses; private static long _diagnosticZoneTtlRefreshes; private static int _diagnosticMaxSyncList; private static int _diagnosticPeakSendQueue; private static HashSet _syncForceSend; private static long _syncReceiverUid; private static Vector3 _syncReceiverRefPos; private static bool _syncPlayerPriority; private void Awake() { //IL_076f: Unknown result type (might be due to invalid IL or missing references) //IL_0779: Expected O, but got Unknown _log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable TerramizerServer."); _serverOnly = ((BaseUnityPlugin)this).Config.Bind("General", "ServerOnly", true, "Only run this plugin when this game instance is acting as a Valheim server."); _dedicatedOnly = ((BaseUnityPlugin)this).Config.Bind("General", "DedicatedOnly", true, "Only run on the dedicated server process. Keep true for local/single-player testing."); _safetyDefaultsVersion = BindRange("General", "SafetyDefaultsVersion", 0, "Internal migration marker used when safer compatibility defaults change between releases.", 0, 4); _maxSendQueueBytes = BindRange("Streaming", "MaxSendQueueBytes", 16384, "Socket send queue ceiling used by ZDO streaming. Vanilla is 10240. This moderate increase avoids the long interactive-update delays seen with a 24576-byte ceiling in dense areas.", 2048, 262144); _minFreeSendQueueBytes = BindRange("Streaming", "MinFreeSendQueueBytes", 4096, "Minimum free socket queue space required before sending another ZDO packet. Vanilla is 2048.", 512, 131072); _peerSendIntervalSeconds = BindRange("Streaming", "PeerSendIntervalSeconds", 0.05f, "Seconds between ZDO send passes. The default preserves Valheim's cadence while allowing a moderately larger packet budget.", 0.01f, 0.25f); _enableServerZoneStreamingBoost = ((BaseUnityPlugin)this).Config.Bind("Streaming", "EnableServerZoneStreamingBoost", true, "When a player enters or approaches a new zone, queue nearby server-known ZDOs to that peer first. This improves area load-in without changing ZDO ownership."); _zoneStreamingBoostExtraRadius = BindRange("Streaming", "ZoneStreamingBoostExtraRadius", 0, "Extra zone rings around the player's current and predicted zone to force-send on zone entry. 0 keeps the boost inside vanilla active-area coverage.", 0, 3); _maxZoneStreamingBoostZdosPerPeer = BindRange("Streaming", "MaxZoneStreamingBoostZdosPerPeer", 128, "Maximum ZDOs to force-queue for one peer on a zone-entry streaming boost. A conservative cap prevents bulk objects from delaying ownership and interaction updates.", 0, 5000); _zoneStreamingBoostCooldownSeconds = BindRange("Streaming", "ZoneStreamingBoostCooldownSeconds", 1.5f, "Minimum seconds between zone-entry streaming boosts for the same peer.", 0.1f, 10f); _zoneStreamingBoostMaxQueuePercent = BindRange("Streaming", "ZoneStreamingBoostMaxQueuePercent", 35, "Do not add a zone-entry boost while the peer socket queue is above this percentage of MaxSendQueueBytes. The zone change is retried after the queue drains.", 10, 90); _zoneStreamingBoostIncludeDistantZdos = ((BaseUnityPlugin)this).Config.Bind("Streaming", "ZoneStreamingBoostIncludeDistantZdos", true, "Also force-queue distant ZDOs if the near-object boost has remaining room."); _logZoneStreamingBoosts = ((BaseUnityPlugin)this).Config.Bind("Streaming", "LogZoneStreamingBoosts", false, "Log each zone-entry streaming boost with peer name, zone, and queued ZDO count."); _logDenseSyncs = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "LogDenseSyncs", true, "Log when a peer has a large pending ZDO sync list."); _denseSyncThreshold = BindRange("Diagnostics", "DenseSyncThreshold", 1000, "Pending ZDO count that triggers a dense-area log line.", 100, 100000); _denseSyncLogIntervalSeconds = BindRange("Diagnostics", "DenseSyncLogIntervalSeconds", 30f, "Minimum seconds between dense-area log lines per peer.", 5f, 600f); _pauseExtraStreamingDuringDenseSync = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "PauseExtraStreamingDuringDenseSync", true, "Pause TerramizerServer's extra zone generation and zone-entry boosts while any peer already has at least DenseSyncThreshold pending objects."); _denseSyncPauseSeconds = BindRange("Diagnostics", "DenseSyncPauseSeconds", 2f, "How long to keep extra streaming paused after observing a dense pending sync list. Repeated dense lists extend the pause.", 0.5f, 30f); _extendedZoneRadius = BindRange("WorldObjects", "ExtendedZoneRadius", 0, "Additional zone layers the server preloads around each peer. 0 keeps vanilla radius while still combining all ready peers.", 0, 3); _createDestroyIntervalSeconds = BindRange("WorldObjects", "CreateDestroyIntervalSeconds", 0.1f, "Minimum time between combined multi-peer object create passes. This bounds repeated scans in object-dense bases.", 0.03f, 1f); _removeObjectsIntervalSeconds = BindRange("WorldObjects", "RemoveObjectsIntervalSeconds", 0.5f, "Minimum time between server instance removal scans. Object creation still runs at CreateDestroyIntervalSeconds.", 0.05f, 2f); _enablePredictiveZoneStreaming = ((BaseUnityPlugin)this).Config.Bind("WorldObjects", "EnablePredictiveZoneStreaming", true, "Bias each peer's active-area center forward along their velocity so the server loads ahead of movement."); _predictionLookaheadSec = BindRange("WorldObjects", "PredictionLookaheadSec", 3f, "Seconds ahead to project each peer before zone checks.", 0.5f, 10f); _predictionMinVelocity = BindRange("WorldObjects", "PredictionMinVelocity", 2f, "Minimum smoothed speed before prediction engages.", 0.5f, 20f); _predictionMaxLookaheadZones = BindRange("WorldObjects", "PredictionMaxLookaheadZones", 9, "Hard cap on prediction distance, in zones.", 1, 25); _enableZDOThrottling = ((BaseUnityPlugin)this).Config.Bind("WorldObjects", "EnableZDOThrottling", false, "Reduce send priority for far-away default-type ZDOs while preserving Valheim's terrain, solid, prioritized, and player ordering. Disabled by default because distant objects normally fall outside the near sync list."); _zdoThrottleDistance = BindRange("WorldObjects", "ZDOThrottleDistance", 500f, "Distance beyond which ordinary ZDOs are deprioritized during send sorting.", 0f, 1000f); _enablePlayerPriority = ((BaseUnityPlugin)this).Config.Bind("WorldObjects", "EnablePlayerPriority", true, "Boost player ZDOs ahead of bulk world objects during server send sorting."); _enablePeerZoneCreation = ((BaseUnityPlugin)this).Config.Bind("ServerSimulation", "EnablePeerZoneCreation", false, "Experimental: instantiate full local zones around every ready peer. Disabled by default because moving around a multi-zone base can repeatedly unload/reload dungeon-bearing edge zones; Valheim still performs its normal peer ghost-zone generation."); _peerZoneUpdateIntervalSeconds = BindRange("ServerSimulation", "PeerZoneUpdateIntervalSeconds", 0.5f, "Seconds between server-side peer zone creation passes. This avoids hundreds of no-op checks per minute after zones are warm.", 0.05f, 2f); _maxPeerZoneCreationsPerPass = BindRange("ServerSimulation", "MaxPeerZoneCreationsPerPass", 1, "Maximum current/predicted peer centers allowed to generate a new zone in one server update pass.", 1, 16); _enableMultiPeerOutsideActiveArea = ((BaseUnityPlugin)this).Config.Bind("ServerSimulation", "EnableMultiPeerOutsideActiveArea", true, "Treat a point as active when it is inside any ready peer's active area. This helps server-side systems work for split-up players."); _enableServerOwnershipForPersistentZdos = ((BaseUnityPlugin)this).Config.Bind("ServerSimulation", "EnableServerOwnershipForPersistentZdos", false, "Legacy dangerous option kept only for config migration. TerramizerServer no longer mass-transfers persistent ZDO ownership; use Streaming.EnableServerZoneStreamingBoost instead."); _allowExperimentalServerOwnershipForPersistentZdos = ((BaseUnityPlugin)this).Config.Bind("ServerSimulation", "AllowExperimentalServerOwnershipForPersistentZdos", false, "Legacy dangerous option kept only for config migration. It is ignored by this build."); _enableHeadlessVisualGuards = ((BaseUnityPlugin)this).Config.Bind("ServerSimulation", "EnableHeadlessVisualGuards", true, "Skip a few visual/audio-only systems on dedicated servers to avoid pointless headless work and log spam."); _pauseExtraZoneWorkDuringSleep = ((BaseUnityPlugin)this).Config.Bind("Sleep", "PauseExtraZoneWorkDuringSleep", true, "Temporarily disable TerramizerServer's extra peer-zone warming and zone-entry streaming boost while all connected characters are in bed or the server is skipping time."); _speedUpSleepFastForward = ((BaseUnityPlugin)this).Config.Bind("Sleep", "SpeedUpSleepFastForward", true, "Tune Valheim's sleep time-skip so the night passes in the configured number of real seconds."); _sleepFastForwardSeconds = BindRange("Sleep", "SleepFastForwardSeconds", 4f, "Target real seconds for the sleep time-skip. Lower is faster, but very low values can make time-based object catch-up spike after waking.", 1f, 60f); _skipSleepWorldSave = ((BaseUnityPlugin)this).Config.Bind("Sleep", "SkipSleepWorldSave", false, "Skip the extra world save Valheim runs when sleep ends. This is off by default to preserve vanilla durability and save-system compatibility."); _preserveAutosaveTimer = ((BaseUnityPlugin)this).Config.Bind("Sleep", "PreserveAutosaveTimer", true, "Keep Valheim's regular autosave timer unchanged while skipping the sleep-triggered world save."); _logSkippedSleepSaves = ((BaseUnityPlugin)this).Config.Bind("Sleep", "LogSkippedSleepSaves", true, "Log when TerramizerServer skips a sleep-triggered world save."); _logSleepOptimizations = ((BaseUnityPlugin)this).Config.Bind("Sleep", "LogSleepOptimizations", true, "Log when TerramizerServer speeds up sleep or pauses extra zone work for sleep."); _repairStaleWearNTearRemoveOwnership = ((BaseUnityPlugin)this).Config.Bind("BuildInteractions", "RepairStaleWearNTearRemoveOwnership", true, "Recover legitimate hammer deconstruct RPCs when a build piece is routed to a different, stale, disconnected, or inactive ZDO owner. The requester must be nearby, the piece must be removable, and no-build locations are rejected."); _maxRemoveRepairDistance = BindRange("BuildInteractions", "MaxRemoveRepairDistance", 8f, "Maximum 3D distance from the requesting player's reference position to a build piece before stale-remove ownership repair is refused.", 5f, 16f); _logRemoveOwnershipRepairs = ((BaseUnityPlugin)this).Config.Bind("BuildInteractions", "LogRemoveOwnershipRepairs", true, "Log when TerramizerServer repairs stale ownership for a build-piece remove RPC."); _repairStaleItemDropOwnership = ((BaseUnityPlugin)this).Config.Bind("ItemInteractions", "RepairStaleItemDropOwnership", true, "Let the server complete a nearby ItemDrop ownership request when food, pickup, or tame consumption is stuck on another owner route."); _logItemDropOwnershipRepairs = ((BaseUnityPlugin)this).Config.Bind("ItemInteractions", "LogItemDropOwnershipRepairs", false, "Log each server-assisted ItemDrop ownership transfer. Leave off normally because feeding and pickup can produce many requests."); _diagnosticIntervalSec = BindRange("Diagnostics", "DiagnosticIntervalSec", 60f, "Seconds between optional server status log lines.", 10f, 3600f); ClampConfig(); ApplySafetyMigrations(); _nextDiagnosticLogTime = Time.realtimeSinceStartup + Mathf.Clamp(_diagnosticIntervalSec.Value, 10f, 3600f); _createObjects = AccessTools.MethodDelegate(AccessTools.Method(typeof(ZNetScene), "CreateObjects", (Type[])null, (Type[])null), (object)null, true); _removeObjects = AccessTools.MethodDelegate(AccessTools.Method(typeof(ZNetScene), "RemoveObjects", (Type[])null, (Type[])null), (object)null, true); _createLocalZones = CreateDelegateSafely(typeof(ZoneSystem), "CreateLocalZones"); _wearNTearRemoveRpc = CreateDelegateSafely(typeof(WearNTear), "RPC_Remove"); InitializeRoutedRpcReflection(); InitializeSleepReflection(); InitializeZoneTtlReflection(); InitializeZNetSceneReflection(); _harmony = new Harmony("r4v9n1.terramizerserver"); InstallPatches(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("TerramizerServer 0.4.7 loaded. MaxQueue=" + _maxSendQueueBytes.Value + ", MinFree=" + _minFreeSendQueueBytes.Value + ", PeerInterval=" + _peerSendIntervalSeconds.Value + "s.")); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Created by R4V9N1."); } private ConfigEntry BindRange(string section, string key, T defaultValue, string description, T minimum, T maximum) where T : IComparable { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(minimum, maximum), Array.Empty())); } private void Update() { if (CanRunOnServer()) { MaybeLogDiagnostics(); } } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); _harmony = null; } } private void InstallPatches() { bool num = HasPlugin("CW_Jesse.BetterNetworking") || HasPlugin("com.Fire.FiresGhettoNetworkMod") || HasPlugin("is.codex.valheim.zdostreamtuner"); bool flag = HasPlugin("MVP.Valheim_Serverside_Simulations"); if (num) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Detected another ZDO networking tuner. TerramizerServer will not replace send-queue constants or the send interval; its correctness and diagnostic patches remain active."); } else { PatchFeature(typeof(ZdoManSendZdosPatch), "ZDO queue limits"); PatchFeature(typeof(ZdoManSendZdoToPeersPatch), "ZDO send interval"); } if (!PatchFeature(typeof(ZdoManCreateSyncListPatch), "ZDO sync-list deduplication and priority") && _enableServerZoneStreamingBoost != null) { _enableServerZoneStreamingBoost.Value = false; ((BaseUnityPlugin)this).Logger.LogError((object)"Disabled server zone streaming boost because sync-list deduplication could not be installed safely."); } PatchFeature(typeof(ZDOManServerSortSendZdosPatch), "server ZDO distance adjustment"); PatchFeature(typeof(EnvManSkipToMorningPatch), "sleep fast-forward"); PatchFeature(typeof(GameSleepStopPatch), "sleep save policy"); PatchFeature(typeof(ZRoutedRpcHandleRoutedRpcPatch), "locally routed ownership recovery"); PatchFeature(typeof(ZRoutedRpcRouteRpcPatch), "forwarded ownership recovery"); PatchFeature(typeof(AudioManUpdateHeadlessPatch), "headless audio guard"); PatchFeature(typeof(ShieldDomeImageEffectAwakeHeadlessPatch), "headless shield initialization guard"); PatchFeature(typeof(ShieldDomeImageEffectGetDomeColorHeadlessPatch), "headless shield guard"); if (flag) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Detected Valheim Serverside Simulations. TerramizerServer's overlapping multi-peer object/zone simulation patches are disabled to avoid double work and ownership conflicts."); return; } PatchFeature(typeof(ZNetSceneRemoveObjectsSafetyPatch), "stale ZNetScene instance sanitation"); PatchFeature(typeof(ZNetSceneCreateDestroyObjectsPatch), "multi-peer object coverage"); PatchFeature(typeof(ZoneSystemUpdatePeerZoneCreationPatch), "bounded peer zone creation"); PatchFeature(typeof(ZNetSceneOutsideActiveAreaPeerPatch), "multi-peer active area"); PatchFeature(typeof(ZdoManReleaseNearbyZdosServerOwnershipPatch), "legacy ownership migration"); } private bool PatchFeature(Type patchType, string featureName) { try { _harmony.CreateClassProcessor(patchType).Patch(); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Disabled " + featureName + " because its Valheim patch no longer matched: " + ex.Message)); return false; } } private static bool HasPlugin(string guid) { if (Chainloader.PluginInfos != null) { return Chainloader.PluginInfos.ContainsKey(guid); } return false; } internal static bool CanRunOnServer() { if (_enabled == null || !_enabled.Value) { return false; } try { ZNet instance = ZNet.instance; if (_dedicatedOnly != null && _dedicatedOnly.Value) { return (Object)(object)instance != (Object)null && instance.IsDedicated(); } if (_serverOnly == null || !_serverOnly.Value) { return true; } return (Object)(object)instance != (Object)null && (instance.IsServer() || instance.IsDedicated()); } catch { return false; } } internal static bool ShouldRunCreateDestroyObjects() { return CanRunOnServer(); } internal static int GetMaxSendQueueBytes() { if (!CanRunOnServer()) { return 10240; } if (_maxSendQueueBytes != null) { return _maxSendQueueBytes.Value; } return 10240; } internal static int GetMinFreeSendQueueBytes() { if (!CanRunOnServer()) { return 2048; } if (_minFreeSendQueueBytes != null) { return _minFreeSendQueueBytes.Value; } return 2048; } internal static float GetPeerSendIntervalSeconds() { if (!CanRunOnServer()) { return 0.05f; } if (_peerSendIntervalSeconds != null) { return _peerSendIntervalSeconds.Value; } return 0.05f; } internal static bool RunCreateDestroyObjects(ZNetScene scene) { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) if (!ShouldRunCreateDestroyObjects()) { return true; } bool flag = false; try { ZNet instance = ZNet.instance; ZoneSystem instance2 = ZoneSystem.instance; ZDOMan instance3 = ZDOMan.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || instance3 == null) { return true; } if (ShouldPauseExtraZoneWorkForSleep()) { return true; } float realtimeSinceStartup = Time.realtimeSinceStartup; float num = ((_createDestroyIntervalSeconds == null) ? 0.1f : Mathf.Clamp(_createDestroyIntervalSeconds.Value, 0.03f, 1f)); if (realtimeSinceStartup < _nextCreateDestroyTime) { return false; } _nextCreateDestroyTime = realtimeSinceStartup + num; List connectedPeers = instance.GetConnectedPeers(); if (connectedPeers == null || connectedPeers.Count == 0) { return true; } int num2 = Mathf.Clamp(_extendedZoneRadius.Value, 0, 3); int num3 = Math.Max(1, instance2.m_activeArea + num2); int num4 = Math.Max(num3, instance2.m_activeDistantArea + num2); _nearScratch.Clear(); _distantScratch.Clear(); _coverageZones.Clear(); bool flag2 = false; for (int i = 0; i < connectedPeers.Count; i++) { ZNetPeer val = connectedPeers[i]; if (val != null && val.IsReady()) { flag2 = true; Vector2i zone = ZoneSystem.GetZone(val.GetRefPos()); Vector2i zone2 = ZoneSystem.GetZone(GetPredictedRefPos(val)); if (_coverageZones.Add(zone)) { instance3.FindSectorObjects(zone, num3, num4, _nearScratch, _distantScratch); } if (_coverageZones.Add(zone2)) { instance3.FindSectorObjects(zone2, num3, num4, _nearScratch, _distantScratch); } } } if (!flag2) { return true; } _seenZdos.Clear(); FilterAndDedupeZdos(_nearScratch, _nearFiltered); FilterAndDedupeZdos(_distantScratch, _distantFiltered); if (_createObjects != null) { _createObjects(scene, _nearFiltered, _distantFiltered); } float num5 = ((_removeObjectsIntervalSeconds == null) ? 0.5f : Mathf.Clamp(_removeObjectsIntervalSeconds.Value, num, 2f)); if (_removeObjects != null && realtimeSinceStartup >= _nextRemoveObjectsTime) { _nextRemoveObjectsTime = realtimeSinceStartup + num5; flag = true; RepairStaleSceneInstances(scene); _removeObjects(scene, _nearFiltered, _distantFiltered); flag = false; } _diagnosticCreateDestroyPasses++; return false; } catch (Exception ex) { if (_log != null) { if (flag) { _log.LogWarning((object)("TerramizerServer RemoveObjects pass failed; suppressing vanilla retry for this frame: " + ex.Message)); } else { _log.LogWarning((object)("TerramizerServer CreateDestroyObjects fell back to vanilla: " + ex.Message)); } } return !flag; } } internal static void SortZdosForServer(List toSync, Vector3 refPos) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (!CanRunOnServer() || toSync == null || toSync.Count == 0) { return; } float num = ((_enableZDOThrottling != null && _enableZDOThrottling.Value) ? Mathf.Max(0f, _zdoThrottleDistance.Value) : 0f); float num2 = num * num; for (int i = 0; i < toSync.Count; i++) { ZDO val = toSync[i]; if (val != null && val.IsValid() && !(num2 <= 0f) && (int)val.Type == 0) { Vector3 position = val.GetPosition(); float num3 = position.x - refPos.x; float num4 = position.z - refPos.z; if (num3 * num3 + num4 * num4 > num2) { val.m_tempSortValue += 500f; } } } } internal static void PostProcessSyncList(object zdoPeer, List toSync) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (!CanRunOnServer() || zdoPeer == null || toSync == null) { return; } ZNetPeer netPeer = GetNetPeer(zdoPeer); HashSet forceSendSet = GetForceSendSet(zdoPeer); _syncSeenZdos.Clear(); int num = 0; int count = toSync.Count; for (int i = 0; i < count; i++) { ZDO val = toSync[i]; if (val != null && val.IsValid() && !((ZDOID)(ref val.m_uid)).IsNone() && _syncSeenZdos.Add(val.m_uid)) { toSync[num++] = val; } } if (num < count) { toSync.RemoveRange(num, count - num); _diagnosticSyncDuplicatesRemoved += count - num; } _syncForceSend = forceSendSet; _syncReceiverUid = netPeer?.m_uid ?? 0; _syncReceiverRefPos = ((netPeer == null) ? Vector3.zero : netPeer.GetRefPos()); _syncPlayerPriority = _enablePlayerPriority != null && _enablePlayerPriority.Value; try { toSync.Sort(CompareFinalSyncZdos); } finally { _syncForceSend = null; } _diagnosticMaxSyncList = Math.Max(_diagnosticMaxSyncList, toSync.Count); if (netPeer != null && netPeer.m_socket != null) { _diagnosticPeakSendQueue = Math.Max(_diagnosticPeakSendQueue, netPeer.m_socket.GetSendQueueSize()); } if (_pauseExtraStreamingDuringDenseSync != null && _pauseExtraStreamingDuringDenseSync.Value && _denseSyncThreshold != null && toSync.Count >= _denseSyncThreshold.Value) { float num2 = ((_denseSyncPauseSeconds == null) ? 2f : Mathf.Clamp(_denseSyncPauseSeconds.Value, 0.5f, 30f)); _denseSyncPauseUntil = Math.Max(_denseSyncPauseUntil, Time.realtimeSinceStartup + num2); } MaybeLogDenseSync(zdoPeer, toSync); } private static int CompareFinalSyncZdos(ZDO left, ZDO right) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Invalid comparison between Unknown and I4 //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Invalid comparison between Unknown and I4 //IL_00cb: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected I4, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected I4, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) bool flag = _syncForceSend != null && _syncForceSend.Contains(left.m_uid); bool flag2 = _syncForceSend != null && _syncForceSend.Contains(right.m_uid); if (flag != flag2) { if (!flag) { return 1; } return -1; } if (_syncPlayerPriority) { int playerPrefabHash = GetPlayerPrefabHash(); bool flag3 = left.GetPrefab() == playerPrefabHash; bool flag4 = right.GetPrefab() == playerPrefabHash; if (flag3 != flag4) { if (!flag3) { return 1; } return -1; } } bool flag5 = (int)left.Type == 1 && left.HasOwner() && left.GetOwner() != _syncReceiverUid; bool flag6 = (int)right.Type == 1 && right.HasOwner() && right.GetOwner() != _syncReceiverUid; if (flag5 != flag6) { if (!flag5) { return 1; } return -1; } if (left.Type != right.Type) { return ((int)right.Type).CompareTo((int)left.Type); } if (flag) { Vector3 val = left.GetPosition() - _syncReceiverRefPos; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; val = right.GetPosition() - _syncReceiverRefPos; float sqrMagnitude2 = ((Vector3)(ref val)).sqrMagnitude; return Utils.CompareFloats(sqrMagnitude, sqrMagnitude2); } return Utils.CompareFloats(left.m_tempSortValue, right.m_tempSortValue); } internal static void RunPeerZoneCreation(ZoneSystem zoneSystem) { //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) if (!CanRunOnServer() || (Object)(object)zoneSystem == (Object)null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; float num = ((_peerZoneUpdateIntervalSeconds == null) ? 0.5f : Mathf.Clamp(_peerZoneUpdateIntervalSeconds.Value, 0.05f, 2f)); if (realtimeSinceStartup < _nextPeerZoneCreateTime) { return; } _nextPeerZoneCreateTime = realtimeSinceStartup + num; RefreshLoadedPeerZoneTtls(zoneSystem); if (ShouldPauseExtraZoneWorkForSleep()) { return; } if (_pauseExtraStreamingDuringDenseSync != null && _pauseExtraStreamingDuringDenseSync.Value && realtimeSinceStartup < _denseSyncPauseUntil) { _diagnosticDensePressurePauses++; return; } bool flag = _enablePeerZoneCreation != null && _enablePeerZoneCreation.Value && _createLocalZones != null; bool flag2 = IsZoneStreamingBoostEnabled(); if (!flag && !flag2) { return; } try { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return; } ZDOMan instance2 = ZDOMan.instance; if (instance2 == null) { flag2 = false; } List connectedPeers = instance.GetConnectedPeers(); if (connectedPeers == null) { return; } _readyPeerIds.Clear(); _coverageZones.Clear(); _zoneCreationCenters.Clear(); for (int i = 0; i < connectedPeers.Count; i++) { ZNetPeer val = connectedPeers[i]; if (val == null || !val.IsReady()) { continue; } _readyPeerIds.Add(val.m_uid); Vector3 refPos = val.GetRefPos(); Vector3 predictedRefPos = GetPredictedRefPos(val); if (flag) { if (_coverageZones.Add(ZoneSystem.GetZone(refPos))) { _zoneCreationCenters.Add(refPos); } if (_coverageZones.Add(ZoneSystem.GetZone(predictedRefPos))) { _zoneCreationCenters.Add(predictedRefPos); } } if (flag2) { MaybeBoostPeerZoneStreaming(instance2, zoneSystem, val, predictedRefPos); } } if (flag && _zoneCreationCenters.Count > 0) { int num2 = ((_maxPeerZoneCreationsPerPass == null) ? 1 : Mathf.Clamp(_maxPeerZoneCreationsPerPass.Value, 1, 16)); int num3 = _nextZoneCreationCenter % _zoneCreationCenters.Count; int num4 = 0; for (int j = 0; j < _zoneCreationCenters.Count; j++) { if (num4 >= num2) { break; } int index = (num3 + j) % _zoneCreationCenters.Count; _diagnosticZoneCreateAttempts++; if (_createLocalZones(zoneSystem, _zoneCreationCenters[index])) { num4++; _diagnosticZoneCreateSuccesses++; } } _nextZoneCreationCenter = (num3 + 1) % _zoneCreationCenters.Count; } PrunePeerTracking(_readyPeerIds); } catch (Exception ex) { LogWarningThrottled("Peer zone creation skipped after an error: " + ex.Message); } } internal static bool TryMultiPeerOutsideActiveArea(Vector3 point, ref bool result) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00b3: 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_00d2: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) if (!CanRunOnServer() || _enableMultiPeerOutsideActiveArea == null || !_enableMultiPeerOutsideActiveArea.Value) { return true; } if (ShouldPauseExtraZoneWorkForSleep()) { return true; } try { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return true; } List connectedPeers = instance.GetConnectedPeers(); if (connectedPeers == null || connectedPeers.Count == 0) { return true; } ZoneSystem instance2 = ZoneSystem.instance; if ((Object)(object)instance2 == (Object)null) { return true; } bool flag = false; for (int i = 0; i < connectedPeers.Count; i++) { ZNetPeer val = connectedPeers[i]; if (val != null && val.IsReady()) { flag = true; int num = Math.Max(1, instance2.m_activeArea); Vector2i zone = ZoneSystem.GetZone(val.GetRefPos()); if (!ZNetScene.OutsideActiveArea(point, zone, num)) { result = false; return false; } Vector2i zone2 = ZoneSystem.GetZone(GetPredictedRefPos(val)); if (!(zone2 == zone) && !ZNetScene.OutsideActiveArea(point, zone2, num)) { result = false; return false; } } } if (!flag) { return true; } result = true; return false; } catch (Exception ex) { LogWarningThrottled("Multi-peer active-area check fell back to vanilla: " + ex.Message); return true; } } internal static void RunServerOwnershipForPersistentZdos(ZDOMan zdoMan, Vector3 refPosition) { if (_enableServerOwnershipForPersistentZdos != null && _enableServerOwnershipForPersistentZdos.Value) { _enableServerOwnershipForPersistentZdos.Value = false; if (_log != null) { _log.LogWarning((object)"Disabled legacy persistent-ZDO server ownership. TerramizerServer now uses server-side streaming boosts instead of mass ownership transfer."); } } } private static bool IsZoneStreamingBoostEnabled() { if (_enableServerZoneStreamingBoost != null && _enableServerZoneStreamingBoost.Value) { return !ShouldPauseExtraZoneWorkForSleep(); } return false; } internal static void TuneSleepFastForward(EnvMan envMan) { if (!CanRunOnServer() || _speedUpSleepFastForward == null || !_speedUpSleepFastForward.Value || (Object)(object)envMan == (Object)null || !envMan.IsTimeSkipping()) { return; } try { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return; } if (_envManSkipToTimeField == null || _envManTimeSkipSpeedField == null) { LogWarningThrottled("Sleep fast-forward tuning is unavailable because EnvMan sleep fields could not be found."); return; } double num = (double)_envManSkipToTimeField.GetValue(envMan) - instance.GetTimeSeconds(); if (!(num <= 0.0)) { float num2 = ((_sleepFastForwardSeconds == null) ? 4f : Mathf.Clamp(_sleepFastForwardSeconds.Value, 1f, 60f)); double num3 = num / (double)num2; _envManTimeSkipSpeedField.SetValue(envMan, num3); if (_logSleepOptimizations != null && _logSleepOptimizations.Value && _log != null) { _log.LogInfo((object)("Sleep fast-forward tuned to about " + num2.ToString("0.##") + " real second(s).")); } } } catch (Exception ex) { LogWarningThrottled("Sleep fast-forward tuning failed: " + ex.Message); } } internal static void SavePlayerProfileFromSleep(Game game, bool setLogoutPoint) { if ((Object)(object)game == (Object)null) { return; } bool flag = ShouldSkipSleepWorldSave() && _preserveAutosaveTimer != null && _preserveAutosaveTimer.Value && _gameSaveTimerField != null; float num = 0f; if (flag) { try { num = (float)_gameSaveTimerField.GetValue(game); } catch (Exception ex) { flag = false; LogWarningThrottled("Could not read Game.m_saveTimer before sleep save: " + ex.Message); } } game.SavePlayerProfile(setLogoutPoint); if (!flag) { return; } try { _gameSaveTimerField.SetValue(game, num); } catch (Exception ex2) { LogWarningThrottled("Could not restore Game.m_saveTimer after sleep save: " + ex2.Message); } } internal static void SaveWorldFromSleep(ZNet znet, bool sync, bool saveOtherPlayerProfiles, bool waitForNextFrame) { if (!ShouldSkipSleepWorldSave()) { if ((Object)(object)znet != (Object)null) { znet.Save(sync, saveOtherPlayerProfiles, waitForNextFrame); } } else if (_logSkippedSleepSaves != null && _logSkippedSleepSaves.Value && _log != null) { _log.LogInfo((object)"Skipped sleep-triggered world save. The regular autosave schedule is unchanged."); } } private static bool ShouldSkipSleepWorldSave() { if (CanRunOnServer() && _skipSleepWorldSave != null) { return _skipSleepWorldSave.Value; } return false; } private unsafe static void MaybeBoostPeerZoneStreaming(ZDOMan zdoMan, ZoneSystem zoneSystem, ZNetPeer peer, Vector3 predictedRefPos) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) if (zdoMan == null || (Object)(object)zoneSystem == (Object)null || peer == null || !peer.IsReady()) { return; } long uid = peer.m_uid; Vector3 refPos = peer.GetRefPos(); Vector2i zone = ZoneSystem.GetZone(refPos); Vector2i zone2 = ZoneSystem.GetZone(predictedRefPos); float realtimeSinceStartup = Time.realtimeSinceStartup; PeerStreamingState value; bool flag = _peerStreamingStates.TryGetValue(uid, out value) && value.HasZone; if (realtimeSinceStartup < value.NextBoostTime || (flag && value.CurrentZone == zone && value.PredictedZone == zone2)) { return; } int num = Math.Max(1, GetMaxSendQueueBytes()); int num2 = ((_zoneStreamingBoostMaxQueuePercent == null) ? 35 : Mathf.Clamp(_zoneStreamingBoostMaxQueuePercent.Value, 10, 90)); int num3 = ((peer.m_socket != null) ? peer.m_socket.GetSendQueueSize() : 0); _diagnosticPeakSendQueue = Math.Max(_diagnosticPeakSendQueue, num3); if ((long)num3 * 100L >= (long)num * (long)num2) { value.NextBoostTime = realtimeSinceStartup + 0.25f; _peerStreamingStates[uid] = value; _diagnosticBoostBackpressureSkips++; return; } int num4 = QueueZoneStreamingBoost(zdoMan, zoneSystem, peer, zone, zone2, refPos); value.HasZone = true; value.CurrentZone = zone; value.PredictedZone = zone2; value.NextBoostTime = realtimeSinceStartup + ((_zoneStreamingBoostCooldownSeconds == null) ? 1.5f : Mathf.Clamp(_zoneStreamingBoostCooldownSeconds.Value, 0.1f, 10f)); _peerStreamingStates[uid] = value; _diagnosticBoostQueued += num4; if (num4 > 0 && _logZoneStreamingBoosts != null && _logZoneStreamingBoosts.Value && _log != null) { string text = (string.IsNullOrEmpty(peer.m_playerName) ? "unknown peer" : peer.m_playerName); ManualLogSource log = _log; string[] obj = new string[7] { "Queued ", num4.ToString(), " server-side ZDO streaming boost object(s) for ", text, " at zone ", null, null }; Vector2i val = zone; obj[5] = ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString(); obj[6] = "."; log.LogInfo((object)string.Concat(obj)); } } private static int QueueZoneStreamingBoost(ZDOMan zdoMan, ZoneSystem zoneSystem, ZNetPeer peer, Vector2i currentZone, Vector2i predictedZone, Vector3 refPos) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) int num = ((_maxZoneStreamingBoostZdosPerPeer == null) ? 128 : Mathf.Clamp(_maxZoneStreamingBoostZdosPerPeer.Value, 0, 5000)); if (num <= 0) { return 0; } int num2 = ((_zoneStreamingBoostExtraRadius != null) ? Mathf.Clamp(_zoneStreamingBoostExtraRadius.Value, 0, 3) : 0); int num3 = Math.Max(1, zoneSystem.m_activeArea + num2); int num4 = ((_zoneStreamingBoostIncludeDistantZdos != null && _zoneStreamingBoostIncludeDistantZdos.Value) ? Math.Max(0, zoneSystem.m_activeDistantArea) : 0); _streamBoostNearScratch.Clear(); _streamBoostDistantScratch.Clear(); _streamBoostCandidates.Clear(); _streamBoostSeenZdos.Clear(); zdoMan.FindSectorObjects(currentZone, num3, num4, _streamBoostNearScratch, _streamBoostDistantScratch); if (!(predictedZone == currentZone)) { zdoMan.FindSectorObjects(predictedZone, num3, num4, _streamBoostNearScratch, _streamBoostDistantScratch); } AddStreamingBoostCandidates(_streamBoostNearScratch, refPos, near: true); if (_zoneStreamingBoostIncludeDistantZdos != null && _zoneStreamingBoostIncludeDistantZdos.Value && _streamBoostCandidates.Count < num) { AddStreamingBoostCandidates(_streamBoostDistantScratch, refPos, near: false); } _streamBoostCandidates.Sort(CompareStreamingBoostZdos); int num5 = 0; for (int i = 0; i < _streamBoostCandidates.Count; i++) { if (num5 >= num) { break; } ZDO val = _streamBoostCandidates[i]; if (val != null && val.IsValid() && !((ZDOID)(ref val.m_uid)).IsNone()) { zdoMan.ForceSendZDO(peer.m_uid, val.m_uid); num5++; } } _streamBoostNearScratch.Clear(); _streamBoostDistantScratch.Clear(); _streamBoostCandidates.Clear(); _streamBoostSeenZdos.Clear(); return num5; } private static void AddStreamingBoostCandidates(List source, Vector3 refPos, bool near) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return; } ZNetScene instance = ZNetScene.instance; for (int i = 0; i < source.Count; i++) { ZDO val = source[i]; if (val != null && val.IsValid() && !((ZDOID)(ref val.m_uid)).IsNone() && _streamBoostSeenZdos.Add(val.m_uid) && (!((Object)(object)instance != (Object)null) || instance.HasPrefab(val.GetPrefab())) && !ShouldDelayTerrainCompilerZdo(val, instance)) { val.m_tempSortValue = GetStreamingBoostSortValue(val, refPos, near); _streamBoostCandidates.Add(val); } } } private static float GetStreamingBoostSortValue(ZDO zdo, Vector3 refPos, bool near) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Vector3 position = zdo.GetPosition(); float num = position.x - refPos.x; float num2 = position.z - refPos.z; float num3 = num * num + num2 * num2; if (!near) { num3 += 1000000f; } return num3; } private static int CompareStreamingBoostZdos(ZDO left, ZDO right) { int num = GetStreamingBoostTypeRank(left).CompareTo(GetStreamingBoostTypeRank(right)); if (num != 0) { return num; } return Utils.CompareFloats(left.m_tempSortValue, right.m_tempSortValue); } private static int GetStreamingBoostTypeRank(ZDO zdo) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 if (_enablePlayerPriority != null && _enablePlayerPriority.Value && zdo.GetPrefab() == GetPlayerPrefabHash()) { return 0; } if ((int)zdo.Type == 3) { return 1; } if ((int)zdo.Type == 2) { return 2; } if ((int)zdo.Type == 1) { return 3; } return 4; } internal static bool TryHandleRoutedOwnershipRpc(object routedRpcData) { if (!CanRunOnServer() || routedRpcData == null || !HasRoutedRpcReflection()) { return false; } try { int num = (int)_routedRpcMethodHashField.GetValue(routedRpcData); if (num == StringExtensionMethods.GetStableHashCode("RPC_Remove")) { return TryHandleStaleWearNTearRemoveRpc(routedRpcData); } if (num == StringExtensionMethods.GetStableHashCode("RPC_RequestOwn")) { return TryHandleItemDropOwnershipRequest(routedRpcData); } } catch (Exception ex) { LogRemoveRepairWarning("Routed ownership repair inspection failed: " + ex.Message); } return false; } private unsafe static bool TryHandleItemDropOwnershipRequest(object routedRpcData) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) if (_repairStaleItemDropOwnership == null || !_repairStaleItemDropOwnership.Value) { return false; } ZDOID val = (ZDOID)_routedRpcTargetZdoField.GetValue(routedRpcData); long num = (long)_routedRpcSenderPeerIdField.GetValue(routedRpcData); if (((ZDOID)(ref val)).IsNone() || num == 0L) { return false; } ZNet instance = ZNet.instance; ZDOMan instance2 = ZDOMan.instance; ZNetScene instance3 = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance2 == null || (Object)(object)instance3 == (Object)null || !instance.IsServer()) { return false; } ZDO zDO = instance2.GetZDO(val); if (zDO == null || !zDO.IsValid()) { return false; } bool flag = num == ZNet.GetUID(); ZNetPeer val2 = (flag ? null : instance.GetPeer(num)); if (!flag && (val2 == null || !val2.IsReady() || !IsPeerActiveNearZdo(val2, zDO))) { return false; } GameObject prefab = instance3.GetPrefab(zDO.GetPrefab()); if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponentInChildren(true) == (Object)null) { return false; } if (flag) { ZNetView val3 = instance3.FindInstance(zDO); if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).GetComponent() == (Object)null) { return false; } } long owner = zDO.GetOwner(); if (owner == num) { if (flag) { return false; } instance2.ForceSendZDO(num, val); _routedRpcTargetPeerIdField.SetValue(routedRpcData, ZNet.GetUID()); _diagnosticItemDropOwnershipRefreshes++; if (_logItemDropOwnershipRepairs != null && _logItemDropOwnershipRepairs.Value && _log != null) { ManualLogSource log = _log; string[] obj = new string[5] { "Re-sent authoritative ItemDrop ownership for ZDO ", null, null, null, null }; ZDOID val4 = val; obj[1] = ((object)(*(ZDOID*)(&val4))/*cast due to .constrained prefix*/).ToString(); obj[2] = " to recorded owner "; obj[3] = num.ToString(); obj[4] = "."; log.LogInfo((object)string.Concat(obj)); } return true; } zDO.SetOwner(num); if (!flag) { instance2.ForceSendZDO(num, val); } _routedRpcTargetPeerIdField.SetValue(routedRpcData, ZNet.GetUID()); _diagnosticItemDropOwnershipRepairs++; if (_logItemDropOwnershipRepairs != null && _logItemDropOwnershipRepairs.Value && _log != null) { string text = (flag ? "server simulation" : ("nearby peer " + num)); ManualLogSource log2 = _log; string[] obj2 = new string[7] { "Transferred ItemDrop ZDO ", null, null, null, null, null, null }; ZDOID val4 = val; obj2[1] = ((object)(*(ZDOID*)(&val4))/*cast due to .constrained prefix*/).ToString(); obj2[2] = " from owner "; obj2[3] = owner.ToString(); obj2[4] = " to "; obj2[5] = text; obj2[6] = "."; log2.LogInfo((object)string.Concat(obj2)); } return true; } internal unsafe static bool TryHandleStaleWearNTearRemoveRpc(object routedRpcData) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) if (!CanRunOnServer() || _repairStaleWearNTearRemoveOwnership == null || !_repairStaleWearNTearRemoveOwnership.Value || routedRpcData == null) { return false; } if (!HasRoutedRpcReflection()) { LogRemoveRepairWarning("Stale remove ownership repair is unavailable because Valheim's routed-RPC fields could not be found."); return false; } if (_routedRpcParametersField == null || _wearNTearRemoveRpc == null) { LogRemoveRepairWarning("Stale remove ownership repair is unavailable because Valheim's WearNTear remove handler could not be bound safely."); return false; } try { if ((int)_routedRpcMethodHashField.GetValue(routedRpcData) != StringExtensionMethods.GetStableHashCode("RPC_Remove")) { return false; } ZDOID val = (ZDOID)_routedRpcTargetZdoField.GetValue(routedRpcData); if (((ZDOID)(ref val)).IsNone()) { return false; } ZDOMan instance = ZDOMan.instance; ZNetScene instance2 = ZNetScene.instance; ZNet instance3 = ZNet.instance; if (instance == null || (Object)(object)instance2 == (Object)null || (Object)(object)instance3 == (Object)null || !instance3.IsServer()) { return false; } ZDO zDO = instance.GetZDO(val); if (zDO == null || !zDO.IsValid()) { return false; } long num = (long)_routedRpcSenderPeerIdField.GetValue(routedRpcData); if (!IsRemoveRequesterCloseEnough(instance3, num, zDO.GetPosition())) { return false; } if (Location.IsInsideNoBuildLocation(zDO.GetPosition())) { return false; } ZNetView val2 = instance2.FindInstance(zDO); if ((Object)(object)val2 == (Object)null) { val2 = TryCreateServerInstanceForRemove(instance2, zDO); if ((Object)(object)val2 == (Object)null) { ZDOID val3 = val; LogRemoveRepairWarning("Could not handle build-piece remove on the server because there is no active instance for ZDO " + ((object)(*(ZDOID*)(&val3))/*cast due to .constrained prefix*/).ToString() + "."); return false; } } WearNTear component = ((Component)val2).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Piece component2 = ((Component)val2).GetComponent(); if ((Object)(object)component2 != (Object)null && !component2.CanBeRemoved()) { return false; } long targetPeerId = (long)_routedRpcTargetPeerIdField.GetValue(routedRpcData); long owner = zDO.GetOwner(); long uID = ZNet.GetUID(); if (!ShouldServerHandleRemoveRpc(instance3, zDO, owner, targetPeerId, uID, num)) { return false; } zDO.SetOwner(uID); object? value = _routedRpcParametersField.GetValue(routedRpcData); ZPackage val4 = (ZPackage)((value is ZPackage) ? value : null); if (val4 == null || _wearNTearRemoveRpc == null) { zDO.SetOwner(owner); return false; } int pos = val4.GetPos(); bool blockDrop; try { val4.SetPos(0); blockDrop = val4.ReadBool(); } finally { val4.SetPos(pos); } try { _wearNTearRemoveRpc(component, num, blockDrop); } catch { if (zDO.IsValid()) { zDO.SetOwner(owner); } throw; } if (zDO.IsValid()) { zDO.SetOwner(owner); return false; } if (_logRemoveOwnershipRepairs != null && _logRemoveOwnershipRepairs.Value && _log != null) { ManualLogSource log = _log; string[] obj2 = new string[5] { "Handled build-piece remove on server for ZDO ", null, null, null, null }; ZDOID val3 = val; obj2[1] = ((object)(*(ZDOID*)(&val3))/*cast due to .constrained prefix*/).ToString(); obj2[2] = " after temporarily moving ownership from "; obj2[3] = owner.ToString(); obj2[4] = " to server."; log.LogInfo((object)string.Concat(obj2)); } _routedRpcTargetPeerIdField.SetValue(routedRpcData, uID); return true; } catch (Exception innerException) { while (innerException.InnerException != null) { innerException = innerException.InnerException; } LogRemoveRepairWarning("Stale build-piece remove ownership repair failed in " + innerException.GetType().Name + ": " + innerException.Message); return false; } } private static void ApplySafetyMigrations() { if (_enablePeerZoneCreation != null && _enablePeerZoneCreation.Value) { _enablePeerZoneCreation.Value = false; if (_log != null) { _log.LogWarning((object)"Disabled experimental peer zone creation because it can repeatedly reload dungeon-bearing zones. Normal Valheim ghost-zone generation remains active."); } } if (_enableServerOwnershipForPersistentZdos != null && _enableServerOwnershipForPersistentZdos.Value) { _enableServerOwnershipForPersistentZdos.Value = false; if (_log != null) { _log.LogWarning((object)"Disabled legacy persistent-ZDO server ownership. Use Streaming.EnableServerZoneStreamingBoost for faster area data without server ownership transfer."); } } if (_safetyDefaultsVersion != null && _safetyDefaultsVersion.Value < 1) { if (_enableZDOThrottling != null) { _enableZDOThrottling.Value = false; } if (_skipSleepWorldSave != null) { _skipSleepWorldSave.Value = false; } _safetyDefaultsVersion.Value = 1; if (_log != null) { _log.LogWarning((object)"Applied 0.4 safety migration: disabled ZDO throttling and sleep-save skipping."); } } if (_safetyDefaultsVersion != null && _safetyDefaultsVersion.Value < 2) { if (_repairStaleWearNTearRemoveOwnership != null) { _repairStaleWearNTearRemoveOwnership.Value = true; } _safetyDefaultsVersion.Value = 2; if (_log != null) { _log.LogWarning((object)"Applied 0.4.2 ownership-route migration: enabled narrow build-piece remove recovery so non-owner players are not dependent on another client's ownership route."); } } if (_safetyDefaultsVersion != null && _safetyDefaultsVersion.Value < 3) { _maxSendQueueBytes.Value = 16384; _peerSendIntervalSeconds.Value = 0.05f; _zoneStreamingBoostExtraRadius.Value = 0; _maxZoneStreamingBoostZdosPerPeer.Value = 128; _zoneStreamingBoostCooldownSeconds.Value = 1.5f; _extendedZoneRadius.Value = 0; _createDestroyIntervalSeconds.Value = 0.1f; _removeObjectsIntervalSeconds.Value = 0.5f; _peerZoneUpdateIntervalSeconds.Value = 0.5f; _maxPeerZoneCreationsPerPass.Value = 1; if (_repairStaleItemDropOwnership != null) { _repairStaleItemDropOwnership.Value = true; } _safetyDefaultsVersion.Value = 3; if (_log != null) { _log.LogWarning((object)"Applied 0.4.3 dense-world migration: reduced bulk streaming and zone-scan pressure, and enabled nearby ItemDrop ownership-request recovery."); } } if (_safetyDefaultsVersion != null && _safetyDefaultsVersion.Value < 4) { if (_enablePeerZoneCreation != null) { _enablePeerZoneCreation.Value = false; } _safetyDefaultsVersion.Value = 4; if (_log != null) { _log.LogWarning((object)"Applied 0.4.6 zone-loading migration: disabled experimental full local peer-zone instantiation. Valheim's normal peer ghost-zone generation remains enabled."); } } } internal static bool ShouldSkipHeadlessVisualSystems() { if (_enabled == null || !_enabled.Value || _enableHeadlessVisualGuards == null || !_enableHeadlessVisualGuards.Value) { return false; } try { if (Application.isBatchMode) { return true; } if (!CanRunOnServer()) { return false; } ZNet instance = ZNet.instance; return (Object)(object)instance != (Object)null && instance.IsDedicated(); } catch { return false; } } private static bool ShouldPauseExtraZoneWorkForSleep() { if (_pauseExtraZoneWorkDuringSleep == null || !_pauseExtraZoneWorkDuringSleep.Value) { return false; } try { EnvMan instance = EnvMan.instance; if ((Object)(object)instance != (Object)null && instance.IsTimeSkipping()) { LogSleepPause(); return true; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup >= _nextSleepBedCheckTime) { _nextSleepBedCheckTime = realtimeSinceStartup + 0.2f; _sleepBedPauseActive = AreAllCharactersInBed(); } if (_sleepBedPauseActive) { LogSleepPause(); return true; } } catch { return false; } return false; } private static bool AreAllCharactersInBed() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return false; } List allCharacterZDOS = instance.GetAllCharacterZDOS(); if (allCharacterZDOS == null || allCharacterZDOS.Count == 0) { return false; } bool result = false; for (int i = 0; i < allCharacterZDOS.Count; i++) { ZDO val = allCharacterZDOS[i]; if (val != null && val.IsValid()) { result = true; if (!val.GetBool(ZDOVars.s_inBed, false)) { return false; } } } return result; } private static void LogSleepPause() { if (_log != null && _logSleepOptimizations != null && _logSleepOptimizations.Value) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < _nextSleepPauseLogTime)) { _nextSleepPauseLogTime = realtimeSinceStartup + 30f; _log.LogInfo((object)"Paused extra zone warming while players are sleeping."); } } } private static bool ShouldServerHandleRemoveRpc(ZNet znet, ZDO zdo, long owner, long targetPeerId, long serverUid, long senderPeerId) { if ((Object)(object)znet == (Object)null || zdo == null) { return false; } if (owner == 0L || owner == serverUid || targetPeerId == 0L || targetPeerId == serverUid) { return true; } if (senderPeerId != 0L && senderPeerId != owner) { return true; } ZNetPeer peer = znet.GetPeer(targetPeerId); if (peer == null || !peer.IsReady()) { return true; } return !IsPeerActiveNearZdo(peer, zdo); } private unsafe static ZNetView TryCreateServerInstanceForRemove(ZNetScene scene, ZDO zdo) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)scene == (Object)null || zdo == null || _createObjects == null) { return null; } try { List list = new List(1); List distantObjects = new List(0); list.Add(zdo); _createObjects(scene, list, distantObjects); ZNetView obj = scene.FindInstance(zdo); if ((Object)(object)obj != (Object)null && _logRemoveOwnershipRepairs != null && _logRemoveOwnershipRepairs.Value && _log != null) { ManualLogSource log = _log; ZDOID uid = zdo.m_uid; log.LogInfo((object)("Created missing server instance for build-piece remove ZDO " + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString() + ".")); } return obj; } catch (Exception ex) { LogRemoveRepairWarning("Could not create missing server instance for build-piece remove: " + ex.Message); return null; } } private static bool IsRemoveRequesterCloseEnough(ZNet znet, long senderPeerId, Vector3 zdoPosition) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)znet == (Object)null) { return false; } long uID = ZNet.GetUID(); if (senderPeerId == uID) { return true; } ZNetPeer peer = znet.GetPeer(senderPeerId); if (peer == null || !peer.IsReady()) { return false; } float num = ((_maxRemoveRepairDistance == null) ? 8f : Mathf.Clamp(_maxRemoveRepairDistance.Value, 5f, 16f)); Vector3 val = peer.GetRefPos() - zdoPosition; return ((Vector3)(ref val)).sqrMagnitude <= num * num; } private static bool IsPeerActiveNearZdo(ZNetPeer peer, ZDO zdo) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (peer == null || zdo == null) { return false; } ZoneSystem instance = ZoneSystem.instance; int num = (((Object)(object)instance == (Object)null) ? 2 : Math.Max(1, instance.m_activeArea)); return ZNetScene.InActiveArea(zdo.GetSector(), ZoneSystem.GetZone(peer.GetRefPos()), num); } private static void InitializeZNetSceneReflection() { _zNetSceneInstancesField = AccessTools.Field(typeof(ZNetScene), "m_instances"); if (_zNetSceneInstancesField == null && _log != null) { _log.LogWarning((object)"Could not bind ZNetScene.m_instances; stale scene-instance sanitation will be unavailable."); } } internal static int RepairStaleSceneInstances(ZNetScene scene) { if ((Object)(object)scene == (Object)null || _zNetSceneInstancesField == null) { return 0; } if (!(_zNetSceneInstancesField.GetValue(scene) is Dictionary { Count: not 0 } dictionary)) { return 0; } _staleSceneInstanceKeys.Clear(); foreach (KeyValuePair item in dictionary) { ZDO key = item.Key; ZNetView value = item.Value; ZDO val = null; if ((Object)(object)value != (Object)null) { try { val = value.GetZDO(); } catch { } } if (key == null || !key.IsValid() || (Object)(object)value == (Object)null || val == null || val != key) { _staleSceneInstanceKeys.Add(key); } } int num = 0; for (int i = 0; i < _staleSceneInstanceKeys.Count; i++) { ZDO val2 = _staleSceneInstanceKeys[i]; if (val2 == null || !dictionary.TryGetValue(val2, out var value2) || !dictionary.Remove(val2)) { continue; } num++; if (!((Object)(object)value2 != (Object)null)) { continue; } try { if (value2.GetZDO() == null) { Object.Destroy((Object)(object)((Component)value2).gameObject); } } catch { } } if (num > 0) { _diagnosticStaleSceneInstancesRemoved += num; if (_log != null) { _log.LogWarning((object)("Removed " + num + " stale ZNetScene instance registration(s) before RemoveObjects.")); } } _staleSceneInstanceKeys.Clear(); return num; } private static void InitializeRoutedRpcReflection() { MethodInfo methodInfo = AccessTools.Method(typeof(ZRoutedRpc), "RouteRPC", (Type[])null, (Type[])null); Type type = null; if (methodInfo != null) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1) { type = parameters[0].ParameterType; } } if (!(type == null)) { _routedRpcTargetPeerIdField = AccessTools.Field(type, "m_targetPeerID"); _routedRpcSenderPeerIdField = AccessTools.Field(type, "m_senderPeerID"); _routedRpcTargetZdoField = AccessTools.Field(type, "m_targetZDO"); _routedRpcMethodHashField = AccessTools.Field(type, "m_methodHash"); _routedRpcParametersField = AccessTools.Field(type, "m_parameters"); } } private static void InitializeSleepReflection() { _envManSkipToTimeField = AccessTools.Field(typeof(EnvMan), "m_skipToTime"); _envManTimeSkipSpeedField = AccessTools.Field(typeof(EnvMan), "m_timeSkipSpeed"); _gameSaveTimerField = AccessTools.Field(typeof(Game), "m_saveTimer"); } private static void InitializeZoneTtlReflection() { _zoneSystemZonesField = AccessTools.Field(typeof(ZoneSystem), "m_zones"); if (_zoneSystemZonesField != null) { Type[] genericArguments = _zoneSystemZonesField.FieldType.GetGenericArguments(); if (genericArguments.Length == 2) { _zoneDataTtlField = AccessTools.Field(genericArguments[1], "m_ttl"); } } if ((_zoneSystemZonesField == null || _zoneDataTtlField == null) && _enablePeerZoneCreation != null && _enablePeerZoneCreation.Value) { _enablePeerZoneCreation.Value = false; if (_log != null) { _log.LogError((object)"Disabled peer zone creation because Valheim's zone TTL fields could not be found safely."); } } } private static void RefreshLoadedPeerZoneTtls(ZoneSystem zoneSystem) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)zoneSystem == (Object)null || _enablePeerZoneCreation == null || !_enablePeerZoneCreation.Value || _zoneSystemZonesField == null || _zoneDataTtlField == null) { return; } IDictionary dictionary = _zoneSystemZonesField.GetValue(zoneSystem) as IDictionary; ZNet instance = ZNet.instance; if (dictionary == null || (Object)(object)instance == (Object)null || !instance.IsServer()) { return; } List connectedPeers = instance.GetConnectedPeers(); if (connectedPeers == null) { return; } _ttlRefreshZones.Clear(); int radius = Math.Max(1, zoneSystem.m_activeArea); for (int i = 0; i < connectedPeers.Count; i++) { ZNetPeer val = connectedPeers[i]; if (val != null && val.IsReady()) { Vector2i zone = ZoneSystem.GetZone(val.GetRefPos()); AddZoneSquare(_ttlRefreshZones, zone, radius); Vector2i zone2 = ZoneSystem.GetZone(GetPredictedRefPos(val)); if (!(zone2 == zone)) { AddZoneSquare(_ttlRefreshZones, zone2, radius); } } } foreach (Vector2i ttlRefreshZone in _ttlRefreshZones) { if (dictionary.Contains(ttlRefreshZone)) { object obj = dictionary[ttlRefreshZone]; if (obj != null) { _zoneDataTtlField.SetValue(obj, 0f); _diagnosticZoneTtlRefreshes++; } } } } private static void AddZoneSquare(HashSet zones, Vector2i center, int radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) for (int i = center.y - radius; i <= center.y + radius; i++) { for (int j = center.x - radius; j <= center.x + radius; j++) { zones.Add(new Vector2i(j, i)); } } } private static bool HasRoutedRpcReflection() { if (_routedRpcTargetPeerIdField != null && _routedRpcSenderPeerIdField != null && _routedRpcTargetZdoField != null) { return _routedRpcMethodHashField != null; } return false; } private static void LogRemoveRepairWarning(string message) { if (_log != null && !(Time.realtimeSinceStartup < _nextRemoveRepairWarningTime)) { _nextRemoveRepairWarningTime = Time.realtimeSinceStartup + 30f; _log.LogWarning((object)message); } } private static void ClampConfig() { ClampInt(_maxSendQueueBytes, 2048, 262144); ClampInt(_minFreeSendQueueBytes, 512, 131072); ClampFloat(_peerSendIntervalSeconds, 0.01f, 0.25f); ClampInt(_zoneStreamingBoostExtraRadius, 0, 3); ClampInt(_maxZoneStreamingBoostZdosPerPeer, 0, 5000); ClampFloat(_zoneStreamingBoostCooldownSeconds, 0.1f, 10f); ClampInt(_zoneStreamingBoostMaxQueuePercent, 10, 90); ClampInt(_denseSyncThreshold, 100, 100000); ClampFloat(_denseSyncLogIntervalSeconds, 5f, 600f); ClampFloat(_denseSyncPauseSeconds, 0.5f, 30f); ClampInt(_extendedZoneRadius, 0, 3); ClampFloat(_createDestroyIntervalSeconds, 0.03f, 1f); ClampFloat(_removeObjectsIntervalSeconds, 0.05f, 2f); ClampFloat(_predictionLookaheadSec, 0.5f, 10f); ClampFloat(_predictionMinVelocity, 0.5f, 20f); ClampInt(_predictionMaxLookaheadZones, 1, 25); ClampFloat(_zdoThrottleDistance, 0f, 1000f); ClampFloat(_peerZoneUpdateIntervalSeconds, 0.05f, 2f); ClampInt(_maxPeerZoneCreationsPerPass, 1, 16); ClampFloat(_sleepFastForwardSeconds, 1f, 60f); ClampFloat(_maxRemoveRepairDistance, 5f, 16f); ClampFloat(_diagnosticIntervalSec, 10f, 3600f); if (_minFreeSendQueueBytes != null && _maxSendQueueBytes != null && _minFreeSendQueueBytes.Value > _maxSendQueueBytes.Value) { _minFreeSendQueueBytes.Value = _maxSendQueueBytes.Value; } } private static bool ClampInt(ConfigEntry entry, int min, int max) { if (entry.Value < min) { entry.Value = min; return true; } if (entry.Value > max) { entry.Value = max; return true; } return false; } private static bool ClampFloat(ConfigEntry entry, float min, float max) { if (entry.Value < min) { entry.Value = min; return true; } if (entry.Value > max) { entry.Value = max; return true; } return false; } private static int GetPlayerPrefabHash() { return PlayerPrefabHash; } internal static void MaybeLogDenseSync(object zdoPeer, List toSync) { if (CanRunOnServer() && _log != null && _logDenseSyncs != null && _logDenseSyncs.Value && zdoPeer != null && toSync != null && toSync.Count >= _denseSyncThreshold.Value) { ZNetPeer netPeer = GetNetPeer(zdoPeer); long key = netPeer?.m_uid ?? 0; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!_lastDenseSyncLogTimes.TryGetValue(key, out var value) || !(realtimeSinceStartup - value < _denseSyncLogIntervalSeconds.Value)) { _lastDenseSyncLogTimes[key] = realtimeSinceStartup; string text = ((netPeer == null || string.IsNullOrEmpty(netPeer.m_playerName)) ? "unknown peer" : netPeer.m_playerName); string text2 = DescribeDenseSyncPrefabs(toSync); _log.LogInfo((object)("Dense ZDO sync for " + text + ": " + toSync.Count + " pending object(s)" + text2 + ". If this stays high, the area itself is probably too object-heavy for smooth client loading.")); } } } private static string DescribeDenseSyncPrefabs(List toSync) { _densePrefabCounts.Clear(); _densePrefabCountsSorted.Clear(); for (int i = 0; i < toSync.Count; i++) { ZDO val = toSync[i]; if (val != null && val.IsValid()) { int prefab = val.GetPrefab(); _densePrefabCounts.TryGetValue(prefab, out var value); _densePrefabCounts[prefab] = value + 1; } } foreach (KeyValuePair densePrefabCount in _densePrefabCounts) { _densePrefabCountsSorted.Add(densePrefabCount); } _densePrefabCountsSorted.Sort((KeyValuePair left, KeyValuePair right) => right.Value.CompareTo(left.Value)); int num = Math.Min(5, _densePrefabCountsSorted.Count); if (num == 0) { return string.Empty; } ZNetScene instance = ZNetScene.instance; StringBuilder stringBuilder = new StringBuilder("; top pending prefabs: "); for (int num2 = 0; num2 < num; num2++) { if (num2 > 0) { stringBuilder.Append(", "); } KeyValuePair keyValuePair = _densePrefabCountsSorted[num2]; GameObject val2 = (((Object)(object)instance == (Object)null) ? null : instance.GetPrefab(keyValuePair.Key)); stringBuilder.Append(((Object)(object)val2 == (Object)null) ? ("hash:" + keyValuePair.Key) : ((Object)val2).name); stringBuilder.Append('='); stringBuilder.Append(keyValuePair.Value); } return stringBuilder.ToString(); } private static Vector3 GetPredictedRefPos(ZNetPeer peer) { //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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) Vector3 refPos = peer.GetRefPos(); if (_enablePredictiveZoneStreaming == null || !_enablePredictiveZoneStreaming.Value) { return refPos; } float realtimeSinceStartup = Time.realtimeSinceStartup; long uid = peer.m_uid; if (!_peerMotion.TryGetValue(uid, out var value)) { value = new PeerMotionSample(refPos, Vector3.zero, realtimeSinceStartup); _peerMotion[uid] = value; return refPos; } float num = realtimeSinceStartup - value.Time; if (num >= 0.25f) { Vector3 val = refPos - value.Position; val.y = 0f; if (num > 2f || ((Vector3)(ref val)).sqrMagnitude > 16384f) { value.Velocity = Vector3.zero; } else if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { value.Velocity = Vector3.zero; } else { Vector3 val2 = val / Mathf.Max(num, 0.05f); value.Velocity = Vector3.Lerp(value.Velocity, val2, 0.5f); } value.Position = refPos; value.Time = realtimeSinceStartup; _peerMotion[uid] = value; } float num2 = Mathf.Max(0f, _predictionMinVelocity.Value); if (((Vector3)(ref value.Velocity)).magnitude < num2) { return refPos; } Vector3 predicted = refPos + value.Velocity * Mathf.Max(0.5f, _predictionLookaheadSec.Value); return ClampForwardOffsetToMaxZones(refPos, predicted, Mathf.Max(1, _predictionMaxLookaheadZones.Value)); } private static void PrunePeerTracking(HashSet readyPeerIds) { if (readyPeerIds == null) { return; } _stalePeerIds.Clear(); foreach (long key in _peerStreamingStates.Keys) { if (!readyPeerIds.Contains(key)) { _stalePeerIds.Add(key); } } for (int i = 0; i < _stalePeerIds.Count; i++) { _peerStreamingStates.Remove(_stalePeerIds[i]); } _stalePeerIds.Clear(); foreach (long key2 in _peerMotion.Keys) { if (!readyPeerIds.Contains(key2)) { _stalePeerIds.Add(key2); } } for (int j = 0; j < _stalePeerIds.Count; j++) { _peerMotion.Remove(_stalePeerIds[j]); } _stalePeerIds.Clear(); foreach (long key3 in _lastDenseSyncLogTimes.Keys) { if (!readyPeerIds.Contains(key3)) { _stalePeerIds.Add(key3); } } for (int k = 0; k < _stalePeerIds.Count; k++) { _lastDenseSyncLogTimes.Remove(_stalePeerIds[k]); } _stalePeerIds.Clear(); } private static Vector3 ClampForwardOffsetToMaxZones(Vector3 origin, Vector3 predicted, int maxZones) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Vector3 val = predicted - origin; val.y = 0f; float num = (float)maxZones * 64f; if (((Vector3)(ref val)).magnitude <= num) { return new Vector3(origin.x + val.x, predicted.y, origin.z + val.z); } Vector3 val2 = ((Vector3)(ref val)).normalized * num; return new Vector3(origin.x + val2.x, predicted.y, origin.z + val2.z); } private static ZNetPeer GetNetPeer(object zdoPeer) { Type type = zdoPeer.GetType(); if (!_netPeerFields.TryGetValue(type, out var value)) { value = AccessTools.Field(type, "m_peer"); _netPeerFields[type] = value; } if (!(value == null)) { object? value2 = value.GetValue(zdoPeer); return (ZNetPeer)((value2 is ZNetPeer) ? value2 : null); } return null; } private static HashSet GetForceSendSet(object zdoPeer) { Type type = zdoPeer.GetType(); if (!_forceSendFields.TryGetValue(type, out var value)) { value = AccessTools.Field(type, "m_forceSend"); _forceSendFields[type] = value; } if (!(value == null)) { return value.GetValue(zdoPeer) as HashSet; } return null; } private static void FilterAndDedupeZdos(List source, List destination) { destination.Clear(); ZNetScene instance = ZNetScene.instance; for (int i = 0; i < source.Count; i++) { ZDO val = source[i]; if (val != null && val.IsValid() && !((ZDOID)(ref val.m_uid)).IsNone() && (!((Object)(object)instance != (Object)null) || instance.HasPrefab(val.GetPrefab())) && !ShouldDelayTerrainCompilerZdo(val, instance) && _seenZdos.Add(val)) { destination.Add(val); } } } private static bool ShouldDelayTerrainCompilerZdo(ZDO zdo, ZNetScene scene) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || (Object)(object)scene == (Object)null || !IsTerrainCompilerPrefab(scene, zdo.GetPrefab())) { return false; } try { return (Object)(object)Heightmap.FindHeightmap(zdo.GetPosition()) == (Object)null; } catch { return true; } } private static bool IsTerrainCompilerPrefab(ZNetScene scene, int prefabHash) { if (_terrainCompilerPrefabCache.TryGetValue(prefabHash, out var value)) { return value; } bool flag = false; try { GameObject val = (((Object)(object)scene == (Object)null) ? null : scene.GetPrefab(prefabHash)); if ((Object)(object)val != (Object)null) { flag = (Object)(object)val.GetComponent() != (Object)null || (Object)(object)val.GetComponentInChildren(true) != (Object)null; } } catch { flag = false; } _terrainCompilerPrefabCache[prefabHash] = flag; return flag; } private static void MaybeLogDiagnostics() { if (_log != null && !(Time.realtimeSinceStartup < _nextDiagnosticLogTime)) { _nextDiagnosticLogTime = Time.realtimeSinceStartup + Mathf.Clamp(_diagnosticIntervalSec.Value, 10f, 3600f); _log.LogInfo((object)("TerramizerServer status: create/destroy passes=" + _diagnosticCreateDestroyPasses + ", last candidates=" + _nearFiltered.Count + " near/" + _distantFiltered.Count + " distant, zone creates=" + _diagnosticZoneCreateSuccesses + "/" + _diagnosticZoneCreateAttempts + ", boost queued=" + _diagnosticBoostQueued + ", boost backpressure skips=" + _diagnosticBoostBackpressureSkips + ", item ownership transfers=" + _diagnosticItemDropOwnershipRepairs + ", item ownership refreshes=" + _diagnosticItemDropOwnershipRefreshes + ", stale scene registrations removed=" + _diagnosticStaleSceneInstancesRemoved + ", dense-pressure pauses=" + _diagnosticDensePressurePauses + ", zone TTL refreshes=" + _diagnosticZoneTtlRefreshes + ", sync duplicates removed=" + _diagnosticSyncDuplicatesRemoved + ", max sync list=" + _diagnosticMaxSyncList + ", peak socket queue=" + _diagnosticPeakSendQueue + " bytes.")); _diagnosticCreateDestroyPasses = 0L; _diagnosticZoneCreateAttempts = 0L; _diagnosticZoneCreateSuccesses = 0L; _diagnosticBoostQueued = 0L; _diagnosticBoostBackpressureSkips = 0L; _diagnosticItemDropOwnershipRepairs = 0L; _diagnosticItemDropOwnershipRefreshes = 0L; _diagnosticStaleSceneInstancesRemoved = 0L; _diagnosticDensePressurePauses = 0L; _diagnosticZoneTtlRefreshes = 0L; _diagnosticSyncDuplicatesRemoved = 0L; _diagnosticMaxSyncList = 0; _diagnosticPeakSendQueue = 0; } } private static T CreateDelegateSafely(Type type, string methodName) where T : Delegate { try { MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { LogWarningThrottled("Could not find " + type.Name + "." + methodName + "; related server-simulation feature will stay inactive."); return null; } return AccessTools.MethodDelegate(methodInfo, (object)null, true); } catch (Exception ex) { LogWarningThrottled("Could not bind " + type.Name + "." + methodName + ": " + ex.Message); return null; } } private static void LogWarningThrottled(string message) { if (_log != null) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < _nextWarningLogTime)) { _nextWarningLogTime = realtimeSinceStartup + 30f; _log.LogWarning((object)message); } } } internal static void LogPatchMismatch(string feature, string details) { if (_log != null) { _log.LogError((object)("Left " + feature + " at vanilla behavior because the expected IL pattern was not found (" + details + "). This usually means Valheim changed; update TerramizerServer before forcing the feature.")); } } } [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] internal static class ZdoManSendZdosPatch { private static IEnumerable Transpiler(IEnumerable instructions) { //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Expected O, but got Unknown List list = new List(instructions); MethodInfo methodInfo = AccessTools.Method(typeof(TerramizerServerPlugin), "GetMaxSendQueueBytes", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(TerramizerServerPlugin), "GetMinFreeSendQueueBytes", (Type[])null, (Type[])null); int num = 0; int num2 = 0; for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if (val.opcode == OpCodes.Ldc_I4 && val.operand is int) { int num3 = (int)val.operand; num += ((num3 == 10240) ? 1 : 0); num2 += ((num3 == 2048) ? 1 : 0); } } if (methodInfo == null || methodInfo2 == null || num != 2 || num2 != 1) { TerramizerServerPlugin.LogPatchMismatch("ZDO queue limits", "max=" + num + ", min=" + num2); return list; } List list2 = new List(list.Count); foreach (CodeInstruction item in list) { if (item.opcode == OpCodes.Ldc_I4 && item.operand is int) { switch ((int)item.operand) { case 10240: list2.Add(CopyLabelsAndBlocks(item, new CodeInstruction(OpCodes.Call, (object)methodInfo))); continue; case 2048: list2.Add(CopyLabelsAndBlocks(item, new CodeInstruction(OpCodes.Call, (object)methodInfo2))); continue; } } list2.Add(item); } return list2; } private static CodeInstruction CopyLabelsAndBlocks(CodeInstruction source, CodeInstruction target) { target.labels.AddRange(source.labels); target.blocks.AddRange(source.blocks); return target; } } [HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")] internal static class ZdoManSendZdoToPeersPatch { private static IEnumerable Transpiler(IEnumerable instructions) { //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown List list = new List(instructions); MethodInfo methodInfo = AccessTools.Method(typeof(TerramizerServerPlugin), "GetPeerSendIntervalSeconds", (Type[])null, (Type[])null); int num = 0; for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if (val.opcode == OpCodes.Ldc_R4 && val.operand is float && Math.Abs((float)val.operand - 0.05f) < 0.0001f) { num++; } } if (methodInfo == null || num != 1) { TerramizerServerPlugin.LogPatchMismatch("ZDO send interval", "matches=" + num); return list; } List list2 = new List(list.Count); foreach (CodeInstruction item in list) { if (item.opcode == OpCodes.Ldc_R4 && item.operand is float && Math.Abs((float)item.operand - 0.05f) < 0.0001f) { list2.Add(CopyLabelsAndBlocks(item, new CodeInstruction(OpCodes.Call, (object)methodInfo))); } else { list2.Add(item); } } return list2; } private static CodeInstruction CopyLabelsAndBlocks(CodeInstruction source, CodeInstruction target) { target.labels.AddRange(source.labels); target.blocks.AddRange(source.blocks); return target; } } [HarmonyPatch(typeof(ZDOMan), "CreateSyncList")] internal static class ZdoManCreateSyncListPatch { private static void Postfix(object __0, List __1) { TerramizerServerPlugin.PostProcessSyncList(__0, __1); } } [HarmonyPatch(typeof(EnvMan), "SkipToMorning")] internal static class EnvManSkipToMorningPatch { [HarmonyPriority(0)] private static void Postfix(EnvMan __instance) { TerramizerServerPlugin.TuneSleepFastForward(__instance); } } [HarmonyPatch(typeof(Game), "SleepStop")] internal static class GameSleepStopPatch { private static IEnumerable Transpiler(IEnumerable instructions) { //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Expected O, but got Unknown List list = new List(instructions); MethodInfo methodInfo = AccessTools.Method(typeof(Game), "SavePlayerProfile", new Type[1] { typeof(bool) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(TerramizerServerPlugin), "SavePlayerProfileFromSleep", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(ZNet), "Save", new Type[3] { typeof(bool), typeof(bool), typeof(bool) }, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(TerramizerServerPlugin), "SaveWorldFromSleep", (Type[])null, (Type[])null); int num = 0; int num2 = 0; for (int i = 0; i < list.Count; i++) { num += ((methodInfo != null && CodeInstructionExtensions.Calls(list[i], methodInfo)) ? 1 : 0); num2 += ((methodInfo3 != null && CodeInstructionExtensions.Calls(list[i], methodInfo3)) ? 1 : 0); } if (methodInfo2 == null || methodInfo4 == null || num != 1 || num2 != 1) { TerramizerServerPlugin.LogPatchMismatch("sleep save policy", "playerSave=" + num + ", worldSave=" + num2); return list; } List list2 = new List(list.Count); foreach (CodeInstruction item in list) { if (methodInfo != null && methodInfo2 != null && CodeInstructionExtensions.Calls(item, methodInfo)) { CodeInstruction val = new CodeInstruction(item); val.opcode = OpCodes.Call; val.operand = methodInfo2; list2.Add(val); } else if (methodInfo3 != null && methodInfo4 != null && CodeInstructionExtensions.Calls(item, methodInfo3)) { CodeInstruction val2 = new CodeInstruction(item); val2.opcode = OpCodes.Call; val2.operand = methodInfo4; list2.Add(val2); } else { list2.Add(item); } } return list2; } } [HarmonyPatch(typeof(ZRoutedRpc), "HandleRoutedRPC")] internal static class ZRoutedRpcHandleRoutedRpcPatch { private static bool Prefix(object __0) { return !TerramizerServerPlugin.TryHandleRoutedOwnershipRpc(__0); } } [HarmonyPatch(typeof(ZRoutedRpc), "RouteRPC")] internal static class ZRoutedRpcRouteRpcPatch { private static bool Prefix(object __0) { return !TerramizerServerPlugin.TryHandleRoutedOwnershipRpc(__0); } } [HarmonyPatch(typeof(ZNetScene), "RemoveObjects")] internal static class ZNetSceneRemoveObjectsSafetyPatch { private static void Prefix(ZNetScene __instance) { TerramizerServerPlugin.RepairStaleSceneInstances(__instance); } } [HarmonyPatch(typeof(ZNetScene), "CreateDestroyObjects")] internal static class ZNetSceneCreateDestroyObjectsPatch { private static bool Prefix(ZNetScene __instance) { return TerramizerServerPlugin.RunCreateDestroyObjects(__instance); } } [HarmonyPatch(typeof(ZoneSystem), "Update")] internal static class ZoneSystemUpdatePeerZoneCreationPatch { private static void Postfix(ZoneSystem __instance) { TerramizerServerPlugin.RunPeerZoneCreation(__instance); } } [HarmonyPatch(typeof(ZNetScene), "OutsideActiveArea", new Type[] { typeof(Vector3) })] internal static class ZNetSceneOutsideActiveAreaPeerPatch { private static bool Prefix(Vector3 point, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return TerramizerServerPlugin.TryMultiPeerOutsideActiveArea(point, ref __result); } } [HarmonyPatch(typeof(ZDOMan), "ReleaseNearbyZDOS")] internal static class ZdoManReleaseNearbyZdosServerOwnershipPatch { private static void Postfix(ZDOMan __instance, Vector3 __0) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) TerramizerServerPlugin.RunServerOwnershipForPersistentZdos(__instance, __0); } } [HarmonyPatch(typeof(ZDOMan), "ServerSortSendZDOS")] internal static class ZDOManServerSortSendZdosPatch { private static void Postfix(List __0, Vector3 __1) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) TerramizerServerPlugin.SortZdosForServer(__0, __1); } } [HarmonyPatch(typeof(AudioMan), "Update")] internal static class AudioManUpdateHeadlessPatch { private static bool Prefix() { return !TerramizerServerPlugin.ShouldSkipHeadlessVisualSystems(); } } [HarmonyPatch(typeof(ShieldDomeImageEffect), "Awake")] internal static class ShieldDomeImageEffectAwakeHeadlessPatch { private static bool Prefix(ShieldDomeImageEffect __instance) { if (!TerramizerServerPlugin.ShouldSkipHeadlessVisualSystems()) { return true; } ((Behaviour)__instance).enabled = false; return false; } } [HarmonyPatch(typeof(ShieldDomeImageEffect), "GetDomeColor")] internal static class ShieldDomeImageEffectGetDomeColorHeadlessPatch { private static bool Prefix(ref Color __result) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!TerramizerServerPlugin.ShouldSkipHeadlessVisualSystems()) { return true; } __result = Color.white; return false; } }