Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FreshWorld v1.0.6
FreshWorld.dll
Decompiled 18 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Xml; using BepInEx; using BepInEx.Configuration; using FreshWorld.Backend; using FreshWorld.Commands; using FreshWorld.Configuration; using FreshWorld.Core; using FreshWorld.Engine; using FreshWorld.Runtime; using HarmonyLib; using Microsoft.CodeAnalysis; using SoftReferenceableAssets; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("FreshWorld")] [assembly: AssemblyProduct("FreshWorld")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyFileVersion("1.0.6")] [assembly: AssemblyInformationalVersion("1.0.6")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.0.6.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FreshWorld { [BepInPlugin("sighsorry.FreshWorld", "FreshWorld", "1.0.6")] public sealed class FreshWorldPlugin : BaseUnityPlugin { private sealed class ManualRequest { internal readonly ScheduledRun Run; internal readonly CommandRequestContext Context; internal readonly RunOptions Options; internal string? LastWait; internal ManualRequest(ScheduledRun run, CommandRequestContext context, RunOptions options) { Run = run; Context = context; Options = options; } } public const string Author = "sighsorry"; public const string ModName = "FreshWorld"; public const string ModVersion = "1.0.6"; public const string ModGUID = "sighsorry.FreshWorld"; public const string PluginGuid = "sighsorry.FreshWorld"; public const string PluginName = "FreshWorld"; public const string PluginVersion = "1.0.6"; private const float SchedulePollIntervalSeconds = 1f; internal static FreshWorldPlugin? Instance; private FreshWorldConfig configuration; private RuntimeSettings? settings; private Harmony? harmony; private FileSystemWatcher? watcher; private int reloadRequested; private bool supported; private bool sessionFaulted; private ZNet? host; private long? worldUid; private FreshWorldScheduler? scheduler; private GuardedCoroutine? runner; private IDisposable? backendLease; private ScheduleClock lastClock; private float attachedAt; private float nextPoll; private float nextCheckpoint; private ManualRequest? pendingManual; private string? activeRunId; private string progress = "Idle."; private void Awake() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown Instance = this; try { configuration = new FreshWorldConfig(((BaseUnityPlugin)this).Config); harmony = new Harmony("sighsorry.FreshWorld"); harmony.PatchAll(typeof(FreshWorldPlugin).Assembly); ReloadSettings(reloadFile: false); FreshWorldCommands.Register(HandleCommand, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); watcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); watcher.Changed += OnConfigChanged; watcher.Created += OnConfigChanged; watcher.Renamed += OnConfigChanged; watcher.EnableRaisingEvents = true; supported = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("FreshWorld ready. Maintenance runs only on the world host; configure " + ((BaseUnityPlugin)this).Config.ConfigFilePath)); } catch (Exception ex) { CleanupPlugin(); ((BaseUnityPlugin)this).Logger.LogError((object)("FreshWorld initialization failed; maintenance is disabled. " + ex)); } } private void OnConfigChanged(object sender, FileSystemEventArgs args) { Interlocked.Exchange(ref reloadRequested, 1); } private void ReloadSettings(bool reloadFile) { try { if (reloadFile) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; try { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; ((BaseUnityPlugin)this).Config.Reload(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } RuntimeSettings runtimeSettings = configuration.Capture(); try { scheduler?.Reconfigure(runtimeSettings.Schedule, ReadClock()); } catch (Exception ex) { sessionFaulted = true; CloseScheduler("The updated schedule could not be persisted."); ((BaseUnityPlugin)this).Logger.LogError((object)("Could not update the recorded schedule; maintenance is disabled for this world session. " + ex)); return; } settings = runtimeSettings; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Configuration applied: automatic={runtimeSettings.AutomaticEnabled}, schedule={runtimeSettings.Schedule.Mode}, timezone={runtimeSettings.Schedule.TimeZone.Id}, safeZones={runtimeSettings.Options.ZoneSafeZones}/{runtimeSettings.Options.VegetationSafeZones}/{runtimeSettings.Options.LocationSafeZones} (zones/resources/locations)."); nextPoll = 0f; } catch (Exception ex2) { settings = null; CloseScheduler("The host configuration is invalid."); ((BaseUnityPlugin)this).Logger.LogError((object)("Invalid FreshWorld configuration; new maintenance is disabled until the cfg is corrected. " + ex2.Message)); } } private static bool IsHost(ZNet? net) { if ((Object)(object)net != (Object)null && net.IsServer() && !net.HaveStopped) { return ZNet.World != null; } return false; } private static bool WorldReady() { if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.LocationsGenerated && ZDOMan.instance != null && (Object)(object)ZNetScene.instance != (Object)null && WorldGenerator.instance != null && (Object)(object)DungeonDB.instance != (Object)null && (Object)(object)Game.instance != (Object)null && (Object)(object)EnvMan.instance != (Object)null) { return !ZNet.m_loadError; } return false; } private ScheduleClock ReadClock() { if (!IsHost(host) || (Object)(object)EnvMan.instance == (Object)null) { return new ScheduleClock(DateTimeOffset.UtcNow, lastClock.GameDay); } long dayLengthSec = EnvMan.instance.m_dayLengthSec; if (dayLengthSec <= 0) { throw new InvalidOperationException("The game day length must be positive."); } return new ScheduleClock(DateTimeOffset.UtcNow, Math.Max(0.0, host.GetTimeSeconds() / (double)dayLengthSec)); } private void Update() { if (!supported) { return; } try { ZNet instance = ZNet.instance; long? num = (IsHost(instance) ? new long?(instance.GetWorldUID()) : ((long?)null)); if (worldUid.HasValue && (host != instance || worldUid != num || !IsHost(instance))) { StopSession(); } if (runner == null && Interlocked.Exchange(ref reloadRequested, 0) != 0) { ReloadSettings(reloadFile: true); } if (!IsHost(instance) || !WorldReady()) { runner?.Dispose(); runner = null; CancelPendingManual("The hosting world is no longer ready."); return; } AttachHost(instance); if (runner != null) { GuardedCoroutine guardedCoroutine = runner; if (!guardedCoroutine.MoveNext() && runner == guardedCoroutine) { runner = null; } } if (runner == null && Interlocked.Exchange(ref reloadRequested, 0) != 0) { ReloadSettings(reloadFile: true); } if (settings == null || sessionFaulted) { return; } lastClock = ReadClock(); EnsureScheduler(); if (pendingManual != null) { if (!IsCurrentRequest(pendingManual.Context)) { CancelPendingManual("The requester disconnected, changed session, or lost administrator authority."); } else if (runner == null) { TryDispatch(pendingManual.Run, pendingManual.Options); } } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < nextPoll)) { nextPoll = realtimeSinceStartup + 1f; ScheduledRun due = scheduler.GetDue(lastClock); if (realtimeSinceStartup >= nextCheckpoint) { scheduler.Checkpoint(lastClock); nextCheckpoint = realtimeSinceStartup + 60f; } if (runner == null && due != null) { TryDispatch(due, settings.Options); } } } catch (Exception ex) { sessionFaulted = true; runner?.Dispose(); runner = null; ((BaseUnityPlugin)this).Logger.LogError((object)("FreshWorld stopped maintenance for this world session after an error. " + ex)); CloseScheduler("The host stopped maintenance after an error."); } } private void AttachHost(ZNet current) { long worldUID = current.GetWorldUID(); if (worldUid.HasValue && (host != current || worldUid != worldUID)) { StopSession(); } if (!((Object)(object)host != (Object)null)) { host = current; worldUid = worldUID; attachedAt = Time.realtimeSinceStartup; nextCheckpoint = attachedAt + 60f; sessionFaulted = false; } } private void EnsureScheduler() { if (scheduler == null) { string text = worldUid.Value.ToString(CultureInfo.InvariantCulture); scheduler = FreshWorldScheduler.Open(settings.Schedule, new JsonWorldStateStore(Path.Combine(Paths.ConfigPath, "FreshWorld", "state")), text, lastClock); ScheduledRun pendingRun = scheduler.PendingRun; if (pendingRun != null && pendingRun.IsManual) { scheduler.CancelPendingManualRun(pendingRun.Id, lastClock, "The manual request belonged to an earlier host session."); ((BaseUnityPlugin)this).Logger.LogWarning((object)("Cancelled an earlier session's unstarted manual request: " + pendingRun.Id)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Scheduler attached to world " + host.GetWorldName() + " (" + text + ").")); } } private bool IsCurrentRequest(CommandRequestContext context) { if (context.IsAuthorizedNow && context.Network == ZNet.instance && IsHost(context.Network)) { return context.Network.GetWorldUID() == context.WorldUid; } return false; } internal void HandleCommand(FreshWorldCommandAction action, CommandRequestContext context) { try { if (!supported || !IsCurrentRequest(context)) { context.Reply("Request rejected: this host session or your authority is no longer valid."); return; } if (!Enum.IsDefined(typeof(FreshWorldCommandAction), action)) { context.Reply("Invalid command action."); return; } if (!WorldReady()) { context.Reply("Request rejected: the world is not ready."); return; } AttachHost(context.Network); if (action == FreshWorldCommandAction.Status) { context.Reply(StatusMessage()); return; } if (runner != null || activeRunId != null) { context.Reply("Maintenance is already running: " + activeRunId + ". " + progress); return; } if (pendingManual != null) { if (IsCurrentRequest(pendingManual.Context)) { context.Reply("A manual request is already pending: " + pendingManual.Run.Id); return; } CancelPendingManual("The earlier request lost its authority or session."); } Interlocked.Exchange(ref reloadRequested, 0); ReloadSettings(reloadFile: true); if (settings == null) { context.Reply("Request rejected: the host cfg is invalid; see the host log."); return; } if (sessionFaulted) { context.Reply("Request rejected: maintenance is faulted for this world session; see the host log."); return; } if (!IsCurrentRequest(context)) { context.Reply("Request rejected: authority changed before admission."); return; } lastClock = ReadClock(); EnsureScheduler(); ScheduledRun due = scheduler.GetDue(lastClock); if (due != null) { context.Reply("A scheduled maintenance is already pending: " + due.Id); return; } ScheduledRun scheduledRun = scheduler.RequestManualRun(lastClock); pendingManual = new ManualRequest(scheduledRun, context, settings.Options); context.Reply("Accepted " + scheduledRun.Id + ". Host cfg: zones=" + settings.Options.ZonesEnabled + ", vegetation=" + settings.Options.VegetationEnabled + ", locations=" + settings.Options.LocationsEnabled + ", safeZones=" + settings.Options.ZoneSafeZones + "/" + settings.Options.VegetationSafeZones + "/" + settings.Options.LocationSafeZones + " (zones/resources/locations)."); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Manual request accepted from " + context.Actor + ": " + scheduledRun.Id)); TryDispatch(scheduledRun, pendingManual.Options); nextPoll = 0f; } catch (Exception ex) { sessionFaulted = true; context.Reply("Request failed; see the host log. New maintenance is disabled for this session."); ((BaseUnityPlugin)this).Logger.LogError((object)("FreshWorld command admission failed: " + ex)); CloseScheduler("Command admission failed."); } } private string StatusMessage() { if (sessionFaulted) { return "Maintenance is faulted for this world session. " + progress; } if (runner != null) { return "Running " + activeRunId + ": " + progress; } if (pendingManual != null) { return "Pending " + pendingManual.Run.Id + ": " + (pendingManual.LastWait ?? "waiting to start"); } if (settings == null) { return "The host cfg is invalid; new maintenance is disabled."; } ScheduledRun scheduledRun = scheduler?.PendingRun; if (scheduledRun != null) { return "Scheduled maintenance pending: " + scheduledRun.Id; } RunRecord runRecord = scheduler?.Attempts.LastOrDefault(); return string.Concat("Automatic=" + settings.AutomaticEnabled + "; manual commands available; Mode=" + settings.Schedule.Mode.ToString() + "; zones=" + settings.Options.ZonesEnabled + ", resources=" + settings.Options.VegetationEnabled + ", locations=" + settings.Options.LocationsEnabled + "; safeZones=" + settings.Options.ZoneSafeZones + "/" + settings.Options.VegetationSafeZones + "/" + settings.Options.LocationSafeZones + " (zones/resources/locations). ", (runRecord == null) ? "Idle. No maintenance has run in this world's recorded history." : ("Idle. Last run: " + runRecord.Status.ToString() + " (" + runRecord.Run.Id + ").")); } private void TryDispatch(ScheduledRun run, RunOptions options) { if (runner != null || settings == null || sessionFaulted || (!run.IsManual && !settings.AutomaticEnabled)) { return; } ManualRequest manualRequest = pendingManual; if (run.IsManual) { if (manualRequest == null || manualRequest.Run.Id != run.Id || !IsCurrentRequest(manualRequest.Context)) { if (manualRequest != null && manualRequest.Run.Id == run.Id) { CancelPendingManual("The requester is no longer authorized in this session."); } else { scheduler.CancelPendingManualRun(run.Id, lastClock, "No authenticated request exists in this session."); } return; } options = manualRequest.Options; } string text = null; if (!WorldReady() || !IsHost(host)) { text = "world readiness"; } else if (Time.realtimeSinceStartup - attachedAt < settings.StartupDelaySeconds) { text = "startup delay"; } else if (Time.timeScale <= 0f) { text = "the host to unpause"; } else if (host.IsSaving()) { text = "the current world save"; } else if (!MaintenanceGate.IsAvailable) { text = "another maintenance operation"; } if (text != null) { if (manualRequest != null && manualRequest.LastWait != text) { manualRequest.LastWait = text; manualRequest.Context.Reply("Waiting for " + text + "."); } } else { StartRun(run, options); } } private void StartRun(ScheduledRun run, RunOptions options) { FreshWorldScheduler activeScheduler = scheduler; ZNet activeHost = host; long? activeWorldUid = worldUid; CommandRequestContext requester = ((pendingManual?.Run.Id == run.Id) ? pendingManual.Context : null); backendLease = MaintenanceGate.Acquire(); try { activeScheduler.BeginRun(run, lastClock); pendingManual = null; activeRunId = run.Id; progress = "Starting."; Exception failure = null; MaintenancePipeline maintenancePipeline = new MaintenancePipeline(options, run.IncludeVegetation, delegate(string message) { progress = message; ((BaseUnityPlugin)this).Logger.LogInfo((object)message); requester?.Reply(message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); requester?.Reply(message); }); runner = new GuardedCoroutine(maintenancePipeline.Run(), CanContinue, delegate(Exception error) { failure = failure ?? error; ((BaseUnityPlugin)this).Logger.LogError((object)("Maintenance error: " + error)); }, delegate(bool success) { try { if (success) { activeScheduler.CompleteRun(run.Id, ReadClock()); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Maintenance completed: " + run.Id)); progress = "Completed."; requester?.Reply("Completed " + run.Id + ". World changes use the game's normal saves; no extra save was requested."); } else { activeScheduler.FailRun(run.Id, ReadClock(), failure?.Message ?? "World session ended or maintenance was cancelled."); ((BaseUnityPlugin)this).Logger.LogWarning((object)("Maintenance stopped; completed world changes remain. This run will not retry automatically: " + run.Id)); progress = "Stopped: " + (failure?.Message ?? "world session ended"); requester?.Reply("Failed or interrupted " + run.Id + ": " + (failure?.Message ?? "world session ended") + ". Applied changes remain; this run will not retry automatically."); } } catch (Exception ex) { sessionFaulted = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Could not persist the maintenance result; further runs disabled for this session. " + ex)); requester?.Reply("Maintenance ended, but its result could not be recorded. Further runs are disabled; see the host log."); } finally { backendLease?.Dispose(); backendLease = null; activeRunId = null; } }); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Maintenance starting: " + run.Id + "; vegetation=" + run.IncludeVegetation)); requester?.Reply("Starting " + run.Id + "."); bool CanContinue() { if (requester != null && !IsCurrentRequest(requester)) { if (failure == null) { failure = new InvalidOperationException("The requester's connection, session, or authority is no longer valid."); } return false; } if (ZNet.instance == activeHost && IsHost(activeHost) && activeHost.GetWorldUID() == activeWorldUid) { return WorldReady(); } return false; } } catch { backendLease?.Dispose(); backendLease = null; activeRunId = null; requester?.Reply("Maintenance could not start; see the host log."); throw; } } private void CancelPendingManual(string reason) { ManualRequest manualRequest = pendingManual; if (manualRequest != null) { pendingManual = null; try { scheduler?.CancelPendingManualRun(manualRequest.Run.Id, ReadClock(), reason); } catch (Exception ex) { sessionFaulted = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Could not persist cancellation of the manual request: " + ex)); } manualRequest.Context.Reply("Cancelled " + manualRequest.Run.Id + " before execution: " + reason); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Manual request cancelled before execution: " + manualRequest.Run.Id + "; " + reason)); } } private void CloseScheduler(string reason = "The scheduler was closed.") { CancelPendingManual(reason); FreshWorldScheduler freshWorldScheduler = scheduler; scheduler = null; if (freshWorldScheduler == null) { return; } try { freshWorldScheduler.Dispose(); } catch (Exception ex) { sessionFaulted = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to close world state. " + ex)); } } internal void StopSession() { runner?.Dispose(); runner = null; backendLease?.Dispose(); backendLease = null; CloseScheduler("The hosting world session ended."); host = null; worldUid = null; nextPoll = 0f; sessionFaulted = false; activeRunId = null; progress = "Idle."; } private void OnDestroy() { CleanupPlugin(); } private void CleanupPlugin() { supported = false; try { TryCleanup(DisposeWatcher, "Could not stop configuration watching."); TryCleanup(StopSession, "Could not stop the active world session."); TryCleanup(FreshWorldCommands.Unregister, "Could not unregister the console command."); TryCleanup(delegate { Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } }, "Could not remove Harmony patches."); } finally { Instance = null; } } private void DisposeWatcher() { FileSystemWatcher fileSystemWatcher = watcher; watcher = null; if (fileSystemWatcher != null) { fileSystemWatcher.Changed -= OnConfigChanged; fileSystemWatcher.Created -= OnConfigChanged; fileSystemWatcher.Renamed -= OnConfigChanged; fileSystemWatcher.Dispose(); } } private void TryCleanup(Action cleanup, string message) { try { cleanup(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)(message + " " + ex)); } } } [HarmonyPatch(typeof(ZNet), "Shutdown", new Type[] { typeof(bool) })] internal static class ShutdownPatch { [HarmonyPrefix] private static void Prefix() { FreshWorldPlugin.Instance?.StopSession(); } } [HarmonyPatch(typeof(ZNet), "ShutdownWithoutSave", new Type[] { typeof(bool) })] internal static class ShutdownWithoutSavePatch { [HarmonyPrefix] private static void Prefix() { FreshWorldPlugin.Instance?.StopSession(); } } } namespace FreshWorld.Core { public sealed class FreshWorldScheduler : IDisposable { private readonly object gate = new object(); private ScheduleSettings settings; private readonly JsonWorldStateStore.Lease lease; private WorldState state; private bool disposed; private bool faulted; public string WorldId => state.WorldId; public IReadOnlyList<RunRecord> Attempts { get { lock (gate) { return state.Attempts.Select((RunRecord a) => a.Copy()).ToArray(); } } } public RunRecord? ActiveRun { get { lock (gate) { return state.Attempts.FirstOrDefault((RunRecord a) => a.Status == RunStatus.Running)?.Copy(); } } } public ScheduledRun? PendingRun { get { lock (gate) { EnsureUsable(); return state.Pending; } } } private FreshWorldScheduler(ScheduleSettings settings, JsonWorldStateStore.Lease lease, WorldState state) { this.settings = settings; this.lease = lease; this.state = state; } public static FreshWorldScheduler Open(ScheduleSettings settings, JsonWorldStateStore store, string worldId, ScheduleClock now) { if (settings == null) { throw new ArgumentNullException("settings"); } if (store == null) { throw new ArgumentNullException("store"); } JsonWorldStateStore.Lease lease = store.Acquire(worldId); try { WorldState worldState = lease.Read(worldId); if (worldState == null) { worldState = NewState(settings, worldId, now); } else { foreach (RunRecord item in worldState.Attempts.Where((RunRecord a) => a.Status == RunStatus.Running)) { item.Finish(RunStatus.Interrupted, now.UtcNow, "The previous host session ended before completion. This run will not be retried automatically."); } if (worldState.Fingerprint != settings.Fingerprint) { ResetAutomaticSchedule(worldState, settings, now); } else if (!settings.RunMissedOnWorldStart) { Advance(worldState, now); } } if (!settings.AutomaticEnabled) { ScheduledRun? pending = worldState.Pending; if (pending == null || !pending.IsManual) { worldState.Pending = null; } } lease.Write(worldState); return new FreshWorldScheduler(settings, lease, worldState); } catch { lease.Dispose(); throw; } } public void Reconfigure(ScheduleSettings nextSettings, ScheduleClock now) { if (nextSettings == null) { throw new ArgumentNullException("nextSettings"); } lock (gate) { EnsureUsable(); WorldState worldState = state.Copy(); if (worldState.Fingerprint != nextSettings.Fingerprint) { ResetAutomaticSchedule(worldState, nextSettings, now); } if (!nextSettings.AutomaticEnabled) { ScheduledRun? pending = worldState.Pending; if (pending == null || !pending.IsManual) { worldState.Pending = null; } } if (worldState.Fingerprint != state.Fingerprint || worldState.Pending?.Id != state.Pending?.Id) { Persist(worldState); } settings = nextSettings; } } public ScheduledRun? GetDue(ScheduleClock now) { lock (gate) { EnsureUsable(); WorldState worldState = state.Copy(); ScheduledRun candidate = ((!settings.AutomaticEnabled) ? null : ((settings.Mode == ScheduleMode.DailyTimes) ? LatestDaily(worldState, now) : LatestGameDay(worldState, now))); if (!settings.AutomaticEnabled) { ScheduledRun? pending = worldState.Pending; if (pending == null || !pending.IsManual) { worldState.Pending = null; } } Advance(worldState, now); if (candidate != null) { ScheduledRun? pending2 = worldState.Pending; if ((pending2 == null || !pending2.IsManual) && !worldState.Attempts.Any((RunRecord a) => a.Run.Id == candidate.Id)) { worldState.Pending = candidate; } } if (worldState.Pending?.Id != state.Pending?.Id) { Persist(worldState); } else { state = worldState; } return state.Attempts.Any((RunRecord a) => a.Status == RunStatus.Running) ? null : state.Pending; } } public void BeginRun(ScheduledRun run, ScheduleClock now) { if (run == null) { throw new ArgumentNullException("run"); } lock (gate) { EnsureUsable(); if (state.Attempts.Any((RunRecord a) => a.Status == RunStatus.Running)) { throw new InvalidOperationException("A FreshWorld run is already active."); } if (state.Pending == null || state.Pending.Id != run.Id || state.Attempts.Any((RunRecord a) => a.Run.Id == run.Id)) { throw new InvalidOperationException("The run is no longer pending or has already been attempted."); } WorldState worldState = state.Copy(); worldState.Attempts.Add(new RunRecord(worldState.Pending, now.UtcNow)); worldState.Pending = null; while (worldState.Attempts.Count > 128) { worldState.Attempts.RemoveAt(0); } Persist(worldState); } } public ScheduledRun RequestManualRun(ScheduleClock now, bool includeVegetation = true) { lock (gate) { EnsureUsable(); if (state.Attempts.Any((RunRecord a) => a.Status == RunStatus.Running)) { throw new InvalidOperationException("A FreshWorld run is already active."); } if (state.Pending != null) { throw new InvalidOperationException("A FreshWorld run is already pending; a manual request cannot replace it."); } WorldState worldState = state.Copy(); ScheduledRun result = (worldState.Pending = new ScheduledRun("manual:" + Guid.NewGuid().ToString("N"), now.UtcNow, now.GameDay, "Manual", includeVegetation)); Persist(worldState); return result; } } public void CompleteRun(string runId, ScheduleClock now) { FinishRun(runId, now, RunStatus.Completed, ""); } public void FailRun(string runId, ScheduleClock now, string reason) { FinishRun(runId, now, RunStatus.Failed, reason); } public bool CancelPendingManualRun(string runId, ScheduleClock now, string reason) { lock (gate) { EnsureUsable(); if (state.Pending == null || state.Pending.Id != runId || !state.Pending.IsManual) { return false; } WorldState worldState = state.Copy(); RunRecord runRecord = new RunRecord(worldState.Pending, now.UtcNow); runRecord.Finish(RunStatus.Interrupted, now.UtcNow, reason); worldState.Attempts.Add(runRecord); worldState.Pending = null; while (worldState.Attempts.Count > 128) { worldState.Attempts.RemoveAt(0); } Persist(worldState); return true; } } public void Checkpoint(ScheduleClock now) { lock (gate) { EnsureUsable(); GetDue(now); Persist(state.Copy()); } } private void FinishRun(string runId, ScheduleClock now, RunStatus status, string reason) { lock (gate) { EnsureUsable(); WorldState worldState = state.Copy(); (worldState.Attempts.SingleOrDefault((RunRecord a) => a.Run.Id == runId && a.Status == RunStatus.Running) ?? throw new InvalidOperationException("No matching active FreshWorld run exists.")).Finish(status, now.UtcNow, reason); Persist(worldState); } } private ScheduledRun? LatestDaily(WorldState current, ScheduleClock now) { if (now.UtcNow.UtcDateTime.Ticks <= current.CursorUtcTicks) { return null; } DateTime date = TimeZoneInfo.ConvertTime(now.UtcNow, settings.TimeZone).Date; for (int i = 0; i < 3; i++) { DateTime dateTime = date.AddDays(-i); for (int num = settings.DailyTimes.Count - 1; num >= 0; num--) { TimeSpan timeSpan = settings.DailyTimes[num]; DateTime dateTime2 = DateTime.SpecifyKind(dateTime.Add(timeSpan), DateTimeKind.Unspecified); if (!settings.TimeZone.IsInvalidTime(dateTime2)) { TimeSpan offset = (settings.TimeZone.IsAmbiguousTime(dateTime2) ? settings.TimeZone.GetAmbiguousTimeOffsets(dateTime2).Max() : settings.TimeZone.GetUtcOffset(dateTime2)); DateTimeOffset dateTimeOffset = new DateTimeOffset(dateTime2, offset).ToUniversalTime(); if (!(dateTimeOffset > now.UtcNow)) { if (dateTimeOffset.UtcDateTime.Ticks <= current.CursorUtcTicks) { return null; } string text = ScheduleSettings.FormatTime(timeSpan); return new ScheduledRun(current.Fingerprint + ":daily:" + dateTime.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + ":" + text, dateTimeOffset, now.GameDay, text, settings.IncludeVegetation(timeSpan)); } } } } return null; } private ScheduledRun? LatestGameDay(WorldState current, ScheduleClock now) { if (now.GameDay <= current.CursorGameDay) { return null; } double num = Math.Floor((now.GameDay - current.GameDayAnchor) / settings.GameDayInterval); double num2 = Math.Floor((current.CursorGameDay - current.GameDayAnchor) / settings.GameDayInterval); if (num < 1.0 || num <= num2) { return null; } double gameDay = current.GameDayAnchor + num * settings.GameDayInterval; return new ScheduledRun(current.Fingerprint + ":days:" + current.GameDayAnchor.ToString("R", CultureInfo.InvariantCulture) + ":" + num.ToString("0", CultureInfo.InvariantCulture), now.UtcNow, gameDay, "GameDays", vegetation: true); } private void Persist(WorldState next) { try { lease.Write(next); state = next; } catch { faulted = true; throw; } } private static void Advance(WorldState target, ScheduleClock clock) { target.CursorUtcTicks = Math.Max(target.CursorUtcTicks, clock.UtcNow.UtcDateTime.Ticks); target.CursorGameDay = Math.Max(target.CursorGameDay, clock.GameDay); } private static void ResetAutomaticSchedule(WorldState target, ScheduleSettings settings, ScheduleClock now) { target.Fingerprint = settings.Fingerprint; ScheduledRun? pending = target.Pending; if (pending == null || !pending.IsManual) { target.Pending = null; } target.GameDayAnchor = now.GameDay; target.CursorGameDay = now.GameDay; target.CursorUtcTicks = now.UtcNow.UtcDateTime.Ticks; } private static WorldState NewState(ScheduleSettings settings, string worldId, ScheduleClock now) { return new WorldState { WorldId = worldId, Fingerprint = settings.Fingerprint, CursorUtcTicks = now.UtcNow.UtcDateTime.Ticks, CursorGameDay = now.GameDay, GameDayAnchor = now.GameDay }; } private void EnsureUsable() { if (disposed) { throw new ObjectDisposedException("FreshWorldScheduler"); } if (faulted) { throw new InvalidOperationException("State persistence failed; this scheduler is disabled for the session."); } } public void Dispose() { lock (gate) { if (disposed) { return; } try { if (!faulted) { lease.Write(state); } } finally { disposed = true; lease.Dispose(); } } } } public sealed class StateCorruptionException : IOException { public StateCorruptionException(string message) : base(message) { } public StateCorruptionException(string message, Exception inner) : base(message, inner) { } } public sealed class JsonWorldStateStore { internal sealed class Lease : IDisposable { private readonly string path; private readonly FileStream fileLock; private readonly DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(WorldState)); public Lease(string path, FileStream fileLock) { this.path = path; this.fileLock = fileLock; } public WorldState? Read(string worldId) { if (!File.Exists(path)) { return null; } try { using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); if (fileStream.Length > 4194304) { throw new StateCorruptionException("FreshWorld state exceeds its size limit."); } WorldState obj = (serializer.ReadObject(fileStream) as WorldState) ?? throw new StateCorruptionException("FreshWorld state is empty."); obj.Validate(worldId); return obj; } catch (StateCorruptionException) { throw; } catch (Exception ex2) when (ex2 is SerializationException || ex2 is XmlException || ex2 is ArgumentException) { throw new StateCorruptionException("FreshWorld state is corrupt. Automatic reset is disabled; a stale backup is never restored automatically.", ex2); } } public void Write(WorldState state) { state.Validate(state.WorldId); string sourceFileName = path + "." + Guid.NewGuid().ToString("N") + ".tmp"; try { using (FileStream fileStream = new FileStream(sourceFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { serializer.WriteObject(fileStream, state); fileStream.Flush(flushToDisk: true); } if (File.Exists(path)) { File.Replace(sourceFileName, path, null); } else { File.Move(sourceFileName, path); } } finally { if (File.Exists(sourceFileName)) { File.Delete(sourceFileName); } } } public void Dispose() { fileLock.Dispose(); } } private readonly string directory; public JsonWorldStateStore(string directory) { if (string.IsNullOrWhiteSpace(directory)) { throw new ArgumentException("A state directory is required.", "directory"); } this.directory = Path.GetFullPath(directory); } public string GetStatePath(string worldId) { ValidateWorldId(worldId); return Path.Combine(directory, ScheduleSettings.Hash(worldId) + ".json"); } internal Lease Acquire(string worldId) { string statePath = GetStatePath(worldId); Directory.CreateDirectory(directory); return new Lease(statePath, new FileStream(statePath + ".lock", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)); } private static void ValidateWorldId(string worldId) { if (string.IsNullOrWhiteSpace(worldId)) { throw new ArgumentException("Use the persistent world UID, not its display name.", "worldId"); } } } public enum ScheduleMode { DailyTimes, GameDays } public sealed class ScheduleSettings { private readonly HashSet<TimeSpan> vegetationTimes; public ScheduleMode Mode { get; } public bool AutomaticEnabled { get; } public IReadOnlyList<TimeSpan> DailyTimes { get; } public TimeZoneInfo TimeZone { get; } public double GameDayInterval { get; } public bool RunMissedOnWorldStart { get; } internal string Fingerprint { get; } public ScheduleSettings(ScheduleMode mode, string dailyTimesCsv, string timeZoneId, double gameDayInterval, string vegetationDailyTimesCsv = "*", bool runMissedOnWorldStart = false, bool automaticEnabled = true) { if (!Enum.IsDefined(typeof(ScheduleMode), mode)) { throw new ArgumentOutOfRangeException("mode"); } Mode = mode; AutomaticEnabled = automaticEnabled; bool flag = automaticEnabled && mode == ScheduleMode.DailyTimes; bool flag2 = automaticEnabled && mode == ScheduleMode.GameDays; if (flag2) { ValidateInterval(gameDayInterval); } GameDayInterval = (flag2 ? gameDayInterval : 24.0); RunMissedOnWorldStart = automaticEnabled && runMissedOnWorldStart; DailyTimes = Array.AsReadOnly(flag ? ParseTimes(dailyTimesCsv, allowEmpty: false) : Array.Empty<TimeSpan>()); TimeZone = (flag ? ResolveTimeZone(timeZoneId) : TimeZoneInfo.Utc); vegetationTimes = (flag ? ParseVegetationTimes(DailyTimes, vegetationDailyTimesCsv) : new HashSet<TimeSpan>()); string text = mode switch { ScheduleMode.GameDays => gameDayInterval.ToString("R", CultureInfo.InvariantCulture), ScheduleMode.DailyTimes => string.Join("|", TimeZone.Id, string.Join(",", DailyTimes.Select(FormatTime)), string.Join(",", vegetationTimes.OrderBy((TimeSpan t) => t).Select(FormatTime))), _ => "", }; Fingerprint = Hash(automaticEnabled ? ("active-v2|" + mode.ToString() + "|" + text) : "automatic-disabled-v1").Substring(0, 16); } internal bool IncludeVegetation(TimeSpan slot) { return vegetationTimes.Contains(slot); } internal static string FormatTime(TimeSpan time) { return time.ToString("hh\\:mm", CultureInfo.InvariantCulture); } internal static string Hash(string value) { using SHA256 sHA = SHA256.Create(); return string.Concat(from b in sHA.ComputeHash(Encoding.UTF8.GetBytes(value)) select b.ToString("x2", CultureInfo.InvariantCulture)); } private static void ValidateInterval(double interval) { if (double.IsNaN(interval) || double.IsInfinity(interval) || interval <= 0.0) { throw new ArgumentOutOfRangeException("interval", "Game day interval must be finite and greater than zero."); } } private static TimeZoneInfo ResolveTimeZone(string timeZoneId) { if (!string.Equals(timeZoneId, "Local", StringComparison.OrdinalIgnoreCase)) { return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); } return TimeZoneInfo.Local; } private static HashSet<TimeSpan> ParseVegetationTimes(IReadOnlyList<TimeSpan> times, string text) { if (text == null) { throw new ArgumentNullException("text"); } IReadOnlyList<TimeSpan> collection; if (!(text.Trim() == "*")) { IReadOnlyList<TimeSpan> readOnlyList = ParseTimes(text, allowEmpty: true); collection = readOnlyList; } else { collection = times; } HashSet<TimeSpan> hashSet = new HashSet<TimeSpan>(collection); if (hashSet.Any((TimeSpan t) => !times.Contains(t))) { throw new ArgumentException("Vegetation times must be a subset of daily run times, or '*'.", "text"); } return hashSet; } private static TimeSpan[] ParseTimes(string text, bool allowEmpty) { if (text == null) { throw new ArgumentNullException("text"); } if (allowEmpty && string.IsNullOrWhiteSpace(text)) { return Array.Empty<TimeSpan>(); } HashSet<TimeSpan> hashSet = new HashSet<TimeSpan>(); string[] array = text.Split(','); for (int i = 0; i < array.Length; i++) { if (!TimeSpan.TryParseExact(array[i].Trim(), "hh\\:mm", CultureInfo.InvariantCulture, out var result) || result.TotalHours >= 24.0) { throw new ArgumentException("Daily times must contain HH:mm values separated by commas.", "text"); } if (!hashSet.Add(result)) { throw new ArgumentException("Daily times cannot contain duplicates.", "text"); } } if (hashSet.Count == 0) { throw new ArgumentException("At least one daily run time is required.", "text"); } return hashSet.OrderBy((TimeSpan t) => t).ToArray(); } } public readonly struct ScheduleClock { public DateTimeOffset UtcNow { get; } public double GameDay { get; } public ScheduleClock(DateTimeOffset utcNow, double gameDay) { if (double.IsNaN(gameDay) || double.IsInfinity(gameDay) || gameDay < 0.0) { throw new ArgumentOutOfRangeException("gameDay"); } UtcNow = utcNow.ToUniversalTime(); GameDay = gameDay; } } [DataContract] public sealed class ScheduledRun { internal const string ManualSlot = "Manual"; [DataMember(Order = 2)] private long dueUtcTicks; [DataMember(Order = 1)] public string Id { get; private set; } = ""; [DataMember(Order = 3)] public double GameDay { get; private set; } [DataMember(Order = 4)] public string Slot { get; private set; } = ""; [DataMember(Order = 5)] public bool IncludeVegetation { get; private set; } public DateTimeOffset DueUtc => new DateTimeOffset(dueUtcTicks, TimeSpan.Zero); internal bool IsManual => string.Equals(Slot, "Manual", StringComparison.Ordinal); internal bool Valid { get { if (!string.IsNullOrWhiteSpace(Id) && dueUtcTicks > 0 && dueUtcTicks <= DateTime.MaxValue.Ticks && GameDay >= 0.0 && !double.IsInfinity(GameDay) && !double.IsNaN(GameDay)) { return Slot != null; } return false; } } internal ScheduledRun(string id, DateTimeOffset dueUtc, double gameDay, string slot, bool vegetation) { Id = id; dueUtcTicks = dueUtc.UtcDateTime.Ticks; GameDay = gameDay; Slot = slot; IncludeVegetation = vegetation; } } public enum RunStatus { Running, Completed, Failed, Interrupted } [DataContract] public sealed class RunRecord { [DataMember(Order = 3)] private long startedUtcTicks; [DataMember(Order = 4)] private long finishedUtcTicks; [DataMember(Order = 1)] public ScheduledRun Run { get; private set; } [DataMember(Order = 2)] public RunStatus Status { get; internal set; } [DataMember(Order = 5)] public string Message { get; internal set; } = ""; public DateTimeOffset StartedUtc => new DateTimeOffset(startedUtcTicks, TimeSpan.Zero); public DateTimeOffset? FinishedUtc { get { if (finishedUtcTicks != 0L) { return new DateTimeOffset(finishedUtcTicks, TimeSpan.Zero); } return null; } } internal bool Valid { get { if (Run != null && Run.Valid && Enum.IsDefined(typeof(RunStatus), Status) && startedUtcTicks > 0 && startedUtcTicks <= DateTime.MaxValue.Ticks && finishedUtcTicks >= 0 && finishedUtcTicks <= DateTime.MaxValue.Ticks && ((Status == RunStatus.Running) ? (finishedUtcTicks == 0) : (finishedUtcTicks > 0))) { return Message != null; } return false; } } internal RunRecord(ScheduledRun run, DateTimeOffset start) { Run = run; startedUtcTicks = start.UtcDateTime.Ticks; Status = RunStatus.Running; } internal void Finish(RunStatus status, DateTimeOffset time, string message) { Status = status; finishedUtcTicks = time.UtcDateTime.Ticks; Message = message ?? ""; } internal RunRecord Copy() { return (RunRecord)MemberwiseClone(); } } [DataContract] internal sealed class WorldState { [DataMember(Order = 1)] public int SchemaVersion = 1; [DataMember(Order = 2)] public string WorldId = ""; [DataMember(Order = 3)] public string Fingerprint = ""; [DataMember(Order = 4)] public long CursorUtcTicks; [DataMember(Order = 5)] public double CursorGameDay; [DataMember(Order = 6)] public double GameDayAnchor; [DataMember(Order = 7)] public ScheduledRun? Pending; [DataMember(Order = 8)] public List<RunRecord> Attempts = new List<RunRecord>(); public WorldState Copy() { WorldState obj = (WorldState)MemberwiseClone(); obj.Attempts = Attempts.Select((RunRecord a) => a.Copy()).ToList(); return obj; } public void Validate(string expectedWorldId) { if (SchemaVersion != 1 || WorldId != expectedWorldId || string.IsNullOrWhiteSpace(Fingerprint) || CursorUtcTicks <= 0 || CursorUtcTicks > DateTime.MaxValue.Ticks || !FiniteNonnegative(CursorGameDay) || !FiniteNonnegative(GameDayAnchor) || GameDayAnchor > CursorGameDay || Attempts == null || Attempts.Any((RunRecord a) => a == null || !a.Valid) || Attempts.Select((RunRecord a) => a.Run.Id).Distinct().Count() != Attempts.Count || Attempts.Count((RunRecord a) => a.Status == RunStatus.Running) > 1 || (Pending != null && (!Pending.Valid || Attempts.Any((RunRecord a) => a.Run.Id == Pending.Id)))) { throw new StateCorruptionException("FreshWorld state is inconsistent. Automatic reset is disabled until the state file is recovered."); } } private static bool FiniteNonnegative(double x) { if (x >= 0.0 && !double.IsNaN(x)) { return !double.IsInfinity(x); } return false; } } } namespace FreshWorld.Runtime { public sealed class GuardedCoroutine : IEnumerator, IDisposable { private readonly Stack<IEnumerator> _stack = new Stack<IEnumerator>(); private readonly Func<bool> _canContinue; private readonly Action<Exception> _onError; private readonly Action<bool> _onFinished; private bool _finished; private object? _current; public object? Current => _current; public GuardedCoroutine(IEnumerator root, Func<bool> canContinue, Action<Exception> onError, Action<bool> onFinished) { if (root == null) { throw new ArgumentNullException("root"); } _canContinue = canContinue ?? throw new ArgumentNullException("canContinue"); _onError = onError ?? throw new ArgumentNullException("onError"); _onFinished = onFinished ?? throw new ArgumentNullException("onFinished"); _stack.Push(root); } public bool MoveNext() { if (_finished) { return false; } _current = null; try { while (_stack.Count > 0) { if (!_canContinue()) { Finish(success: false); return false; } IEnumerator enumerator = _stack.Peek(); if (!enumerator.MoveNext()) { _stack.Pop(); (enumerator as IDisposable)?.Dispose(); continue; } object current = enumerator.Current; if (current is IEnumerator enumerator2) { foreach (IEnumerator item in _stack) { if (item == enumerator2) { throw new InvalidOperationException("A coroutine yielded an already active enumerator."); } } _stack.Push(enumerator2); continue; } _current = current; return true; } Finish(success: true); return false; } catch (Exception error) { Report(error); Finish(success: false); return false; } } public void Reset() { throw new NotSupportedException("Coroutines cannot be reset."); } public void Dispose() { Finish(success: false); } private void Finish(bool success) { if (_finished) { return; } _finished = true; _current = null; while (_stack.Count > 0) { IEnumerator enumerator = _stack.Pop(); try { (enumerator as IDisposable)?.Dispose(); } catch (Exception error) { success = false; Report(error); } } try { _onFinished(success); } catch (Exception error2) { Report(error2); } } private void Report(Exception error) { try { _onError(error); } catch (Exception) { } } } } namespace FreshWorld.Engine { internal static class BaseProtection { private static HashSet<int> playerObjects = new HashSet<int>(); private static HashSet<int> unconditionalObjects = new HashSet<int>(); private static HashSet<Vector2s> excluded = new HashSet<Vector2s>(); private static DateTime calculatedAt = DateTime.MinValue; private static int lastSize = -1; public static void Configure(IEnumerable<string> placedObjects, IEnumerable<string> alwaysProtectedObjects) { playerObjects = new HashSet<int>(placedObjects.Select((string id) => StringExtensionMethods.GetStableHashCode(id))); unconditionalObjects = new HashSet<int>(alwaysProtectedObjects.Select((string id) => StringExtensionMethods.GetStableHashCode(id))); InvalidateCache(); } public static HashSet<Vector2s> GetExcluded(int size) { //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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (size < 0) { throw new ArgumentOutOfRangeException("size"); } if (lastSize == size && DateTime.UtcNow - calculatedAt < TimeSpan.FromSeconds(10.0)) { return excluded; } HashSet<Vector2s> hashSet = new HashSet<Vector2s>(); if (size > 0) { int num = size - 1; foreach (ZDO item in GameWorld.AllZDOs()) { int prefab = item.GetPrefab(); if (!unconditionalObjects.Contains(prefab) && (!playerObjects.Contains(prefab) || item.GetLong(ZDOVars.s_creator, 0L) == 0L)) { continue; } Vector2s zone = ZoneSystem.GetZone(item.GetPosition()); for (int i = Math.Max(-32768, zone.x - num); i <= Math.Min(32767, zone.x + num); i++) { for (int j = Math.Max(-32768, zone.y - num); j <= Math.Min(32767, zone.y + num); j++) { hashSet.Add(new Vector2s(i, j)); } } } } excluded = hashSet; lastSize = size; calculatedAt = DateTime.UtcNow; return excluded; } public static void InvalidateCache() { calculatedAt = DateTime.MinValue; lastSize = -1; excluded = new HashSet<Vector2s>(); } } [HarmonyPatch(typeof(ZDOMan), "GetSaveClonePerChunk", new Type[] { })] internal static class EmptyChunkSavePatch { private static readonly FieldInfo FileCount = FindCounter("m_numFiles"); private static readonly FieldInfo DirtyFileCount = FindCounter("m_dirtyFiles"); private static FieldInfo FindCounter(string name) { FieldInfo field = (typeof(ZDOMan).GetNestedType("SaveData", BindingFlags.NonPublic) ?? throw new TypeLoadException("ZDOMan.SaveData")).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || field.FieldType != typeof(int)) { throw new MissingFieldException("ZDOMan.SaveData", name); } return field; } [HarmonyPostfix] internal static void Postfix(List<Tuple<ChunkIndex, List<ZDO>>> __result, ChunkSaveMapping ___m_chunkSaveMapping, HashSet<ChunkIndex>[] ___m_dirtyChunks, List<ZDO>[] ___m_objectsBySector, object ___m_saveData) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) ZNet instance = ZNet.instance; if (instance == null || !instance.IsServer()) { return; } HashSet<ChunkIndex> scheduled = new HashSet<ChunkIndex>(); HashSet<ChunkIndex> splitParents = new HashSet<ChunkIndex>(); foreach (Tuple<ChunkIndex, List<ZDO>> item in __result) { Record(item.Item1, scheduled, splitParents); } int num = 0; foreach (KeyValuePair<ChunkIndex, ChunkInfo> chunk in ___m_chunkSaveMapping.Chunks) { ChunkIndex key = chunk.Key; ChunkInfo value = chunk.Value; if (value.SaveChunk && value.m_numZDOs > 0 && IsOrdinaryChunk(key) && ___m_dirtyChunks[0].Contains(key) && !Overlaps(key, scheduled, splitParents) && IsEmpty(key, ___m_objectsBySector)) { __result.Add(Tuple.Create<ChunkIndex, List<ZDO>>(key, new List<ZDO>())); Record(key, scheduled, splitParents); num++; } } if (num != 0) { FileCount.SetValue(___m_saveData, (int)FileCount.GetValue(___m_saveData) + num); DirtyFileCount.SetValue(___m_saveData, (int)DirtyFileCount.GetValue(___m_saveData) + num); } } private static bool IsOrdinaryChunk(ChunkIndex chunk) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0033: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (!((ChunkIndex)(ref chunk)).Equals(ZoneSystem.ChunkPortal) && chunk.m_chunkSize <= 3 && (chunk.Chunk & 0xFF) < 64 && chunk.Chunk >> 8 < 64) { ChunkIndex val = ZoneSystem.ChunkIndexFromIndexAndSize(chunk, chunk.m_chunkSize); return ((ChunkIndex)(ref val)).Equals(chunk); } return false; } private static void Record(ChunkIndex chunk, HashSet<ChunkIndex> scheduled, HashSet<ChunkIndex> splitParents) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (IsOrdinaryChunk(chunk)) { scheduled.Add(chunk); for (int i = chunk.m_chunkSize + 1; i <= 3; i++) { splitParents.Add(ZoneSystem.ChunkIndexFromIndexAndSize(chunk, (byte)i)); } } } private static bool Overlaps(ChunkIndex chunk, HashSet<ChunkIndex> scheduled, HashSet<ChunkIndex> splitParents) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (scheduled.Contains(chunk) || splitParents.Contains(chunk)) { return true; } for (int i = chunk.m_chunkSize + 1; i <= 3; i++) { if (scheduled.Contains(ZoneSystem.ChunkIndexFromIndexAndSize(chunk, (byte)i))) { return true; } } return false; } private static bool IsEmpty(ChunkIndex chunk, List<ZDO>[] sectors) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) ValueTuple<int, int> zoneFromChunk = ZoneSystem.GetZoneFromChunk(chunk); int item = zoneFromChunk.Item1; int item2 = zoneFromChunk.Item2; int num = 8 << (int)chunk.m_chunkSize; for (int i = item2; i < item2 + num; i++) { for (int j = item; j < item + num; j++) { List<ZDO> list = sectors[ZoneSystem.SectorToIndex(j, i).Sector]; if (list != null && list.Count != 0) { return false; } } } return true; } } internal static class GameWorld { [HarmonyPatch(typeof(ZDOMan), "SendDestroyed")] private static class DestroyBatchPatch { private const int MaxPerPacket = 10000; private static readonly FieldRef<ZDOMan, List<ZDOID>> Pending = AccessTools.FieldRefAccess<ZDOMan, List<ZDOID>>("m_destroySendList"); [HarmonyPrefix] private static bool Prefix(ZDOMan __instance) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) List<ZDOID> list = Pending.Invoke(__instance); if (list.Count < 10000) { return true; } ZPackage val = new ZPackage(); val.Write(10000); for (int i = 0; i < 10000; i++) { val.Write(list[i]); } list.RemoveRange(0, 10000); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "DestroyZDO", new object[1] { val }); return false; } } private static readonly FieldRef<ZoneSystem, HashSet<Vector2s>> GeneratedZones = AccessTools.FieldRefAccess<ZoneSystem, HashSet<Vector2s>>("m_generatedZones"); private static readonly FieldRef<ZDOMan, Dictionary<ZDOID, ZDO>> ObjectsById = AccessTools.FieldRefAccess<ZDOMan, Dictionary<ZDOID, ZDO>>("m_objectsByID"); private static readonly FieldRef<ZNetScene, Dictionary<ZDO, ZNetView>> SceneInstances = AccessTools.FieldRefAccess<ZNetScene, Dictionary<ZDO, ZNetView>>("m_instances"); private static readonly Func<ZoneSystem, Vector2s, bool> PokeLocalZone = AccessTools.MethodDelegate<Func<ZoneSystem, Vector2s, bool>>(AccessTools.Method(typeof(ZoneSystem), "PokeLocalZone", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldInfo ZoneRoots = AccessTools.Field(typeof(ZoneSystem), "m_zones") ?? throw new MissingFieldException(typeof(ZoneSystem).FullName, "m_zones"); private static readonly FieldInfo RootObject = AccessTools.Field(ZoneRoots.FieldType.GetGenericArguments()[1], "m_root") ?? throw new MissingFieldException("ZoneSystem.ZoneData", "m_root"); private static readonly FieldInfo Heightmaps = AccessTools.Field(typeof(Heightmap), "s_heightmaps") ?? throw new MissingFieldException(typeof(Heightmap).FullName, "s_heightmaps"); private static readonly FieldInfo HeightmapBuildData = AccessTools.Field(typeof(Heightmap), "m_buildData") ?? throw new MissingFieldException(typeof(Heightmap).FullName, "m_buildData"); private static readonly int PlayerPrefab = StringExtensionMethods.GetStableHashCode("Player"); private static readonly HashSet<Vector2s> OwnedLoads = new HashSet<Vector2s>(); private static ZoneSystem? _loadWorld; public static Vector2s[] GeneratedSnapshot(HashSet<Vector2s>? candidates = null) { IEnumerable<Vector2s> source = GeneratedZones.Invoke(ZoneSystem.instance); if (candidates != null) { source = source.Where(candidates.Contains); } return source.OrderBy((Vector2s zone) => (long)zone.x * (long)zone.x + (long)zone.y * (long)zone.y).ToArray(); } public static HashSet<Vector2s> GeneratedSetSnapshot() { return new HashSet<Vector2s>(GeneratedZones.Invoke(ZoneSystem.instance)); } public static bool IsGenerated(Vector2s zone) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return GeneratedZones.Invoke(ZoneSystem.instance).Contains(zone); } public static List<ZDO> GetZDOs(Vector2s zone) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) List<ZDO> list = new List<ZDO>(); ZDOMan.instance.FindSectorObjects(zone, new SimulationDistance(0, 0, false), list, (List<ZDO>)null); list.RemoveAll((ZDO zdo) => zdo == null || !zdo.IsValid() || ZoneSystem.GetZone(zdo.GetPosition()) != zone); return list; } public static IEnumerable<ZDO> AllZDOs() { return ObjectsById.Invoke(ZDOMan.instance).Values.ToArray(); } public static void CollectPlayerZones(HashSet<Vector2s> zones) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_00ef: Unknown result type (might be due to invalid IL or missing references) if (zones == null) { throw new ArgumentNullException("zones"); } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer() || instance.HaveStopped) { return; } Player localPlayer = Player.m_localPlayer; if (!instance.IsDedicated() && (Object)(object)localPlayer != (Object)null) { TryAddPlayerZone(zones, ((Component)localPlayer).transform.position); } ZDOMan instance2 = ZDOMan.instance; Dictionary<ZDOID, ZDO> dictionary = ((instance2 == null) ? null : ObjectsById.Invoke(instance2)); foreach (ZNetPeer peer in instance.GetPeers()) { if (peer != null && !peer.m_server && peer.IsReady() && peer.m_socket != null && peer.m_socket.IsConnected() && (!(peer.m_characterID != ZDOID.None) || dictionary == null || !dictionary.TryGetValue(peer.m_characterID, out var value) || value == null || !value.IsValid() || !TryAddPlayerZone(zones, value.GetPosition()))) { TryAddPlayerZone(zones, peer.GetRefPos()); } } } private static bool TryAddPlayerZone(HashSet<Vector2s> zones, Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if (float.IsNaN(position.x) || float.IsInfinity(position.x) || float.IsNaN(position.y) || float.IsInfinity(position.y) || float.IsNaN(position.z) || float.IsInfinity(position.z)) { return false; } float num = (float)(((double)position.x + 32.0) / 64.0); float num2 = (float)(((double)position.z + 32.0) / 64.0); if (Math.Floor(num) < -32768.0 || Math.Floor(num) > 32767.0 || Math.Floor(num2) < -32768.0 || Math.Floor(num2) > 32767.0) { return false; } zones.Add(ZoneSystem.GetZone(position)); return true; } public static void RemoveZDO(ZDO zdo) { RemoveZDO(zdo, null); } private static void RemoveZDO(ZDO zdo, HashSet<ZDOID>? visited) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !zdo.IsValid() || IsPlayer(zdo) || (visited != null && !visited.Add(zdo.m_uid))) { return; } ZDOMan instance = ZDOMan.instance; zdo.SetOwner(ZDOMan.GetSessionID()); ZDOID connectionZDOID = zdo.GetConnectionZDOID((ConnectionType)3); if (connectionZDOID != ZDOID.None && ObjectsById.Invoke(instance).TryGetValue(connectionZDOID, out var value) && value != zdo) { if (visited == null) { visited = new HashSet<ZDOID> { zdo.m_uid }; } RemoveZDO(value, visited); } if (zdo.IsValid()) { ZNetScene instance2 = ZNetScene.instance; if (SceneInstances.Invoke(instance2).TryGetValue(zdo, out var value2) && (Object)(object)value2 != (Object)null) { instance2.Destroy(((Component)value2).gameObject); } else { instance.DestroyZDO(zdo); } } } private static bool IsPlayer(ZDO zdo) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if (zdo.GetPrefab() == PlayerPrefab) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && ((Character)localPlayer).GetZDOID() == zdo.m_uid) { return true; } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return false; } ZDOID uid = zdo.m_uid; foreach (PlayerInfo player in instance.GetPlayerList()) { if (player.m_characterID == uid) { return true; } } foreach (ZNetPeer peer in instance.GetPeers()) { if (peer.m_characterID == uid) { return true; } } return false; } private static void EnsureLoadWorld() { ZoneSystem instance = ZoneSystem.instance; if (instance != _loadWorld) { OwnedLoads.Clear(); _loadWorld = instance; } } public static void PokeZone(Vector2s zone) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) EnsureLoadWorld(); ZoneSystem instance = ZoneSystem.instance; if (!instance.IsZoneLoaded(zone)) { if (!TryGetRoot(zone, out GameObject _)) { OwnedLoads.Add(zone); } PokeLocalZone(instance, zone); } } public static void ReleaseZone(Vector2s zone) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) EnsureLoadWorld(); if (!OwnedLoads.Remove(zone)) { return; } List<ZDO> zDOs = GetZDOs(zone); if (zDOs.Any((ZDO zdo) => zdo != null && zdo.IsValid() && IsPlayer(zdo))) { return; } ZNetScene instance = ZNetScene.instance; Dictionary<ZDO, ZNetView> dictionary = SceneInstances.Invoke(instance); foreach (ZDO item in zDOs) { if (!dictionary.TryGetValue(item, out var value)) { continue; } if ((Object)(object)value != (Object)null) { GameObject gameObject = ((Component)value).gameObject; if (value.GetZDO() != null) { value.ResetZDO(); } Object.Destroy((Object)(object)gameObject); } dictionary.Remove(item); } RemoveRoot(zone); } public static bool TryGetRoot(Vector2s zone, out GameObject root) { //IL_0025: 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) IDictionary dictionary = (IDictionary)(ZoneRoots.GetValue(ZoneSystem.instance) ?? throw new InvalidOperationException("The zone root registry is not ready.")); if (dictionary.Contains(zone)) { object? value = RootObject.GetValue(dictionary[zone]); GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null && (Object)(object)val != (Object)null) { root = val; return true; } } root = null; return false; } public static void RemoveGeneratedZone(Vector2s zone) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) EnsureLoadWorld(); GeneratedZones.Invoke(ZoneSystem.instance).Remove(zone); OwnedLoads.Remove(zone); RemoveRoot(zone); } private static void RemoveRoot(Vector2s zone) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; IDictionary obj = (IDictionary)(ZoneRoots.GetValue(instance) ?? throw new InvalidOperationException("The zone root registry is not ready.")); if (TryGetRoot(zone, out GameObject root)) { Object.Destroy((Object)(object)root); } obj.Remove(zone); } public static void RecalculateTerrain() { Heightmap[] array = ((List<Heightmap>)(Heightmaps.GetValue(null) ?? throw new InvalidOperationException("The heightmap registry is not ready."))).ToArray(); foreach (Heightmap val in array) { if (!((Object)(object)val == (Object)null)) { HeightmapBuildData.SetValue(val, null); val.Poke(1, false); } } } } internal static class NativePlacement { private sealed class LocationTemplateState { private sealed class TemplateTransform { private readonly Transform _transform; private readonly Vector3 _position; private readonly Quaternion _rotation; private readonly Vector3 _scale; private readonly bool _active; public TemplateTransform(Transform transform) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_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) _transform = transform; _position = transform.localPosition; _rotation = transform.localRotation; _scale = transform.localScale; _active = ((Component)transform).gameObject.activeSelf; } public void RestoreTransform() { //IL_0016: 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_0038: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_transform == (Object)null)) { _transform.localPosition = _position; _transform.localRotation = _rotation; _transform.localScale = _scale; } } public void RestoreActive() { if ((Object)(object)_transform != (Object)null && ((Component)_transform).gameObject.activeSelf != _active) { ((Component)_transform).gameObject.SetActive(_active); } } } private readonly Transform _root; private readonly Vector3 _position; private readonly Quaternion _rotation; private readonly TemplateTransform[] _transforms; private LocationTemplateState(GameObject prefab) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) _root = prefab.transform; _position = _root.position; _rotation = _root.rotation; _transforms = (from transform in prefab.GetComponentsInChildren<Transform>(true) select new TemplateTransform(transform)).ToArray(); } public static LocationTemplateState? Capture(ZoneSystem zones, Vector2s zone) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (!zones.m_locationInstances.TryGetValue(zone, out var value) || value.m_location == null) { return null; } SoftReference<GameObject> prefab = value.m_location.m_prefab; if (!prefab.IsValid || !prefab.IsLoaded) { return null; } GameObject asset = prefab.Asset; if (!((Object)(object)asset != (Object)null)) { return null; } return new LocationTemplateState(asset); } public void Restore() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) TemplateTransform[] transforms = _transforms; for (int i = 0; i < transforms.Length; i++) { transforms[i].RestoreTransform(); } if ((Object)(object)_root != (Object)null) { _root.position = _position; _root.rotation = _rotation; } transforms = _transforms; for (int i = 0; i < transforms.Length; i++) { transforms[i].RestoreActive(); } } } private static readonly Type ClearAreaType = AccessTools.Inner(typeof(ZoneSystem), "ClearArea") ?? throw new TypeLoadException("ZoneSystem.ClearArea"); private static readonly Type ClearAreasType = typeof(List<>).MakeGenericType(ClearAreaType); private static readonly ConstructorInfo ClearAreaConstructor = AccessTools.Constructor(ClearAreaType, new Type[2] { typeof(Vector3), typeof(float) }, false) ?? throw new MissingMethodException("ZoneSystem.ClearArea constructor"); private static readonly MethodInfo Vegetation = AccessTools.Method(typeof(ZoneSystem), "PlaceVegetation", (Type[])null, (Type[])null) ?? throw new MissingMethodException("ZoneSystem.PlaceVegetation"); private static readonly MethodInfo Locations = AccessTools.Method(typeof(ZoneSystem), "PlaceLocations", (Type[])null, (Type[])null) ?? throw new MissingMethodException("ZoneSystem.PlaceLocations"); private static readonly Func<ZoneSystem, ZoneLocation, bool, bool> PokeLocation = AccessTools.MethodDelegate<Func<ZoneSystem, ZoneLocation, bool, bool>>(AccessTools.Method(typeof(ZoneSystem), "PokeCanSpawnLocation", (Type[])null, (Type[])null) ?? throw new MissingMethodException("ZoneSystem.PokeCanSpawnLocation"), (object)null, true); private static readonly FieldRef<ZoneSystem, List<GameObject>> Temporary = AccessTools.FieldRefAccess<ZoneSystem, List<GameObject>>("m_tempSpawnedObjects"); private static readonly FieldInfo GhostInit = AccessTools.Field(typeof(ZNetView), "m_ghostInit") ?? throw new MissingFieldException("ZNetView.m_ghostInit"); private static readonly FieldInfo RandomInitialDamage = AccessTools.Field(typeof(WearNTear), "m_randomInitialDamage") ?? throw new MissingFieldException("WearNTear.m_randomInitialDamage"); private static readonly FieldInfo CachedPrefabName = AccessTools.Field(AccessTools.Field(typeof(ZoneLocation), "m_prefab").FieldType, "m_name") ?? throw new MissingFieldException("SoftReference.m_name"); public static List<GameObject> TemporaryObjects(ZoneSystem zones) { return Temporary.Invoke(zones); } public static IList CreateClearAreas() { return (IList)Activator.CreateInstance(ClearAreasType); } public static object CreateClearArea(Vector3 center, float radius) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) return ClearAreaConstructor.Invoke(new object[2] { center, radius }); } public static bool IsValidLocationPrefab(ZoneLocation location) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (location != null) { if (!location.m_prefab.IsValid) { if (CachedPrefabName.GetValue(location.m_prefab) is string value) { return !string.IsNullOrWhiteSpace(value); } return false; } return true; } return false; } public static bool CanSpawnLocation(ZoneSystem zones, ZoneLocation location) { return PokeLocation(zones, location, arg3: true); } public static void PlaceVegetation(ZoneSystem zones, Vector2s zone, Transform parent, Heightmap heightmap, IList clearAreas, List<GameObject> objects) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) Run(delegate { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Vegetation.Invoke(zones, new object[7] { zone, ZoneSystem.GetZonePos(zone), parent, heightmap, clearAreas, (object)(SpawnMode)2, objects }); }); } public static void PlaceLocations(ZoneSystem zones, Vector2s zone, Transform parent, Heightmap heightmap, IList clearAreas, List<GameObject> objects) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) LocationTemplateState locationTemplateState = LocationTemplateState.Capture(zones, zone); try { Run(delegate { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Locations.Invoke(zones, new object[7] { zone, ZoneSystem.GetZonePos(zone), parent, heightmap, clearAreas, (object)(SpawnMode)2, objects }); }); } finally { locationTemplateState?.Restore(); } } private static void Run(Action placement) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) State state = Random.state; bool flag = (bool)GhostInit.GetValue(null); bool flag2 = (bool)RandomInitialDamage.GetValue(null); try { placement(); } catch (TargetInvocationException ex) when (ex.InnerException != null) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); throw; } finally { Random.state = state; GhostInit.SetValue(null, flag); RandomInitialDamage.SetValue(null, flag2); } } public static HashSet<string> RequireIds(HashSet<string> ids) { if (ids == null || ids.Count == 0 || ids.Any(string.IsNullOrWhiteSpace)) { throw new ArgumentException("At least one exact, nonempty prefab ID is required.", "ids"); } return new HashSet<string>(ids, StringComparer.Ordinal); } public static void DestroyCreatedObjects(List<GameObject> temporary, HashSet<GameObject> original) { GameObject[] array = temporary.Where((GameObject obj) => !original.Contains(obj)).ToArray(); foreach (GameObject val in array) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } temporary.Remove(val); } } } internal sealed class OperationParameters { public int SafeZones { get; set; } public float TerrainReset { get; set; } } internal abstract class ExecutedOperation { protected readonly Action<string> Log; protected int Failed; protected ExecutedOperation(Action<string> log) { Log = log; } public void Init() { Log(OnInit()); } public IEnumerator Execute() { OnStart(); yield return OnExecute(); OnEnd(); } protected abstract string OnInit(); protected abstract IEnumerator OnExecute(); protected virtual void OnStart() { } protected virtual void OnEnd() { } } internal abstract class ZoneOperation : ExecutedOperation { protected readonly OperationParameters Args; protected Vector2s[] ZonesToUpgrade; protected ZoneOperation(Action<string> log, OperationParameters args, HashSet<Vector2s>? candidates = null) : base(log) { Args = args; ZonesToUpgrade = GameWorld.GeneratedSnapshot(candidates); } protected override string OnInit() { HashSet<Vector2s> protectedZones = BaseProtection.GetExcluded(Args.SafeZones); ZonesToUpgrade = ZonesToUpgrade.Where((Vector2s zone) => !protectedZones.Contains(zone)).ToArray(); return GetType().Name + ": " + ZonesToUpgrade.Length + " zones selected."; } protected abstract bool ExecuteZone(Vector2s zone); protected override IEnumerator OnExecute() { throw new InvalidOperationException("A zone operation must be executed through its tracking wrapper."); } } internal class RegenerateLocations : ZoneOperation { private static readonly int ZoneControlHash = StringExtensionMethods.GetStableHashCode("_ZoneCtrl"); private static readonly int TerrainCompilerHash = StringExtensionMethods.GetStableHashCode("_TerrainCompiler"); private readonly HashSet<string> _ids; private int _reset; private bool _terrainTouched; public RegenerateLocations(Action<string> log, HashSet<string> ids, OperationParameters args, HashSet<Vector2s>? candidates = null) : base(log, args, candidates) { _ids = NativePlacement.RequireIds(ids); } protected override string OnInit() { Dictionary<Vector2s, LocationInstance> locations = ZoneSystem.instance.m_locationInstances; ZonesToUpgrade = ZonesToUpgrade.Where((Vector2s zone) => locations.TryGetValue(zone, out var value) && IsSelected(value)).ToArray(); return base.OnInit(); } private bool IsSelected(LocationInstance location) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (location.m_placed && NativePlacement.IsValidLocationPrefab(location.m_location)) { return _ids.Contains(location.m_location.m_prefab.Name); } return false; } protected override bool ExecuteZone(Vector2s zone) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if (!instance.m_locationInstances.TryGetValue(zone, out var value) || !IsSelected(value)) { return true; } if (!instance.IsZoneLoaded(zone) || !GameWorld.TryGetRoot(zone, out GameObject _) || !NativePlacement.CanSpawnLocation(instance, value.m_location)) { GameWorld.PokeZone(zone); return false; } if (ExecuteLocation(zone, value)) { _reset++; } return true; } protected unsafe virtual bool ExecuteLocation(Vector2s zone, LocationInstance location) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) if (!IsSelected(location)) { return false; } if (!GameWorld.TryGetRoot(zone, out GameObject root)) { Vector2s val = zone; throw new InvalidOperationException("Location zone became unavailable before reset: " + ((object)(*(Vector2s*)(&val))/*cast due to .constrained prefix*/).ToString()); } Heightmap componentInChildren = root.GetComponentInChildren<Heightmap>(); if ((Object)(object)componentInChildren == (Object)null) { Vector2s val = zone; throw new InvalidOperationException("Loaded location zone has no heightmap: " + ((object)(*(Vector2s*)(&val))/*cast due to .constrained prefix*/).ToString()); } ZoneSystem instance = ZoneSystem.instance; float exteriorRadius = location.m_location.m_exteriorRadius; ClearLocationObjects(zone, location.m_position, exteriorRadius); float num = ((Args.TerrainReset > 0f) ? Args.TerrainReset : exteriorRadius); if (num > 0f) { _terrainTouched = true; TerrainResetter.Execute(location.m_position, num); } location.m_placed = false; instance.m_locationInstances[zone] = location; List<GameObject> list = NativePlacement.TemporaryObjects(instance); HashSet<GameObject> original = new HashSet<GameObject>(list); bool active = TerrainResetter.Active; try { TerrainResetter.Active = num > 0f; NativePlacement.PlaceLocations(instance, zone, root.transform, componentInChildren, NativePlacement.CreateClearAreas(), list); NativePlacement.DestroyCreatedObjects(list, original); } finally { TerrainResetter.Active = active; } return true; } private static void ClearLocationObjects(Vector2s zone, Vector3 center, float radius) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) if (radius <= 0f) { return; } float num = radius * radius; foreach (ZDO zDO in GameWorld.GetZDOs(zone)) { int prefab = zDO.GetPrefab(); if (prefab != ZoneControlHash && prefab != TerrainCompilerHash) { Vector3 position = zDO.GetPosition(); float num2 = position.x - center.x; float num3 = position.z - center.z; if (position.y > 4000f || num2 * num2 + num3 * num3 < num) { GameWorld.RemoveZDO(zDO); } } } } protected override void OnEnd() { base.OnEnd(); if (_terrainTouched) { GameWorld.RecalculateTerrain(); } Log($"Location reset: {_reset} locations regenerated."); } } internal class ResetVegetation : ZoneOperation { private readonly HashSet<string> _ids; private readonly HashSet<int> _hashes; private List<ZoneVegetation> _vegetation = new List<ZoneVegetation>(); private int _removed; private int _spawned; private bool _terrainTouched; public ResetVegetation(Action<string> log, HashSet<string> ids, OperationParameters args, HashSet<Vector2s>? candidates = null) : base(log, args, candidates) { _ids = NativePlacement.RequireIds(ids); _hashes = new HashSet<int>(_ids.Select((string id) => StringExtensionMethods.GetStableHashCode(id))); foreach (string id in _ids) { string text = id + "_frac"; if ((Object)(object)ZNetScene.instance.GetPrefab(text) != (Object)null) { _hashes.Add(StringExtensionMethods.GetStableHashCode(text)); } } } protected override void OnStart() { base.OnStart(); _vegetation = ZoneSystem.instance.m_vegetation.Select(delegate(ZoneVegetation vegetation) { ZoneVegetation val = vegetation.Clone(); val.m_enable = (Object)(object)val.m_prefab != (Object)null && _ids.Contains(((Object)val.m_prefab).name); return val; }).ToList(); } protected unsafe override bool ExecuteZone(Vector2s zone) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) if (!ZoneSystem.instance.IsZoneLoaded(zone) || !GameWorld.TryGetRoot(zone, out GameObject root)) { GameWorld.PokeZone(zone); return false; } Heightmap componentInChildren = root.GetComponentInChildren<Heightmap>(); if ((Object)(object)componentInChildren == (Object)null) { Vector2s val = zone; throw new InvalidOperationException("Loaded vegetation zone has no heightmap: " + ((object)(*(Vector2s*)(&val))/*cast due to .constrained prefix*/).ToString()); } foreach (ZDO zDO in GameWorld.GetZDOs(zone)) { if (_hashes.Contains(zDO.GetPrefab())) { GameWorld.RemoveZDO(zDO); _removed++; } } ZoneSystem instance = ZoneSystem.instance; List<ZoneVegetation> vegetation = instance.m_vegetation; bool active = TerrainResetter.Active; List<GameObject> list = NativePlacement.TemporaryObjects(instance); HashSet<GameObject> originalObjects = new HashSet<GameObject>(list); try { instance.m_vegetation = _vegetation; TerrainResetter.Active = Args.TerrainReset > 0f; NativePlacement.PlaceVegetation(instance, zone, root.transform, componentInChildren, GetClearAreas(zone), list); GameObject[] array = list.Where((GameObject obj) => !originalObjects.Contains(obj)).ToArray(); foreach (GameObject val2 in array) { if (!((Object)(object)val2 == (Object)null)) { _spawned++; if (Args.TerrainReset > 0f) { _terrainTouched = true; TerrainResetter.Execute(val2.transform.position, Args.TerrainReset); } } } NativePlacement.DestroyCreatedObjects(list, originalObjects); } finally { instance.m_vegetation = vegetation; TerrainResetter.Active = active; } return true; } private static IList GetClearAreas(Vector2s zone) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) IList list = NativePlacement.CreateClearAreas(); if (ZoneSystem.instance.m_locationInstances.TryGetValue(zone, out var value) && value.m_location != null && value.m_location.m_clearArea) { list.Add(NativePlacement.CreateClearArea(value.m_position, value.m_location.m_exteriorRadius)); } return list; } protected override void OnEnd() { base.OnEnd(); if (_terrainTouched) { GameWorld.RecalculateTerrain(); } Log($"Vegetation reset: {_removed} objects removed, {_spawned} vegetation objects created."); } } internal class ResetZones : ZoneOperation { private static readonly Action<Minimap, float> UpdateLocationPins = AccessTools.MethodDelegate<Action<Minimap, float>>(AccessTools.Method(typeof(Minimap), "UpdateLocationPins", (Type[])null, (Type[])null), (object)null, true); private readonly Dictionary<Vector2s, BorderDirection> _borders = new Dictionary<Vector2s, BorderDirection>(); private int _reset; public ResetZones(Action<string> log, OperationParameters args, HashSet<Vector2s>? candidates = null) : base(log, args, candidates) { } protected override bool ExecuteZone(Vector2s zone) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_0116: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_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) ZoneSystem instance = ZoneSystem.instance; AddBorder(zone.x, zone.y - 1, BorderDirection.North); AddBorder(zone.x - 1, zone.y, BorderDirection.East); AddBorder(zone.x, zone.y + 1, BorderDirection.South); AddBorder(zone.x + 1, zone.y, BorderDirection.West); AddBorder(zone.x + 1, zone.y - 1, BorderDirection.NorthWest); AddBorder(zone.x - 1, zone.y - 1, BorderDirection.NorthEast); AddBorder(zone.x + 1, zone.y + 1, BorderDirection.SouthWest); AddBorder(zone.x - 1, zone.y + 1, BorderDirection.SouthEast); foreach (ZDO zDO in GameWorld.GetZDOs(zone)) { if (zDO != null && zDO.IsValid() && ZoneSystem.GetZone(zDO.GetPosition()) == zone) { GameWorld.RemoveZDO(zDO); } } if (instance.m_locationInstances.TryGetValue(zone, out var value)) { value.m_placed = false; Vector3 position = value.m_position; position.y = WorldGenerator.instance.GetHeight(position.x, position.z); value.m_position = position; instance.m_locationInstances[zone] = value; } GameWorld.RemoveGeneratedZone(zone); _reset++; return true; } private void AddBorder(int x, int y, BorderDirection direction) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (x >= -32768 && x <= 32767 && y >= -32768 && y <= 32767) { Vector2s key = default(Vector2s); ((Vector2s)(ref key))..ctor(x, y); if