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 Delve v0.1.15
plugins/Delve/Delve.Core.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.Versioning; using System.Text; using ExtraPotions.Delve.Config; using ExtraPotions.Delve.Conversion; using ExtraPotions.Delve.Data; using ExtraPotions.Delve.Engine; using ExtraPotions.Delve.Regrow; using ExtraPotions.Delve.Surface; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.1.15.0")] [assembly: AssemblyInformationalVersion("0.1.15")] [assembly: AssemblyProduct("Delve.Core")] [assembly: AssemblyTitle("Delve.Core")] [assembly: AssemblyVersion("0.1.15.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 ExtraPotions.Delve { public static class DelveCore { public const string PluginGuid = "com.meadworks.delve"; public const string ProductName = "Delve"; public const string ProductVersion = "0.1.15"; public const string Family = "Runestone"; public const string TokenNamespace = "delve"; public const string TokenPrefix = "meadworks_delve_"; public const string ZdoPrefix = "delve."; public const string InstanceKey = "delve.instance"; public const string SupportedGameVersion = "1.0.17"; } } namespace ExtraPotions.Delve.Surface { public enum SurfaceScope { Resources, Nature, All } public enum SurfaceClass { None, Tree, Bush, Mineable, Pickable } public enum SurfaceSpot { Present, Base, Location, Player, Restore } public sealed class SurfaceFacts { public Vec3 Spot { get; } public bool Present { get; } public float BaseClearance { get; } public IReadOnlyList<Vec3> Built { get; set; } = Array.Empty<Vec3>(); public IReadOnlyList<Area> Bases { get; set; } = Array.Empty<Area>(); public IReadOnlyList<Area> Locations { get; set; } = Array.Empty<Area>(); public IReadOnlyList<Vec3> Players { get; set; } = Array.Empty<Vec3>(); public SurfaceFacts(Vec3 spot, bool present, float baseClearance) { if (!(baseClearance >= 0f) || float.IsInfinity(baseClearance)) { throw new ArgumentException("Expected a finite clearance of 0 or more.", "baseClearance"); } Spot = spot; Present = present; BaseClearance = baseClearance; } } public static class SurfaceRules { public const float MatchRadius = 0.75f; public const float PlayerRadius = 3f; public const float StubRadius = 1.5f; public const double LayoutShare = 0.75; public const float InteriorGap = 1000f; public static bool InScope(SurfaceScope scope, SurfaceClass kind) { if (kind == SurfaceClass.None) { return false; } switch (scope) { case SurfaceScope.Resources: if (kind != SurfaceClass.Mineable) { return kind == SurfaceClass.Pickable; } return true; case SurfaceScope.Nature: if (kind != SurfaceClass.Tree) { return kind == SurfaceClass.Bush; } return true; default: return true; } } public static SurfaceClass Classify(bool tree, bool mineable, bool pickable, bool destructible, bool excluded) { if (excluded) { return SurfaceClass.None; } if (tree) { return SurfaceClass.Tree; } if (mineable) { return SurfaceClass.Mineable; } if (pickable) { return SurfaceClass.Pickable; } if (!destructible) { return SurfaceClass.None; } return SurfaceClass.Bush; } public static SurfaceSpot Decide(SurfaceFacts facts) { if (facts == null) { throw new ArgumentNullException("facts"); } if (facts.Present) { return SurfaceSpot.Present; } if (BuiltWithin(facts.Spot, facts.BaseClearance, facts.Built) || facts.Bases.Any((Area a) => a.Contains(facts.Spot))) { return SurfaceSpot.Base; } if (facts.Locations.Any((Area a) => a.Contains(facts.Spot))) { return SurfaceSpot.Location; } if (facts.Players.Any((Vec3 p) => Vec3.Flat(p, facts.Spot) < 3f)) { return SurfaceSpot.Player; } return SurfaceSpot.Restore; } public static bool BuiltWithin(Vec3 spot, float clearance, IEnumerable<Vec3> built) { if (clearance <= 0f || built == null) { return false; } foreach (Vec3 item in built) { if (Vec3.Flat(item, spot) < clearance && Math.Abs(item.Y - spot.Y) < 1000f) { return true; } } return false; } public static bool Matches(Vec3 generated, Vec3 standing) { return Vec3.Flat(generated, standing) < 0.75f; } public static bool LayoutMatches(int standing, int matched) { if (standing > 0) { return (double)matched >= Math.Ceiling((double)standing * 0.75); } return true; } public static IReadOnlyList<(int X, int Z)> ZonesWithin(Vec3 center, float radius) { (int X, int Z) home = RegrowRules.Zone(center); int num = (int)Math.Ceiling(radius / 64f) + 1; List<(int, int)> list = new List<(int, int)>(); for (int i = -num; i <= num; i++) { for (int j = -num; j <= num; j++) { (int, int) tuple = (home.X + i, home.Z + j); if (RegrowRules.ZoneNear(tuple, center, radius)) { list.Add(tuple); } } } return (from z in list orderby Math.Abs(z.X - home.X) + Math.Abs(z.Z - home.Z), z.X, z.Z select z).ToList(); } } public sealed class SurfaceReport { public SurfaceScope Scope { get; } public bool DryRun { get; } public int Zones { get; set; } public int ZonesSkipped { get; set; } public int Restored { get; set; } public int Stubs { get; set; } public int Diverged { get; private set; } public SortedSet<string> DivergedNames { get; } = new SortedSet<string>(StringComparer.Ordinal); public Dictionary<SurfaceSpot, int> Held { get; } = new Dictionary<SurfaceSpot, int>(); public SortedDictionary<string, int> ByPrefab { get; } = new SortedDictionary<string, int>(StringComparer.Ordinal); public SurfaceReport(SurfaceScope scope, bool dryRun) { Scope = scope; DryRun = dryRun; } public void Diverge(string prefab) { Diverged++; DivergedNames.Add(prefab); } public IReadOnlyList<string> Lines() { string text = ((Scope == SurfaceScope.Resources) ? "resources" : ((Scope == SurfaceScope.Nature) ? "nature" : "resources and nature")); List<string> list = new List<string>(); list.Add("Delve surface reset (" + text + ")" + (DryRun ? ", dry run: nothing was changed" : "") + ": " + N(Restored) + (DryRun ? " would come back" : " came back") + " in " + N(Zones) + " zones" + ((Stubs > 0) ? (", " + N(Stubs) + " stumps cleared") : "") + "."); List<string> list2 = list; List<string> list3 = new List<string>(); if (Held.TryGetValue(SurfaceSpot.Base, out var value) && value > 0) { list3.Add(N(value) + " inside the base clearance"); } if (Held.TryGetValue(SurfaceSpot.Location, out var value2) && value2 > 0) { list3.Add(N(value2) + " inside locations"); } if (Held.TryGetValue(SurfaceSpot.Player, out var value3) && value3 > 0) { list3.Add(N(value3) + " next to a player"); } if (list3.Count > 0) { list2.Add("Held back: " + string.Join(", ", list3) + "."); } if (ZonesSkipped > 0) { list2.Add(N(ZonesSkipped) + " zones in range are not generated or not loaded on the server and were left out."); } if (Diverged > 0) { list2.Add(N(Diverged) + " object layouts no longer match their zone and were left as they are (" + string.Join(", ", DivergedNames) + ")."); } return list2; } private static string N(int value) { return value.ToString(CultureInfo.InvariantCulture); } } } namespace ExtraPotions.Delve.Rooms { public readonly struct RoomCounts : IEquatable<RoomCounts> { public int Min { get; } public int Max { get; } public RoomCounts(int min, int max) { Min = min; Max = max; } public bool Equals(RoomCounts other) { if (Min == other.Min) { return Max == other.Max; } return false; } public override bool Equals(object? obj) { if (obj is RoomCounts other) { return Equals(other); } return false; } public override int GetHashCode() { return Min * 1009 + Max; } public override string ToString() { return Min + "/" + Max; } } public readonly struct RoomOverride { public bool Enabled { get; } public int Min { get; } public int Max { get; } public RoomOverride(bool enabled, int min, int max) { Enabled = enabled; Min = min; Max = max; } } public sealed class RoomSettings { public bool Enabled { get; } public int Min { get; } public int Max { get; } public RoomOverride ForestCrypt { get; } public RoomOverride SunkenCrypt { get; } public RoomOverride Cave { get; } public RoomSettings(bool enabled, int min, int max, RoomOverride forestCrypt, RoomOverride sunkenCrypt, RoomOverride cave) { Enabled = enabled; Min = min; Max = max; ForestCrypt = forestCrypt; SunkenCrypt = sunkenCrypt; Cave = cave; } } public static class RoomRules { public const int MinFloor = 10; public const int MinCap = 120; public const int MaxCap = 1000; public const string ForestCrypt = "DG_ForestCrypt"; public const string SunkenCrypt = "DG_SunkenCrypt"; public const string Cave = "DG_Cave"; public static IReadOnlyList<string> ConfigGenerators { get; } = Array.AsReadOnly(new string[3] { "DG_ForestCrypt", "DG_SunkenCrypt", "DG_Cave" }); public static IReadOnlyList<string> VanillaGenerators { get; } = Array.AsReadOnly(new string[14] { "DG_AshlandRuins", "DG_Cave", "DG_DvergrBoss", "DG_DvergrTown", "DG_ForestCrypt", "DG_FortressRuins", "DG_GoblinCamp", "DG_HalfBurried_ForestCrypt", "DG_Hole", "DG_MeadowsFarm", "DG_MeadowsVillage", "DG_MorkHalla", "DG_NorthVillage", "DG_SunkenCrypt" }); public static bool Protected(string generator) { return generator?.StartsWith("DG_Hildir_", StringComparison.Ordinal) ?? false; } public static RoomCounts Clamp(int min, int max) { int num = Math.Min(120, Math.Max(10, min)); int val = Math.Min(1000, Math.Max(10, max)); return new RoomCounts(num, Math.Max(num, val)); } public static RoomCounts? Effective(string generator, RoomSettings settings, RoomsFile file) { if (settings == null) { throw new ArgumentNullException("settings"); } if (file == null) { throw new ArgumentNullException("file"); } if (!settings.Enabled || string.IsNullOrEmpty(generator) || Protected(generator)) { return null; } switch (generator) { case "DG_ForestCrypt": return Pick(settings.ForestCrypt, settings); case "DG_SunkenCrypt": return Pick(settings.SunkenCrypt, settings); case "DG_Cave": return Pick(settings.Cave, settings); default: { if (file.Overrides.TryGetValue(generator, out var value)) { return Clamp(value.Min, value.Max); } if (!VanillaGenerators.Contains(generator)) { return null; } return Clamp(settings.Min, settings.Max); } } } public static IReadOnlyList<string> Unknown(RoomsFile file, IEnumerable<string> knownGenerators) { HashSet<string> known = new HashSet<string>(knownGenerators ?? Enumerable.Empty<string>(), StringComparer.Ordinal); return file.Overrides.Keys.Where((string g) => !known.Contains(g)).OrderBy<string, string>((string g) => g, StringComparer.Ordinal).ToArray(); } private static RoomCounts Pick(RoomOverride o, RoomSettings s) { if (!o.Enabled) { return Clamp(s.Min, s.Max); } return Clamp(o.Min, o.Max); } } public sealed class RoomsFile { public static RoomsFile Empty { get; } = new RoomsFile(new Dictionary<string, RoomCounts>(), Array.Empty<string>()); public IReadOnlyDictionary<string, RoomCounts> Overrides { get; } public IReadOnlyList<string> Errors { get; } private RoomsFile(IReadOnlyDictionary<string, RoomCounts> overrides, IReadOnlyList<string> errors) { Overrides = overrides; Errors = errors; } public static RoomsFile Parse(string? text) { List<string> list = new List<string>(); Dictionary<string, RoomCounts> dictionary = new Dictionary<string, RoomCounts>(StringComparer.Ordinal); if (MiniYaml.Read(text, list).TryGetValue("overrides", out IReadOnlyList<YamlEntry> value)) { foreach (YamlEntry item in value) { if (item.Map == null || !item.Map.TryGetValue("min", out string value2) || !item.Map.TryGetValue("max", out string value3) || !int.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !int.TryParse(value3, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { list.Add("line " + item.Line + ": '" + item.Name + "' needs {min: <whole number>, max: <whole number>}"); } else if (RoomRules.Protected(item.Name)) { list.Add("line " + item.Line + ": '" + item.Name + "' is never changed (Hildir's dungeons keep their size)"); } else if (RoomRules.ConfigGenerators.Contains(item.Name)) { list.Add("line " + item.Line + ": '" + item.Name + "' is set in the Rooms section of the config; this entry is ignored"); } else { dictionary[item.Name] = new RoomCounts(result, result2); } } } return new RoomsFile(dictionary, list); } } } namespace ExtraPotions.Delve.Respawn { public sealed class RespawnOptions { public static RespawnOptions Defaults { get; } = new RespawnOptions(camps: false, 5.0, dungeons: false, 7.0); public bool Camps { get; } public double CampDays { get; } public bool Dungeons { get; } public double DungeonDays { get; } public RespawnOptions(bool camps, double campDays, bool dungeons, double dungeonDays) { Camps = camps; CampDays = campDays; Dungeons = dungeons; DungeonDays = dungeonDays; } } public enum RespawnApproach { ArmLocal, ServerReset } public enum SpawnerReset { Leave, Waiting, Blocked, Reset } public static class SpawnerRules { public const float InteriorHeight = 3000f; public static double MinutesFor(bool inDungeon, RespawnOptions options, double daySeconds) { if (options == null) { throw new ArgumentNullException("options"); } double num = daySeconds / 60.0; if (inDungeon) { if (!options.Dungeons) { return 0.0; } return Math.Max(0.0, options.DungeonDays) * num; } if (!options.Camps) { return 0.0; } return Math.Max(0.0, options.CampDays) * num; } public static float? Arm(float currentMinutes, bool hasSpawned, double minutes) { if (currentMinutes > 0f || !hasSpawned) { return null; } if (!(minutes > 0.0)) { return null; } return (float)minutes; } public static RespawnApproach Approach(bool authorityOwnsSpawner) { if (!authorityOwnsSpawner) { return RespawnApproach.ServerReset; } return RespawnApproach.ArmLocal; } public static RespawnApproach PathFor(bool loadedAndOwned, ClockKind clock) { if (!loadedAndOwned || clock != ClockKind.InGameDays) { return RespawnApproach.ServerReset; } return RespawnApproach.ArmLocal; } public static double ArmMinutes(double days, double daySeconds) { return ClockRules.Clamp(ClockKind.InGameDays, days) * daySeconds / 60.0; } public static Stamp LastAlive(long aliveTicks, double daySeconds) { return WorldClock.FromGameTicks(aliveTicks, daySeconds); } public static bool InDungeon(Vec3 position) { return position.Y > 3000f; } public static SpawnerReset ServerReset(bool oneShot, bool hasSpawned, bool creatureAlive, Stamp lastAlive, Stamp now, ClockKind clock, double delay, GuardInput guard) { GuardResult blockedBy; return ServerReset(oneShot, hasSpawned, creatureAlive, lastAlive, now, clock, delay, guard, out blockedBy); } public static SpawnerReset ServerReset(bool oneShot, bool hasSpawned, bool creatureAlive, Stamp lastAlive, Stamp now, ClockKind clock, double delay, GuardInput guard, out GuardResult blockedBy) { if (guard == null) { throw new ArgumentNullException("guard"); } blockedBy = GuardResult.Clear; if (!oneShot || !hasSpawned || creatureAlive) { return SpawnerReset.Leave; } if (!ClockRules.Due(clock, delay, lastAlive.CompletedWith(now), now)) { return SpawnerReset.Waiting; } blockedBy = Guards.Evaluate(guard); if (blockedBy != GuardResult.Clear) { return SpawnerReset.Blocked; } return SpawnerReset.Reset; } } } namespace ExtraPotions.Delve.Reset { public enum FullResetOutcome { FirstSight, Waiting, PlayerInside, Tombstone, Built, PlayerBase, Reset } public enum WipeAction { Keep, Remove } public static class FullResetRules { public readonly struct ResetCommit { public bool RemoveOld { get; } public bool RemoveCreated { get; } private ResetCommit(bool removeOld, bool removeCreated) { RemoveOld = removeOld; RemoveCreated = removeCreated; } public static ResetCommit For(bool replacementReady) { if (!replacementReady) { return new ResetCommit(removeOld: false, removeCreated: true); } return new ResetCommit(removeOld: true, removeCreated: false); } public static long[] Surviving(IReadOnlyList<long> oldIds, IReadOnlyList<long> createdIds, bool replacementReady) { if (oldIds == null) { throw new ArgumentNullException("oldIds"); } if (createdIds == null) { throw new ArgumentNullException("createdIds"); } return (For(replacementReady).RemoveCreated ? oldIds : createdIds).ToArray(); } } public const float Margin = 8f; public const bool NativeProbePassed = true; public const string Citadel = "DG_DvergrBoss"; public static bool EffectiveDryRun(bool dryRun) { return dryRun; } public static FullResetOutcome Decide(Stamp? lastVisit, Stamp now, ClockKind clock, double delay, bool regenerateNow, bool playerInside, bool tombstoneInside, bool builtInside, bool buildClearance, bool inPlayerBase) { if (!lastVisit.HasValue && !regenerateNow) { return FullResetOutcome.FirstSight; } if (!regenerateNow && !ClockRules.Due(clock, delay, lastVisit.Value.CompletedWith(now), now)) { return FullResetOutcome.Waiting; } if (playerInside) { return FullResetOutcome.PlayerInside; } if (tombstoneInside) { return FullResetOutcome.Tombstone; } if (builtInside && buildClearance) { return FullResetOutcome.Built; } if (inPlayerBase) { return FullResetOutcome.PlayerBase; } return FullResetOutcome.Reset; } public static bool RestoreQueen(string generator, bool queenInside, bool bossRoomFound) { return generator == "DG_DvergrBoss" && !queenInside && bossRoomFound; } public static bool RelockDoor(string generator, bool closeSealedDoor, bool doorFound) { return generator == "DG_DvergrBoss" && closeSealedDoor && doorFound; } public static WipeAction Wipe(bool isGenerator, bool isPlayer, bool isTombstone) { if (!(isGenerator || isPlayer || isTombstone)) { return WipeAction.Remove; } return WipeAction.Keep; } public static Box Interior(Vec3 generator, Vec3 zoneSize, Vec3 zoneCenter) { float num = zoneSize.X / 2f + 8f; float num2 = zoneSize.Y / 2f + 8f; float num3 = zoneSize.Z / 2f + 8f; if (!(num > 0f) || !(num2 > 0f) || !(num3 > 0f)) { throw new ArgumentException("Expected a positive zone size.", "zoneSize"); } return new Box(new Vec3(zoneCenter.X - num, generator.Y - num2, zoneCenter.Z - num3), new Vec3(zoneCenter.X + num, generator.Y + num2, zoneCenter.Z + num3)); } } } namespace ExtraPotions.Delve.Regrow { public readonly struct DriftSpot { public int Id { get; } public Vec3 Position { get; } public DriftSpot(int id, Vec3 position) { Id = id; Position = position; } } public readonly struct DriftObject { public int Id { get; } public Vec3 Position { get; } public bool Whole { get; } public DriftObject(int id, Vec3 position, bool whole) { Id = id; Position = position; Whole = whole; } } public readonly struct DriftMove { public int SpotId { get; } public int ObjectId { get; } public DriftMove(int spotId, int objectId) { SpotId = spotId; ObjectId = objectId; } } public sealed class DriftSettlement { public IReadOnlyList<DriftMove> Moves { get; } public IReadOnlyList<int> NewObjects { get; } public bool BlockMissingRestore { get; } public DriftSettlement(IReadOnlyList<DriftMove> moves, IReadOnlyList<int> newObjects, bool blockMissingRestore) { Moves = moves ?? throw new ArgumentNullException("moves"); NewObjects = newObjects ?? throw new ArgumentNullException("newObjects"); BlockMissingRestore = blockMissingRestore; } } public static class DriftRules { public static DriftSettlement Settle(IReadOnlyList<DriftSpot> spots, IReadOnlyList<DriftObject> objects, float radius = 6f) { if (spots == null) { throw new ArgumentNullException("spots"); } if (objects == null) { throw new ArgumentNullException("objects"); } if (!(radius > 0f) || float.IsInfinity(radius)) { throw new ArgumentException("Expected a positive drift radius.", "radius"); } List<(int, int, float)> list = new List<(int, int, float)>(); for (int i = 0; i < spots.Count; i++) { for (int j = 0; j < objects.Count; j++) { float num = Vec3.Flat(spots[i].Position, objects[j].Position); if (num < radius) { list.Add((i, j, num)); } } } list.Sort(delegate((int Spot, int Object, float Distance) a, (int Spot, int Object, float Distance) b) { int num4 = a.Distance.CompareTo(b.Distance); if (num4 != 0) { return num4; } int num5 = a.Spot.CompareTo(b.Spot); return (num5 == 0) ? a.Object.CompareTo(b.Object) : num5; }); bool[] array = new bool[spots.Count]; bool[] array2 = new bool[objects.Count]; foreach (var item in list) { if (!array[item.Item1] && !array2[item.Item2]) { array[item.Item1] = true; array2[item.Item2] = true; } } List<int> list2 = new List<int>(); for (int num2 = 0; num2 < spots.Count; num2++) { if (!array[num2]) { list2.Add(num2); } } List<int> list3 = new List<int>(); bool flag = false; for (int num3 = 0; num3 < objects.Count; num3++) { if (objects[num3].Whole) { flag = true; } if (!array2[num3]) { list3.Add(num3); } } List<DriftMove> list4 = new List<DriftMove>(); List<int> list5 = new List<int>(); if (list2.Count == 1 && list3.Count == 1) { list4.Add(new DriftMove(spots[list2[0]].Id, objects[list3[0]].Id)); } else if (list2.Count == 0) { foreach (int item2 in list3) { if (objects[item2].Whole) { list5.Add(objects[item2].Id); } } } bool blockMissingRestore = flag && list2.Count - list4.Count > 0; return new DriftSettlement(list4, list5, blockMissingRestore); } } public static class DungeonSites { public const float InteriorHeight = 3000f; public const float DoorSearch = 100f; public static int? InsideOf(Vec3 p, IReadOnlyList<Vec3> mine, IReadOnlyList<Vec3> others, float insideRadius) { if (p.Y < 3000f) { return null; } int? result = null; float num = insideRadius; for (int i = 0; i < mine.Count; i++) { float num2 = Vec3.Flat(p, mine[i]); if (num2 < num && Math.Abs(p.Y - mine[i].Y) < 500f) { result = i; num = num2; } } if (!result.HasValue) { return null; } foreach (Vec3 other in others) { if (other.Y > 3000f && Vec3.Flat(p, other) < num) { return null; } } return result; } public static int? EntranceOf(Vec3 p, IReadOnlyList<Vec3> entrances, float radius) { if (p.Y >= 3000f) { return null; } int? result = null; float num = radius; for (int i = 0; i < entrances.Count; i++) { float num2 = Vec3.Flat(p, entrances[i]); if (num2 < num) { result = i; num = num2; } } return result; } public static int? NearestDoor(Vec3 generator, IReadOnlyList<Vec3> doors) { int? result = null; float num = 100f; for (int i = 0; i < doors.Count; i++) { float num2 = Vec3.Flat(generator, doors[i]); if (num2 < num) { result = i; num = num2; } } return result; } } public sealed class WelcomeMemory { private readonly Dictionary<long, (string Site, int Stage)> told = new Dictionary<long, (string, int)>(); public IEnumerable<long> Players => told.Keys; public bool Enter(long player, string site, int stage) { if (told.TryGetValue(player, out (string, int) value) && value.Item1 == site && stage <= value.Item2) { return false; } told[player] = (site, stage); return true; } public string? SiteOf(long player) { if (!told.TryGetValue(player, out (string, int) value)) { return null; } return value.Item1; } public void Leave(long player) { told.Remove(player); } } public readonly struct PlacedRoom { public int Hash { get; } public Vec3 Position { get; } public Vec3 Euler { get; } public PlacedRoom(int hash, Vec3 position, Vec3 euler) { Hash = hash; Position = position; Euler = euler; } } public static class RoomData { public static IReadOnlyList<PlacedRoom> Parse(byte[]? data) { List<PlacedRoom> list = new List<PlacedRoom>(); if (data == null || data.Length < 4) { return list; } using BinaryReader binaryReader = new BinaryReader(new MemoryStream(data)); int num = binaryReader.ReadInt32(); if (num < 0 || data.Length < 4 + (long)num * 28L) { return new List<PlacedRoom>(); } for (int i = 0; i < num; i++) { list.Add(new PlacedRoom(binaryReader.ReadInt32(), new Vec3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()), new Vec3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()))); } return list; } public static PlacedRoom? Find(IReadOnlyList<PlacedRoom> rooms, int hash) { foreach (PlacedRoom room in rooms) { if (room.Hash == hash) { return room; } } return null; } } public enum DungeonOutcome { FirstSight, BossAlive, NoBossPlace, Waiting, NothingTaken, Occupied, Regenerate } public static class DungeonRegrowRules { public static DungeonOutcome Decide(Stamp? lastVisit, Stamp now, ClockKind clock, double delay, bool regenerateNow, bool bossKind, bool bossAlive, bool bossPlaceKnown, int missing, bool occupied) { if (!lastVisit.HasValue && !regenerateNow) { return DungeonOutcome.FirstSight; } if (bossKind && bossAlive) { return DungeonOutcome.BossAlive; } if (bossKind && !bossPlaceKnown) { return DungeonOutcome.NoBossPlace; } if (!regenerateNow && !ClockRules.Due(clock, delay, lastVisit.Value.CompletedWith(now), now)) { return DungeonOutcome.Waiting; } if (missing == 0 && !bossKind) { return DungeonOutcome.NothingTaken; } if (occupied) { return DungeonOutcome.Occupied; } return DungeonOutcome.Regenerate; } public static bool SpotClear(Vec3 spot, float clearance, IEnumerable<Vec3> playerBuilt) { return !WorldRules.BuiltNear(spot, clearance, playerBuilt); } public static int UnitsLeft(Stamp? lastVisit, Stamp now, ClockKind clock, double delay, bool inside) { if (inside || !lastVisit.HasValue) { return ClockRules.WholeUnitsLeft(ClockRules.Clamp(clock, delay)); } return ClockRules.WholeUnitsLeft(ClockRules.Remaining(clock, delay, lastVisit.Value.CompletedWith(now), now)); } } public static class KallRoom { public const string Location = "DN_Bossroom"; public const string Boss = "FrozenKing"; public const string Proxy = "LocationProxy"; public static readonly IReadOnlyList<string> Phases = Array.AsReadOnly(new string[3] { "FrozenKing", "FrozenKing_p2", "FrozenKing_p3" }); public static readonly Vec3 BossOffset = new Vec3(-50f, 5002f, 200.48f); public const float BossYaw = 180f; public const float InsideRadius = 40f; public const float EntranceRadius = 30f; public static (Vec3 Position, float Yaw) BossSpot(Vec3 location, float locationYaw) { double num = (double)locationYaw * Math.PI / 180.0; float num2 = (float)Math.Cos(num); float num3 = (float)Math.Sin(num); Vec3 bossOffset = BossOffset; Vec3 item = new Vec3(location.X + bossOffset.X * num2 + bossOffset.Z * num3, location.Y + bossOffset.Y, location.Z - bossOffset.X * num3 + bossOffset.Z * num2); float num4 = (locationYaw + 180f) % 360f; return (Position: item, Yaw: (num4 < 0f) ? (num4 + 360f) : num4); } public static bool Inside(Vec3 p, Vec3 bossSpot) { if (p.Y >= 3000f) { return Vec3.Flat(p, bossSpot) <= 40f; } return false; } public static bool AtEntrance(Vec3 p, Vec3 location) { if (p.Y < 3000f) { return Vec3.Flat(p, location) <= 30f; } return false; } public static DungeonOutcome Decide(Stamp? lastVisit, Stamp now, ClockKind clock, double delay, bool regenerateNow, bool bossAlive, bool occupied) { return DungeonRegrowRules.Decide(lastVisit, now, clock, delay, regenerateNow, bossKind: true, bossAlive, bossPlaceKnown: true, 0, occupied); } } public enum RestoreStyle { Respawn, Unpick } public sealed class KindInfo { public DelveKind Kind { get; } public string? Generator { get; } public IReadOnlyList<string> Whole { get; } public IReadOnlyList<string> DamagedSuffixes { get; } public string? Biome { get; } public bool Drifts { get; } public float EntranceRadius { get; } public float InsideRadius { get; } public RestoreStyle Style { get; } public string? RestockRegenSpotsKey { get; } public string? RestockRegenVisitKey { get; } internal HashSet<string> UnpickPrefabs { get; } internal KindInfo(DelveKind kind, string? generator, string[] whole, string[] damagedSuffixes, string? biome, bool drifts, float entranceRadius, float insideRadius, string? spotsKey, string? visitKey, string[]? unpick = null) { Kind = kind; Generator = generator; Whole = Array.AsReadOnly(whole); DamagedSuffixes = Array.AsReadOnly(damagedSuffixes); Biome = biome; Drifts = drifts; EntranceRadius = entranceRadius; InsideRadius = insideRadius; RestockRegenSpotsKey = spotsKey; RestockRegenVisitKey = visitKey; UnpickPrefabs = new HashSet<string>(unpick ?? Array.Empty<string>(), StringComparer.Ordinal); Style = ((UnpickPrefabs.Count == whole.Length && whole.Length != 0) ? RestoreStyle.Unpick : RestoreStyle.Respawn); } } public static class KindCatalog { public static class Citadel { public const string Boss = "SeekerQueen"; public const string BossRoom = "dvergr_new_bossroom_ENTRANCE02"; public const string Door = "dungeon_queen_door"; public static readonly Vec3 BossOffset = new Vec3(2f, -24f, 0f); public const float BossYaw = 270f; } private static readonly string[] None = Array.Empty<string>(); private static readonly KindInfo[] Kinds = new KindInfo[10] { new KindInfo(DelveKind.MudPiles, "DG_SunkenCrypt", new string[2] { "mudpile2", "mudpile" }, new string[1] { "_frac" }, null, drifts: false, 30f, 200f, "restockregen_mudspots", "restockregen_lastvisit"), new KindInfo(DelveKind.FrostCaveCrystals, "DG_Cave", new string[1] { "Pickable_MountainCaveCrystal" }, None, null, drifts: false, 50f, 200f, "restockregen_crystalspots", "restockregen_cavevisit"), new KindInfo(DelveKind.BlackCores, "DG_DvergrTown", new string[1] { "Pickable_BlackCoreStand" }, None, null, drifts: false, 50f, 200f, "restockregen_corespots", "restockregen_minevisit", new string[1] { "Pickable_BlackCoreStand" }), new KindInfo(DelveKind.Morkhalla, "DG_MorkHalla", new string[13] { "Morkhalla_Eye1", "Morkhalla_Eye2", "Morkhalla_Eye3", "Morkhalla_Eye4", "Morkhalla_Eye5_gemstone", "Morkhalla_Eye6_gemstone", "Morkhalla_Eye7_gemstone", "Morkhalla_Rubble1", "Morkhalla_Rubble2", "Morkhalla_Rubble3", "Morkhalla_Rubble4", "Pickable_MorkHallaTreasure", "BlackIce_Start" }, None, null, drifts: false, 50f, 200f, "restockregen_morkspots", "restockregen_morkvisit", new string[7] { "Morkhalla_Eye1", "Morkhalla_Eye2", "Morkhalla_Eye3", "Morkhalla_Eye4", "Morkhalla_Eye5_gemstone", "Morkhalla_Eye6_gemstone", "Morkhalla_Eye7_gemstone" }), new KindInfo(DelveKind.Hole, "DG_Hole", new string[3] { "elaking_trashpile", "Spawner_Hole", "Spawner_Hole_double" }, None, null, drifts: false, 40f, 150f, "restockregen_holespots", "restockregen_holevisit"), new KindInfo(DelveKind.AncientArmor, null, new string[4] { "giant_helmet1", "giant_helmet2", "giant_sword1", "giant_sword2" }, new string[1] { "_destruction" }, "Mistlands", drifts: false, 0f, 0f, null, null), new KindInfo(DelveKind.Obsidian, null, new string[1] { "MineRock_Obsidian" }, None, "Mountain", drifts: false, 0f, 0f, null, null), new KindInfo(DelveKind.Flametal, null, new string[1] { "LeviathanLava" }, None, "AshLands", drifts: true, 0f, 0f, null, null), new KindInfo(DelveKind.Ice, null, new string[2] { "IcePond_rock", "IceShore_1" }, new string[1] { "_frac" }, "DeepNorth", drifts: false, 0f, 0f, null, null), new KindInfo(DelveKind.Queen, "DG_DvergrBoss", new string[6] { "SeekerEgg", "blackmarble_creep_4x1x1", "blackmarble_creep_4x2x1", "blackmarble_creep_stair", "blackmarble_creep_slope_inverted_1x1x2", "blackmarble_creep_slope_inverted_2x2x1" }, None, null, drifts: false, 30f, 150f, "restockregen_citadelspots", "restockregen_citadelvisit") }; private static readonly Lazy<Dictionary<string, (string Whole, KindInfo Kind)>> byPrefab = new Lazy<Dictionary<string, (string, KindInfo)>>(delegate { Dictionary<string, (string, KindInfo)> dictionary = new Dictionary<string, (string, KindInfo)>(StringComparer.Ordinal); KindInfo[] kinds = Kinds; foreach (KindInfo kindInfo in kinds) { foreach (string item in kindInfo.Whole) { dictionary[item] = (item, kindInfo); foreach (string damagedSuffix in kindInfo.DamagedSuffixes) { dictionary[item + damagedSuffix] = (item, kindInfo); } } } return dictionary; }); public static IReadOnlyList<KindInfo> All { get; } = Array.AsReadOnly(Kinds); public static KindInfo Of(DelveKind kind) { return Kinds.FirstOrDefault((KindInfo k) => k.Kind == kind) ?? throw new ArgumentException(kind.ToString() + " has no regrow catalog entry.", "kind"); } public static string? WholeOf(string prefab) { if (prefab == null || !byPrefab.Value.TryGetValue(prefab, out (string, KindInfo) value)) { return null; } return value.Item1; } public static DelveKind? KindOf(string prefab) { if (prefab == null || !byPrefab.Value.TryGetValue(prefab, out (string, KindInfo) value)) { return null; } return value.Item2.Kind; } public static RestoreStyle StyleOf(string prefab) { if (prefab == null || !byPrefab.Value.TryGetValue(prefab, out (string, KindInfo) value)) { throw new ArgumentException(prefab + " is not tracked by Delve.", "prefab"); } if (!value.Item2.UnpickPrefabs.Contains(value.Item1)) { return RestoreStyle.Respawn; } return RestoreStyle.Unpick; } } public readonly struct RecordSpot { public int Prefab { get; } public Vec3 Position { get; } public Vec3 Rotation { get; } public RecordSpot(int prefab, Vec3 position, Vec3 rotation) { Prefab = prefab; Position = position; Rotation = rotation; } } public sealed class RecordFile { public IReadOnlyList<RecordSpot> Spots { get; } public IReadOnlyDictionary<(int, int), int> Visits { get; } public IReadOnlyList<string> Errors { get; } private RecordFile(IReadOnlyList<RecordSpot> spots, IReadOnlyDictionary<(int, int), int> visits, IReadOnlyList<string> errors) { Spots = spots; Visits = visits; Errors = errors; } public static RecordFile Parse(string? text) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; List<RecordSpot> list = new List<RecordSpot>(); Dictionary<(int, int), int> dictionary = new Dictionary<(int, int), int>(); List<string> list2 = new List<string>(); string[] array = (text ?? "").Replace("\r\n", "\n").Split('\n'); for (int i = 0; i < array.Length; i++) { string text2 = array[i]; if (text2.Length == 0 || text2.StartsWith("#")) { continue; } string[] array2 = text2.Split(' '); try { if (array2[0] == "S" && array2.Length == 8) { list.Add(new RecordSpot(int.Parse(array2[1], invariantCulture), new Vec3(float.Parse(array2[2], invariantCulture), float.Parse(array2[3], invariantCulture), float.Parse(array2[4], invariantCulture)), new Vec3(float.Parse(array2[5], invariantCulture), float.Parse(array2[6], invariantCulture), float.Parse(array2[7], invariantCulture)))); } else if (array2[0] == "V" && array2.Length == 4) { dictionary[(int.Parse(array2[1], invariantCulture), int.Parse(array2[2], invariantCulture))] = int.Parse(array2[3], invariantCulture); } else { list2.Add("line " + (i + 1) + ": skipped: " + text2); } } catch (FormatException) { list2.Add("line " + (i + 1) + ": skipped: " + text2); } catch (OverflowException) { list2.Add("line " + (i + 1) + ": skipped: " + text2); } } return new RecordFile(list, dictionary, list2); } public static string Write(IEnumerable<RecordSpot> spots, IReadOnlyDictionary<(int, int), int> visits) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; StringBuilder stringBuilder = new StringBuilder("# Delve regrow records. S = spot (prefab x y z rx ry rz), V = zone last visited (zone x, zone y, day).\n"); foreach (RecordSpot spot in spots) { stringBuilder.Append(string.Format(invariantCulture, "S {0} {1:R} {2:R} {3:R} {4:R} {5:R} {6:R}\n", spot.Prefab, spot.Position.X, spot.Position.Y, spot.Position.Z, spot.Rotation.X, spot.Rotation.Y, spot.Rotation.Z)); } foreach (KeyValuePair<(int, int), int> item in from v in visits orderby v.Key.Item1, v.Key.Item2 select v) { stringBuilder.Append(string.Format(invariantCulture, "V {0} {1} {2}\n", item.Key.Item1, item.Key.Item2, item.Value)); } return stringBuilder.ToString(); } } public static class DungeonSpots { public static byte[] Encode(IReadOnlyCollection<RecordSpot> spots) { if (spots == null) { throw new ArgumentNullException("spots"); } using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(1); binaryWriter.Write(spots.Count); foreach (RecordSpot spot in spots) { binaryWriter.Write(spot.Prefab); Write(binaryWriter, spot.Position); Write(binaryWriter, spot.Rotation); } binaryWriter.Flush(); return memoryStream.ToArray(); } public static IReadOnlyList<RecordSpot> Decode(byte[]? data) { List<RecordSpot> list = new List<RecordSpot>(); if (data == null || data.Length < 8) { return list; } using BinaryReader binaryReader = new BinaryReader(new MemoryStream(data)); if (binaryReader.ReadInt32() != 1) { return list; } int num = binaryReader.ReadInt32(); if (num < 0 || data.Length < 8 + (long)num * 28L) { return new List<RecordSpot>(); } for (int i = 0; i < num; i++) { list.Add(new RecordSpot(binaryReader.ReadInt32(), Read(binaryReader), Read(binaryReader))); } return list; } private static void Write(BinaryWriter w, Vec3 v) { w.Write(v.X); w.Write(v.Y); w.Write(v.Z); } private static Vec3 Read(BinaryReader r) { return new Vec3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); } } public enum SpotState { Whole, Damaged, Missing } public enum SpotOutcome { Whole, Waiting, Near, Built, Restore, Replace } public static class RegrowRules { public const float ZoneSize = 64f; public static bool IsWhole(bool wholePrefab, string? healthText, bool dead, bool anyChunkHit, bool drifts) { if (wholePrefab && string.IsNullOrEmpty(healthText) && !dead) { return !(drifts && anyChunkHit); } return false; } public static SpotOutcome Decide(SpotState state, int lastVisitDay, int today, int days, bool regenerateNow, GuardInput guard) { if (guard == null) { throw new ArgumentNullException("guard"); } if (state == SpotState.Whole) { return SpotOutcome.Whole; } if (!regenerateNow && today - lastVisitDay < Math.Max(1, days)) { return SpotOutcome.Waiting; } switch (Guards.Evaluate(new GuardInput(guard.Center, guard.PlayerRadius) { Interior = guard.Interior, Players = guard.Players, HeldByPlayer = (guard.HeldByPlayer && state == SpotState.Damaged), Clearance = guard.Clearance, Built = guard.Built, BaseGuard = false })) { case GuardResult.PlayerNear: case GuardResult.Held: return SpotOutcome.Near; case GuardResult.Built: return SpotOutcome.Built; default: if (state != SpotState.Damaged) { return SpotOutcome.Restore; } return SpotOutcome.Replace; } } public static SpotOutcome DecideAt(SpotState state, Stamp? lastVisit, Stamp now, ClockKind clock, double delay, bool regenerateNow, GuardInput guard) { if (guard == null) { throw new ArgumentNullException("guard"); } if (state == SpotState.Whole) { return SpotOutcome.Whole; } if (!regenerateNow && (!lastVisit.HasValue || !ClockRules.Due(clock, delay, lastVisit.Value.CompletedWith(now), now))) { return SpotOutcome.Waiting; } return Decide(state, 0, 1, 1, regenerateNow: true, guard); } public static bool ZoneNear((int X, int Z) zone, Vec3 p, float radius) { float num = 32f; float num2 = Math.Max(0f, Math.Abs(p.X - (float)zone.X * 64f) - num); float num3 = Math.Max(0f, Math.Abs(p.Z - (float)zone.Z * 64f) - num); return num2 * num2 + num3 * num3 <= radius * radius; } public static (int X, int Z) Zone(Vec3 p) { return (X: (int)Math.Floor((p.X + 32f) / 64f), Z: (int)Math.Floor((p.Z + 32f) / 64f)); } } public readonly struct Spot { public string Prefab { get; } public Vec3 Position { get; } public Vec3 Rotation { get; } public Spot(string prefab, Vec3 position, Vec3 rotation) { Prefab = prefab ?? throw new ArgumentNullException("prefab"); Position = position; Rotation = rotation; } } public sealed class SpotIndex { public const float DriftRadius = 6f; private readonly Dictionary<(int, int, int), Spot> spots = new Dictionary<(int, int, int), Spot>(); public int Count => spots.Count; public IEnumerable<Spot> Spots => spots.Values; public static (int, int, int) Key(Vec3 p) { return ((int)Math.Round(p.X * 10f, MidpointRounding.ToEven), (int)Math.Round(p.Y * 10f, MidpointRounding.ToEven), (int)Math.Round(p.Z * 10f, MidpointRounding.ToEven)); } public (int, int, int)? KeyAt(Vec3 p, string wholePrefab, bool drifts) { if (drifts) { foreach (KeyValuePair<(int, int, int), Spot> spot in spots) { if (spot.Value.Prefab == wholePrefab && Vec3.Flat(spot.Value.Position, p) < 6f) { return spot.Key; } } return null; } (int, int, int) tuple = Key(p); if (spots.ContainsKey(tuple)) { return tuple; } for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) { for (int k = -2; k <= 2; k++) { (int, int, int) tuple2 = (tuple.Item1 + i, tuple.Item2 + j, tuple.Item3 + k); if (spots.TryGetValue(tuple2, out var value) && Vec3.SqrDistance(value.Position, p) < 0.04f) { return tuple2; } } } } return null; } public bool Remember(Spot spot, bool drifts) { if (KeyAt(spot.Position, spot.Prefab, drifts).HasValue) { return false; } spots[Key(spot.Position)] = spot; return true; } public bool TryGet((int, int, int) key, out Spot spot) { return spots.TryGetValue(key, out spot); } } } namespace ExtraPotions.Delve.Localization { public readonly struct TextKey { public string Key { get; } public int Arguments { get; } public IReadOnlyList<string> Named { get; } public TextKey(string key, int arguments, params string[] named) { Key = key; Arguments = arguments; Named = named.OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToArray(); } } public static class DelveTextKeys { public const string DaysOne = "days_one"; public const string DaysMany = "days_many"; public const string HoursOne = "hours_one"; public const string HoursMany = "hours_many"; public const string ChestRefill = "chest_refill"; public const string ChestRefillLeftovers = "chest_refill_leftovers"; public const string ChestDue = "chest_due"; public const string HistoryMany = "history_many"; public const string HistoryOnce = "history_once"; public const string HistoryYou = "history_you"; public const string HistoryToday = "history_today"; public const string HistoryYesterday = "history_yesterday"; public const string HistoryDaysAgo = "history_days_ago"; public const string ImportDone = "import_done"; public const string ServerOnlyIdle = "server_only_idle"; public static readonly IReadOnlyList<(DelveKind Kind, string Stem)> SiteKinds = Array.AsReadOnly(new(DelveKind, string)[8] { (DelveKind.MudPiles, "mudpiles"), (DelveKind.FrostCaveCrystals, "crystals"), (DelveKind.BlackCores, "blackcores"), (DelveKind.Morkhalla, "morkhalla"), (DelveKind.Hole, "hole"), (DelveKind.Queen, "queen"), (DelveKind.FullReset, "fullreset"), (DelveKind.Kall, "kall") }); public static readonly IReadOnlyList<(DelveKind Kind, string Key)> BiomeKinds = Array.AsReadOnly(new(DelveKind, string)[4] { (DelveKind.AncientArmor, "biome_mistlands"), (DelveKind.Obsidian, "biome_mountain"), (DelveKind.Flametal, "biome_ashlands"), (DelveKind.Ice, "biome_deepnorth") }); private static readonly Lazy<TextKey[]> all = new Lazy<TextKey[]>(delegate { List<TextKey> list = new List<TextKey> { new TextKey("step_aside_rooms", 1), new TextKey("step_aside_fullreset", 1), new TextKey("step_aside_chests", 1), new TextKey("step_aside_resources", 1), new TextKey("step_aside_respawn", 1), new TextKey("days_one", 0), new TextKey("days_many", 1), new TextKey("hours_one", 0), new TextKey("hours_many", 1), new TextKey("chest_refill", 1), new TextKey("chest_refill_leftovers", 1), new TextKey("chest_due", 0), new TextKey("history_many", 0, "last", "lastago", "first", "firstago"), new TextKey("history_once", 0, "last", "lastago"), new TextKey("history_you", 0), new TextKey("history_today", 0), new TextKey("history_yesterday", 0), new TextKey("history_days_ago", 1), new TextKey("import_done", 2), new TextKey("server_only_idle", 0) }; foreach (var siteKind in SiteKinds) { string item = siteKind.Stem; list.Add(new TextKey(item + "_inside", 1)); list.Add(new TextKey(item + "_entrance", 1)); list.Add(new TextKey(item + "_due", 0)); } foreach (var biomeKind in BiomeKinds) { string item2 = biomeKind.Key; list.Add(new TextKey(item2, 1)); } foreach (GuardResult value in Enum.GetValues(typeof(GuardResult))) { if (value != GuardResult.Clear) { list.Add(new TextKey(Guard(value), 0)); } } return list.ToArray(); }); public static IReadOnlyList<TextKey> All => all.Value; public static string ToCatalog(string key, string text) { if (!IsHistoryTemplate(key)) { return text; } return text.Replace("{", "{{").Replace("}", "}}"); } public static string FromCatalog(string key, string text) { if (!IsHistoryTemplate(key)) { return text; } return string.Format(CultureInfo.InvariantCulture, text); } private static bool IsHistoryTemplate(string key) { if (!(key == "history_many")) { return key == "history_once"; } return true; } public static string Guard(GuardResult guard) { return "guard_" + guard.ToString().ToLowerInvariant(); } } public enum SiteNotice { Inside, Entrance, Due } public static class NoticeText { public static (string Key, int Count) Duration(ClockKind clock, int units) { if (clock == ClockKind.RealHours) { if (units != 1) { return (Key: "hours_many", Count: units); } return (Key: "hours_one", Count: 1); } if (units != 1) { return (Key: "days_many", Count: units); } return (Key: "days_one", Count: 1); } public static string Site(DelveKind kind, SiteNotice notice) { return ((from s in DelveTextKeys.SiteKinds where s.Kind == kind select s.Stem).FirstOrDefault() ?? throw new ArgumentException(kind.ToString() + " has no site notices.", "kind")) + "_" + notice.ToString().ToLowerInvariant(); } public static string Biome(DelveKind kind) { return (from b in DelveTextKeys.BiomeKinds where b.Kind == kind select b.Key).FirstOrDefault() ?? throw new ArgumentException(kind.ToString() + " has no biome notice.", "kind"); } } public sealed class NoticeCooldown { private readonly Dictionary<(long, string), (bool Inside, double LastTold)> state = new Dictionary<(long, string), (bool, double)>(); public bool Enter(long player, string biome, bool inside, double nowMinutes, double cooldownMinutes) { (long, string) key = (player, biome ?? ""); state.TryGetValue(key, out (bool, double) value); bool flag = state.ContainsKey(key); bool flag2 = inside && (!flag || !value.Item1) && (!flag || double.IsNegativeInfinity(value.Item2) || nowMinutes - value.Item2 >= cooldownMinutes); state[key] = (inside, flag2 ? nowMinutes : (flag ? value.Item2 : double.NegativeInfinity)); return flag2; } public void Forget(long player) { foreach (var item in state.Keys.Where<(long, string)>(((long, string) k) => k.Item1 == player).ToList()) { state.Remove(item); } } } } namespace ExtraPotions.Delve.Integration { public enum HookKind { Method, Field } public enum HookStatus { Wired, Pending } public sealed class HookTarget { public string Module { get; } public string Type { get; } public string Member { get; } public HookKind Kind { get; } public string Signature { get; } public HookStatus Status { get; } public string Purpose { get; } internal HookTarget(string module, string type, string member, HookKind kind, string signature, HookStatus status, string purpose) { Module = module; Type = type; Member = member; Kind = kind; Signature = signature; Status = status; Purpose = purpose; } public override string ToString() { return Type + "." + Member + ((Kind == HookKind.Method) ? ("(" + Signature + ")") : (" : " + Signature)); } } public static class HookTargets { public static IReadOnlyList<HookTarget> All { get; } = Array.AsReadOnly(new HookTarget[58] { M("Rooms", "DungeonGenerator", "GenerateRooms", "ZoneSystem/SpawnMode", HookStatus.Wired, "prefix sets min and max rooms, postfix restores them (A1)"), F("Rooms", "DungeonGenerator", "m_minRooms", "Int32", HookStatus.Wired, "the generator's own min rooms"), F("Rooms", "DungeonGenerator", "m_maxRooms", "Int32", HookStatus.Wired, "the generator's own max rooms"), F("Rooms", "DungeonGenerator", "m_algorithm", "DungeonGenerator/Algorithm", HookStatus.Wired, "only the Dungeon algorithm is sized"), M("FullReset", "DungeonGenerator", "Generate", "ZoneSystem/SpawnMode", HookStatus.Wired, "generate a wiped interior again in ghost mode (DV09)"), F("FullReset", "DungeonGenerator", "m_zoneSize", "Vector3", HookStatus.Wired, "the interior volume (DV09)"), M("FullReset", "ZNetView", "StartGhostInit", "", HookStatus.Wired, "ghost spawning as ZoneSystem does (DV09)"), M("FullReset", "ZNetView", "FinishGhostInit", "", HookStatus.Wired, "ghost spawning as ZoneSystem does (DV09)"), M("FullReset", "ZoneSystem", "GetZonePos", "Vector2s", HookStatus.Wired, "the interior's zone center (DV09)"), M("Surface", "ZoneSystem", "PlaceVegetation", "Vector2s,Vector3,Transform,Heightmap,List`1<ZoneSystem/ClearArea>,ZoneSystem/SpawnMode,List`1<GameObject>", HookStatus.Wired, "replay a generated zone's vegetation in ghost mode (parity 26, 27)"), M("Surface", "ZoneSystem", "IsZoneGenerated", "Vector2s", HookStatus.Wired, "only generated zones are reset (parity 26, 27)"), F("Surface", "ZoneSystem", "m_zones", "Dictionary`2<Vector2s,ZoneSystem/ZoneData>", HookStatus.Wired, "a loaded zone's own terrain (parity 26, 27)"), F("Surface", "ZoneSystem", "m_zonePrefab", "GameObject", HookStatus.Wired, "terrain for a zone no one has loaded on the server (parity 26, 27)"), F("Surface", "ZoneSystem", "m_locationInstances", "Dictionary`2<Vector2s,ZoneSystem/LocationInstance>", HookStatus.Wired, "location clear areas and exteriors (parity 26, 27)"), M("Surface", "HeightmapBuilder", "IsTerrainReady", "Vector3,Int32,Single,Boolean,WorldGenerator", HookStatus.Wired, "wait for a zone's terrain (parity 26, 27)"), F("Surface", "ZNetScene", "m_instances", "Dictionary`2<ZDO,ZNetView>", HookStatus.Wired, "loaded objects stand aside during the replay (parity 26, 27)"), F("Surface", "TreeBase", "m_stubPrefab", "GameObject", HookStatus.Wired, "clear a stump where a tree returns (parity 26, 27)"), F("Surface", "Destructible", "m_spawnWhenDestroyed", "GameObject", HookStatus.Wired, "a partly mined rock is the same rock (parity 26, 27)"), M("Commands", "ZNet", "RPC_Kick", "ZRpc,String", HookStatus.Wired, "kick fallback: delve-* words from a remote admin (DV11)"), M("Commands", "ZNet", "Kick", "String", HookStatus.Wired, "kick fallback: the host's or single player's own kick (DV11)"), M("Commands", "ZNet", "RemotePrint", "ZRpc,String", HookStatus.Wired, "reply in the admin's console (DV11)"), M("Engine", "EnvMan", "GetDay", "Double", HookStatus.Pending, "in-game day for clocks (DV03b)"), M("Engine", "ZNet", "GetTimeSeconds", "", HookStatus.Wired, "world time for fractional days and dawn (DV03b)"), F("Engine", "EnvMan", "m_dayLengthSec", "Int64", HookStatus.Wired, "day length for the world day (DV03b)"), M("Engine", "SaveSystem", "GetWorldsSaveRootPath", "FileHelpers/FileSource", HookStatus.Wired, "sidecar folder worlds_local (DV03b)"), M("Engine", "ZDOMan", "FindSectorObjects", "Vector2s,SimulationDistance,List`1<ZDO>,List`1<ZDO>", HookStatus.Wired, "objects near a site for clearance and bases (DV03b)"), F("Engine", "PrivateArea", "m_radius", "Single", HookStatus.Wired, "ward radius for the base guard (B6)"), F("Engine", "EffectArea", "m_type", "EffectArea/Type", HookStatus.Wired, "player base areas for the base guard (B6)"), F("Engine", "ZDOVars", "s_enabled", "Int32", HookStatus.Wired, "a disabled ward does not guard"), M("Engine", "ZoneSystem", "GetZone", "Vector3", HookStatus.Wired, "visit zones and nearby objects (DV06b)"), M("Engine", "ZNetScene", "InActiveArea", "Vector3,Vector3", HookStatus.Wired, "held rule (DV03b)"), M("Engine", "ZDOMan", "DestroyZDO", "ZDO", HookStatus.Wired, "remove a damaged object (DV06b)"), M("Engine", "ZDOMan", "CreateNewZDO", "Vector3,Int32", HookStatus.Wired, "create a restored object (DV06b)"), M("Chests", "ZDO", "Deserialize", "ZPackage", HookStatus.Wired, "openings and first sight (DV05b, DV06b)"), M("Chests", "Container", "SetInUse", "Boolean", HookStatus.Wired, "the host's own chest opening and closing (R-Q4)"), M("Chests", "Container", "Save", "", HookStatus.Wired, "the host's own chest emptying (R-Q4)"), F("Chests", "Container", "m_defaultItems", "DropTable", HookStatus.Wired, "a loot chest has a loot table (DV05b)"), F("Chests", "Container", "m_width", "Int32", HookStatus.Wired, "refill inventory size"), F("Chests", "Container", "m_height", "Int32", HookStatus.Wired, "refill inventory size"), M("Chests", "DropTable", "GetDropListItems", "", HookStatus.Wired, "roll the chest's own loot table"), M("Chests", "Inventory", "Save", "ZPackage", HookStatus.Wired, "write the fresh roll as the game does"), F("Chests", "ZDOVars", "s_items", "Int32", HookStatus.Wired, "chest inventory"), F("Chests", "ZDOVars", "s_inUse", "Int32", HookStatus.Wired, "chest open"), F("Chests", "ZDOVars", "s_creator", "Int32", HookStatus.Wired, "player-built objects"), F("Resources", "ZDOVars", "s_picked", "Int32", HookStatus.Wired, "unpick black cores and Morkhalla eyes"), F("Resources", "ZDOVars", "s_pickedTime", "Int32", HookStatus.Wired, "clear the picked time with the mark"), F("Resources", "ZDOVars", "s_roomData", "Int32", HookStatus.Wired, "the Queen's room from the generator's saved rooms (DV08)"), F("Resources", "ZDOVars", "s_state", "Int32", HookStatus.Wired, "close the citadel's sealed door (DV08)"), F("Resources", "ZDOVars", "s_location", "Int32", HookStatus.Wired, "find the Deep North boss room by its location proxy (Kall's return)"), F("Resources", "ZDOVars", "s_dead", "Int32", HookStatus.Wired, "a flametal spire that is leaving"), M("Resources", "WorldGenerator", "GetBiome", "Vector3", HookStatus.Wired, "biome entry notices (DV07)"), F("Respawn", "CreatureSpawner", "m_respawnTimeMinuts", "Single", HookStatus.Wired, "arm a one-shot spawner (DV15b)"), M("Respawn", "CreatureSpawner", "HasSpawned", "", HookStatus.Wired, "only spawners that spawned before"), M("Respawn", "CreatureSpawner", "Awake", "", HookStatus.Wired, "arm on load (Elite Creatures Reborn hook)"), M("Respawn", "CreatureSpawner", "Spawn", "", HookStatus.Wired, "arm after the first spawn"), M("Respawn", "ZDO", "GetConnection", "", HookStatus.Wired, "spawned state and the creature it points to (server reset)"), F("Respawn", "ZDOVars", "s_aliveTime", "Int32", HookStatus.Wired, "last time the spawner's creature lived (server reset)"), F("Respawn", "ZDOVars", "s_spawnTime", "Int32", HookStatus.Pending, "spawned state (server reset)") }); public static IEnumerable<HookTarget> Wired => All.Where((HookTarget t) => t.Status == HookStatus.Wired); private static HookTarget M(string module, string type, string member, string parameters, HookStatus status, string purpose) { return new HookTarget(module, type, member, HookKind.Method, parameters, status, purpose); } private static HookTarget F(string module, string type, string member, string fieldType, HookStatus status, string purpose) { return new HookTarget(module, type, member, HookKind.Field, fieldType, status, purpose); } } public enum DelveModule { Rooms, FullReset, Chests, Resources, Respawn } public sealed class SourceInfo { public string Guid { get; } public string Name { get; } public IReadOnlyList<DelveModule> Modules { get; } internal SourceInfo(string guid, string name, params DelveModule[] modules) { Guid = guid; Name = name; Modules = Array.AsReadOnly(modules); } } public sealed class SourceSwitches { public static SourceSwitches Unknown { get; } = new SourceSwitches(vatDungeons: false, ecrRespawn: false, ecrLoot: false); public bool AdminToolDungeonResets { get; } public bool CreatureRespawn { get; } public bool DungeonLoot { get; } private SourceSwitches(bool vatDungeons, bool ecrRespawn, bool ecrLoot) { AdminToolDungeonResets = vatDungeons; CreatureRespawn = ecrRespawn; DungeonLoot = ecrLoot; } public static SourceSwitches Read(string? adminToolConfig, string? creatureRules) { bool vatDungeons = false; if (adminToolConfig != null) { vatDungeons = CfgFile.Parse(adminToolConfig).Bool("Automatic resets", "Reset dungeons automatically") == true; } IReadOnlyDictionary<string, string> block = SourceImport.RespawningBlock(creatureRules); bool flag = Flag(block, "camps"); bool flag2 = Flag(block, "dungeons"); bool ecrLoot = Flag(block, "dungeon loot"); return new SourceSwitches(vatDungeons, flag || flag2, ecrLoot); } private static bool Flag(IReadOnlyDictionary<string, string> block, string key) { string value; bool result = default(bool); return block.TryGetValue(key, out value) && bool.TryParse(value, out result) && result; } } public sealed class StepAsideNotice { public DelveModule Module { get; } public string SourceGuid { get; } public string SourceName { get; } public string Key => ModuleStepAside.KeyOf(Module); internal StepAsideNotice(DelveModule module, SourceInfo source) { Module = module; SourceGuid = source.Guid; SourceName = source.Name; } } public sealed class ModuleStepAsideDecision { public IReadOnlyList<StepAsideNotice> Notices { get; } public IReadOnlyList<DelveModule> Modules => (from n in Notices select n.Module into m orderby m select m).ToArray(); public bool Any => Notices.Count > 0; internal ModuleStepAsideDecision(IReadOnlyList<StepAsideNotice> notices) { Notices = notices; } public bool SteppedAside(DelveModule module) { return Notices.Any((StepAsideNotice n) => n.Module == module); } } public static class ModuleStepAside { public static readonly SourceInfo MaxDungeonRooms = new SourceInfo("digitalroot.mods.maxdungeonrooms", "Max_Dungeon_Rooms", default(DelveModule)); public static readonly SourceInfo MoreDungeonResets = new SourceInfo("com.loathe.MoreDungeonResets", "MoreDungeonResets", DelveModule.FullReset); public static readonly SourceInfo RestockRegen = new SourceInfo("liekos47.restockregen", "RestockRegen", DelveModule.Chests, DelveModule.Resources); public static readonly SourceInfo ValheimAdminTool = new SourceInfo("com.icecub.valheimadmintool", "Valheim_Admin_Tool", DelveModule.FullReset); public static readonly SourceInfo EliteCreaturesReborn = new SourceInfo("gglasgow.elitecreaturesreborn", "Elite Creatures Reborn", DelveModule.Chests, DelveModule.Respawn); public static IReadOnlyList<SourceInfo> Sources { get; } = Array.AsReadOnly(new SourceInfo[5] { MaxDungeonRooms, MoreDungeonResets, RestockRegen, ValheimAdminTool, EliteCreaturesReborn }); public static string KeyOf(DelveModule module) { return "step_aside_" + module.ToString().ToLowerInvariant(); } public static DelveModule ModuleOf(DelveKind kind) { switch (kind) { case DelveKind.FullReset: return DelveModule.FullReset; case DelveKind.Chests: return DelveModule.Chests; case DelveKind.CampSpawners: case DelveKind.DungeonSpawners: return DelveModule.Respawn; default: return DelveModule.Resources; } } public static ModuleStepAsideDecision Decide(IEnumerable<string> installed, SourceSwitches switches) { if (installed == null) { throw new ArgumentNullException("installed"); } if (switches == null) { throw new ArgumentNullException("switches"); } HashSet<string> hashSet = new HashSet<string>(installed, StringComparer.Ordinal); List<StepAsideNotice> list = new List<StepAsideNotice>(); foreach (SourceInfo source in Sources) { if (!hashSet.Contains(source.Guid)) { continue; } foreach (DelveModule module in source.Modules) { if (Acts(source, module, switches) && !list.Any((StepAsideNotice n) => n.Module == module)) { list.Add(new StepAsideNotice(module, source)); } } } return new ModuleStepAsideDecision(list); } private static bool Acts(SourceInfo source, DelveModule module, SourceSwitches switches) { if (source == ValheimAdminTool) { return switches.AdminToolDungeonResets; } if (source == EliteCreaturesReborn) { if (module != DelveModule.Respawn) { return switches.DungeonLoot; } return switches.CreatureRespawn; } return true; } } } namespace ExtraPotions.Delve.Engine { public readonly struct Stamp { public double Day { get; } public DateTime Utc { get; } public bool HasDay { get; } public bool HasUtc { get; } private Stamp(double day, DateTime utc, bool hasDay, bool hasUtc) { Day = day; Utc = utc; HasDay = hasDay; HasUtc = hasUtc; } public static Stamp At(double day, DateTime time) { if (double.IsNaN(day) || double.IsInfinity(day)) { throw new ArgumentException("Expected a finite world day.", "day"); } if (time.Kind == DateTimeKind.Unspecified) { throw new ArgumentException("Expected a UTC or local time, not an unspecified one.", "time"); } return new Stamp(day, time.ToUniversalTime(), hasDay: true, hasUtc: true); } public static Stamp DayOnly(double day) { if (double.IsNaN(day) || double.IsInfinity(day)) { throw new ArgumentException("Expected a finite world day.", "day"); } return new Stamp(day, default(DateTime), hasDay: true, hasUtc: false); } public static Stamp UtcOnly(DateTime time) { if (time.Kind == DateTimeKind.Unspecified) { throw new ArgumentException("Expected a UTC or local time, not an unspecified one.", "time"); } return new Stamp(0.0, time.ToUniversalTime(), hasDay: false, hasUtc: true); } public Stamp CompletedWith(Stamp now) { return new Stamp(HasDay ? Day : now.Day, HasUtc ? Utc : now.Utc, HasDay || now.HasDay, HasUtc || now.HasUtc); } public override string ToString() { return (HasDay ? ("day " + Day.ToString("0.###", CultureInfo.InvariantCulture)) : "day ?") + " / " + (HasUtc ? Utc.ToString("u", CultureInfo.InvariantCulture) : "utc ?"); } } public static class ClockRules { public const double MinDelay = 0.25; public const double MaxDays = 3650.0; public const double MaxHours = 87600.0; public static double Clamp(ClockKind clock, double delay, double fallback = 30.0) { if (double.IsNaN(delay) || double.IsInfinity(delay)) { delay = fallback; } return Math.Min((clock == ClockKind.RealHours) ? 87600.0 : 3650.0, Math.Max(0.25, delay)); } public static double Elapsed(ClockKind clock, Stamp since, Stamp now) { if (clock == ClockKind.RealHours) { if (!since.HasUtc || !now.HasUtc) { return 0.0; } return Math.Max(0.0, (now.Utc - since.Utc).TotalHours); } if (!since.HasDay || !now.HasDay) { return 0.0; } return Math.Max(0.0, now.Day - since.Day); } public static bool Due(ClockKind clock, double delay, Stamp since, Stamp now) { return Elapsed(clock, since, now) >= Clamp(clock, delay) - 1E-09; } public static double Remaining(ClockKind clock, double delay, Stamp since, Stamp now) { return Math.Max(0.0, Clamp(clock, delay) - Elapsed(clock, since, now)); } public static (double? Remaining, bool Due) Inspect(ClockKind clock, double delay, Stamp? since, Stamp now, bool requested) { bool flag = since.HasValue && ((clock != ClockKind.RealHours) ? (since.Value.HasDay && now.HasDay) : (since.Value.HasUtc && now.HasUtc)); return (Remaining: flag ? new double?(Remaining(clock, delay, since.Value, now)) : ((double?)null), Due: requested || (flag && Due(clock, delay, since.Value, now))); } public static int WholeUnitsLeft(double remaining) { if (!(remaining <= 1E-09)) { return (int)Math.Ceiling(remaining - 1E-09); } return 0; } } public static class DelveKeys { public const string ChestSinceDay = "delve.chest.since_day"; public const string ChestSinceUtc = "delve.chest.since_utc"; public const string ChestFirstBy = "delve.chest.first_by"; public const string ChestFirstDay = "delve.chest.first_day"; public const string ChestLastBy = "delve.chest.last_by"; public const string ChestLastDay = "delve.chest.last_day"; public const string FullResetRetry = "delve.fullreset.retry"; public static IReadOnlyList<string> Fixed { get; } = Array.AsReadOnly(new string[7] { "delve.chest.since_day", "delve.chest.since_utc", "delve.chest.first_by", "delve.chest.first_day", "delve.chest.last_by", "delve.chest.last_day", "delve.fullreset.retry" }); private static string Stem(DelveKind kind) { return "delve." + kind.ToString().ToLowerInvariant(); } public static string Spots(DelveKind kind) { return Stem(kind) + ".spots"; } public static string VisitDay(DelveKind kind) { return Stem(kind) + ".visit_day"; } public static string VisitUtc(DelveKind kind) { return Stem(kind) + ".visit_utc"; } public static float EncodeDay(Stamp? stamp) { if (!stamp.HasValue || !stamp.Value.HasDay) { return 0f; } return (float)(stamp.Value.Day + 1.0); } public static long EncodeUtc(Stamp? stamp) { if (!stamp.HasValue || !stamp.Value.HasUtc) { return 0L; } return stamp.Value.Utc.Ticks; } public static Stamp? Decode(float dayPlusOne, long utcTicks) { bool flag = dayPlusOne > 0f && !float.IsNaN(dayPlusOne) && !float.IsInfinity(dayPlusOne); bool flag2 = utcTicks > 0 && utcTicks <= DateTime.MaxValue.Ticks; if (flag && flag2) { return Stamp.At((double)dayPlusOne - 1.0, new DateTime(utcTicks, DateTimeKind.Utc)); } if (flag) { return Stamp.DayOnly((double)dayPlusOne - 1.0); } if (flag2) { return Stamp.UtcOnly(new DateTime(utcTicks, DateTimeKind.Utc)); } return null; } } public enum DungeonMode { Off, Resources, Full } public static class DungeonModes { public static readonly IReadOnlyDictionary<string, DungeonMode> Defaults = new Dictionary<string, DungeonMode>(StringComparer.Ordinal) { ["DG_SunkenCrypt"] = DungeonMode.Resources, ["DG_Cave"] = DungeonMode.Resources, ["DG_DvergrTown"] = DungeonMode.Resources, ["DG_MorkHalla"] = DungeonMode.Resources, ["DG_Hole"] = DungeonMode.Resources, ["DG_DvergrBoss"] = DungeonMode.Resources }; public static DungeonMode Default(string generator) { if (generator == null || !Defaults.TryGetValue(generator, out var value)) { return DungeonMode.Off; } return value; } public static bool Allows(DungeonMode mode, DelveKind kind) { switch (kind) { case DelveKind.FullReset: return mode == DungeonMode.Full; case DelveKind.MudPiles: case DelveKind.FrostCaveCrystals: case DelveKind.BlackCores: case DelveKind.Morkhalla: case DelveKind.Hole: case DelveKind.Queen: return mode == DungeonMode.Resources; case DelveKind.Chests: return mode != DungeonMode.Full; default: return true; } } } public sealed class DungeonSiteReport { public string Id { get; } public DelveKind Kind { get; } public string Generator { get; } public Vec3 Entrance { get; } public ClockKind Clock { get; } public double? Remaining { get; } public bool Requested { get; } public bool EvaluationPending { get; } public bool Due { get; } public bool DryRun { get; } public DungeonSiteReport(string id, DelveKind kind, string generator, Vec3 entrance, ClockKind clock, double delay, Stamp? lastVisit, Stamp now, bool requested, bool dryRun, bool evaluationPending = false) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Expected a site identity.", "id"); } if (string.IsNullOrWhiteSpace(generator)) { throw new ArgumentException("Expected a generator name.", "generator"); } if (!Finite(entrance.X) || !Finite(entrance.Y) || !Finite(entrance.Z)) { throw new ArgumentException("Expected a finite entrance position.", "entrance"); } Id = id; Kind = kind; Generator = generator; Entrance = entrance; Clock = clock; Requested = requested; DryRun = dryRun; EvaluationPending = evaluationPending; (Remaining, Due) = ClockRules.Inspect(clock, delay, lastVisit, now, requested); } private static bool Finite(float n) { if (!float.IsNaN(n)) { return !float.IsInfinity(n); } return false; } } public sealed class KindReport { public int Tracked { get; } public int Waiting { get; } public int Deferred { get; } public int Acted { get; } public IReadOnlyDictionary<GuardResult, int> Blocked { get; } public bool Active { get; } public bool DryRun { get; } public string? Note { get; } public KindReport(int tracked, int waiting, int deferred, int acted, IReadOnlyDictionary<GuardResult, int>? blocked, bool active, bool dryRun, string? note = null) { Tracked = tracked; Waiting = waiting; Deferred = deferred; Acted = acted; Blocked = blocked ?? new Dictionary<GuardResult, int>(); Active = active; DryRun = dryRun; Note = note; } } public sealed class EngineStatus { private readonly Dictionary<DelveKind, KindReport> kinds = new Dictionary<DelveKind, KindReport>(); private readonly LinkedList<string> actions = new LinkedList<string>(); private int capacity; public IEnumerable<string> LastActions => actions; public EngineStatus(int lines) { Resize(lines); } public void Resize(int lines) { capacity = Math.Max(1, lines); while (actions.Count > capacity) { actions.RemoveLast(); } } public void Record(DelveKind kind, string what, bool dryRun, Stamp when) { actions.AddFirst(Day(when) + " " + kind.ToString() + ": " + (dryRun ? "dry run: would have " : "") + what); while (actions.Count > capacity) { actions.RemoveLast(); } } public void Set(DelveKind kind, KindReport report) { kinds[kind] = report ?? throw new ArgumentNullException("report"); } public KindReport? Get(DelveKind kind) { if (!kinds.TryGetValue(kind, out KindReport value)) { return null; } return value; } public IEnumerable<string> Lines(Stamp now, bool dryRunAll, Func<GuardResult, string>? reason = null) { yield return "Delve status, " + Day(now) + (dryRunAll ? " (DRY RUN for every kind)" : ""); foreach (DelveKind value2 in Enum.GetValues(typeof(DelveKind))) { if (!kinds.TryGetValue(value2, out KindReport value)) { continue; } if (!value.Active) { yield return value2.ToString() + ": off" + ((value.Note != null) ? (" (" + value.Note + ")") : ""); continue; } string text = string.Join(", ", from b in value.Blocked where b.Value > 0 select ((reason != null) ? reason(b.Key) : b.Key.ToString()) + " " + b.Value); yield return value2.ToString() + ": " + value.Tracked + " tracked, " + value.Waiting + " waiting, " + value.Deferred + " deferred, " + value.Acted + " acted" + ((text.Length > 0) ? (", blocked: " + text) : "") + (value.DryRun ? " (dry run)" : "") + ((value.Note != null) ? (" (" + value.Note + ")") : ""); } foreach (string action in actions) { yield return " " + action; } } private static string Day(Stamp s) { if (!s.HasDay) { return "day ?"; } return "day " + s.Day.ToString("0.##", CultureInfo.InvariantCulture); } } public readonly struct Vec3 : IEquatable<Vec3> { public float X { get; } public float Y { get; } public float Z { get; } public Vec3(float x, float y, float z) { X = x; Y = y; Z = z; } public static float Flat(Vec3 a, Vec3 b) { float num = a.X - b.X; float num2 = a.Z - b.Z; return (float)Math.Sqrt(num * num + num2 * num2); } public static float SqrDistance(Vec3 a, Vec3 b) { float num = a.X - b.X; float num2 = a.Y - b.Y; float num3 = a.Z - b.Z; return num * num + num2 * num2 + num3 * num3; } public bool Equals(Vec3 other) { if (X.Equals(other.X) && Y.Equals(other.Y)) { return Z.Equals(other.Z); } return false; } public override bool Equals(object? obj) { if (obj is Vec3 other) { return Equals(other); } return false; } public override int GetHashCode() { return (((X.GetHashCode() * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode(); } public override string ToString() { return "(" + X.ToString("0.##", CultureInfo.InvariantCulture) + ", " + Y.ToString("0.##", CultureInfo.InvariantCulture) + ", " + Z.ToString("0.##", CultureInfo.InvariantCulture) + ")"; } } public readonly struct Box { public Vec3 Min { get; } public Vec3 Max { get; } public Box(Vec3 min, Vec3 max) { if (min.X > max.X || min.Y > max.Y || min.Z > max.Z) { throw new ArgumentException("Expected min below max on every axis."); } Min = min; Max = max; } public bool Contains(Vec3 p) { if (p.X >= Min.X && p.X <= Max.X && p.Y >= Min.Y && p.Y <= Max.Y && p.Z >= Min.Z) { return p.Z <= Max.Z; } return false; } } public readonly struct Area { public Vec3 Center { get; } public float Radius { get; } public Area(Vec3 center, float radius) { if (!(radius >= 0f) || float.IsInfinity(radius)) { throw new ArgumentException("Expected a finite radius of 0 or more.", "radius"); } Center = center; Radius = radius; } public bool Contains(Vec3 p) { return Vec3.Flat(Center, p) <= Radius; } } public enum GuardResult { Clear, PlayerNear, Held, Built, PlayerBase } public sealed class GuardInput { public Vec3 Center { get; } public float PlayerRadius { get; } public Box? Interior { get; set; } public IReadOnlyList<Vec3> Players { get; set; } = Array.Empty<Vec3>(); public bool HeldByPlayer { get; set; } public float Clearance { get; set; } public IReadOnlyList<Vec3> Built { get; set; } = Array.Empty<Vec3>(); public bool BaseGuard { get; set; } = true; public IReadOnlyList<Area> Bases { get; set; } = Array.Empty<Area>(); public GuardInput(Vec3 center, float playerRadius) { if (!(playerRadius >= 0f) || float.IsInfinity(playerRadius)) { throw new ArgumentException("Expected a finite radius of 0 or more.", "playerRadius"); } Center = center; PlayerRadius = playerRadius; } } public static class Guards { public static GuardResult Evaluate(GuardInput input) { if (input == null) { throw new ArgumentNullException("input"); } foreach (Vec3 player in input.Players) { if (Vec3.Flat(player, input.Center) < input.PlayerRadius || (input.Interior.HasValue && input.Interior.Value.Contains(player))) { return GuardResult.PlayerNear; } } if (input.HeldByPlayer) { return GuardResult.Held; } if (WorldRules.BuiltNear(input.Center, input.Clearance, input.Built)) { return GuardResult.Built; } if (input.BaseGuard) { foreach (Area basis in input.Bases) { if (basis.Contains(input.Center)) { return GuardResult.PlayerBase; } } } return GuardResult.Clear; } } public enum ClockKind { InGameDays, RealHours } public enum DelveKind { FullReset, Chests, MudPiles, FrostCaveCrystals, BlackCores, Morkhalla, Hole, AncientArmor, Obsidian, Flametal, Ice, Queen, CampSpawners, DungeonSpawners, Kall } public sealed class Candidate { public string SiteId { get; } public DelveKind Kind { get; } public Stamp Since { get; } public GuardResult Guard { get; } public bool RegenerateNow { get; } public Candidate(string siteId, DelveKind kind, Stamp since, GuardResult guard, bool regenerateNow = false) { if (string.IsNullOrWhiteSpace(siteId)) { throw new ArgumentException("Expected a site id.", "siteId"); } SiteId = siteId; Kind = kind; Since = since; Guard = guard; RegenerateNow = regenerateNow; } } public sealed class SchedulerOptions { public int MaxActions { get; } public bool DryRunAll { get; } public Func<DelveKind, (ClockKind Clock, double Delay)> ClockOf { get; } public Func<DelveKind, bool> DryRunOf { get; } public SchedulerOptions(int maxActions, bool dryRunAll, Func<DelveKind, (ClockKind Clock, double Delay)> clockOf, Func<DelveKind, bool> dryRunOf) { if (maxActions < 1) { throw new ArgumentOutOfRangeException("maxActions", "Expected at least one action per check."); } MaxActions = maxActions; DryRunAll = dryRunAll; ClockOf = clockOf ?? throw new ArgumentNullException("clockOf"); DryRunOf = dryRunOf ?? throw new ArgumentNullException("dryRunOf"); } } public sealed class PlannedAction { public Candidate Site { get; } public string SiteId => Site.SiteId; public DelveKind Kind => Site.Kind; public bool DryRun { get; } public double Overdue { get; } internal PlannedAction(Candidate site, bool dryRun, double overdue) { Site = site; DryRun = dryRun; Overdue = overdue; } } public sealed class SchedulePlan { public IReadOnlyList<PlannedAction> Actions { get; } public int Waiting { get; } public int Deferred { get; } public IReadOnlyDictionary<GuardResult, int> Blocked { get; } internal SchedulePlan(IReadOnlyList<PlannedAction> actions, int waiting, int deferred, IReadOnlyDictionary<GuardResult, int> blocked) { Actions = actions; Waiting = waiting; Deferred = deferred; Blocked = blocked; } } public static class Scheduler { public static SchedulePlan Plan(IEnumerable<Candidate> sites, Stamp now, SchedulerOptions options) { if (sites == null) { throw new ArgumentNullException("sites"); } if (options == null) { throw new ArgumentNullException("options"); } Dictionary<GuardResult, int> dictionary = (from GuardResult g in Enum.GetValues(typeof(GuardResult)) where g != GuardResult.Clear select g).ToDictionary((GuardResult g) => g, (GuardResult g) => 0); List<PlannedAction> list = new List<PlannedAction>(); int num = 0; foreach (Candidate site in sites) { (ClockKind, double) tuple = options.ClockOf(site.Kind); ClockKind item = tuple.Item1; double item2 = tuple.Item2; Stamp since = site.Since.CompletedWith(now); double overdue; if (site.RegenerateNow) { overdue = double.MaxValue; } else { if (!ClockRules.Due(item, item2, since, now)) { num++; continue; } overdue = ClockRules.Elapsed(item, since, now) - ClockRules.Clamp(item, item2); } if (site.Guard != GuardResult.Clear) { dictionary[site.Guard]++; } else { list.Add(new PlannedAction(site, options.DryRunAll || options.DryRunOf(site.Kind), overdue)); } } List<PlannedAction> list2 = list.OrderByDescending((PlannedAction a) => a.Overdue).ThenBy<PlannedAction, string>((PlannedAction a) => a.SiteId, StringComparer.Ordinal).ToList(); List<PlannedAction> list3 = list2.Take(options.MaxActions).ToList(); return new SchedulePlan(list3, num, list2.Count - list3.Count, dictionary); } } public readonly struct ImportCount { public int Spots { get; } public int Visits { get; } public ImportCount(int spots, int visits) { Spots = spots; Visits = visits; } } public sealed class Sidecar { public const int Version = 1; private bool readOnly; private static readonly Lazy<HashSet<int>> drifting = new Lazy<HashSet<int>>(() => new HashSet<int>(KindCatalog.All.Where((KindInfo k) => k.Drifts).SelectMany((KindInfo k) => k.Whole).Select(StableHash.Of))); public List<RecordSpot> Spots { get; } = new List<RecordSpot>(); public Dictionary<(int X, int Z), Stamp> Visits { get; } = new Dictionary<(int, int), Stamp>(); public HashSet<DelveKind> RegenerateNow { get; } = new HashSet<DelveKind>(); public bool ImportedRestockRegen { get; set; } public bool ReadOnly => readOnly; public string Write() { return MiniJson.Write(new Dictionary<string, object> { ["version"] = 1, ["importedRestockRegen"] = ImportedRestockRegen, ["regenerateNow"] = ((IEnumerable<DelveKind>)RegenerateNow.OrderBy((DelveKind k) => k)).Select((Func<DelveKind, object>)((DelveKind k) => k.ToString())).ToList(), ["visits"] = ((IEnumerable<KeyValuePair<(int, int), Stamp>>)(from v in Visits orderby v.Key.X, v.Key.Z select v)).Select((Func<KeyValuePair<(int, int), Stamp>, object>)((KeyValuePair<(int X, int Z), Stamp> v) => new List<object> { v.Key.X, v.Key.Z, StampValue(v.Value) })).ToList(), ["spots"] = ((IEnumerable<RecordSpot>)Spots).Select((Func<RecordSpot, object>)((RecordSpot s) => new List<object> { s.Prefab, s.Position.X, s.Position.Y, s.Position.Z, s.Rotation.X, s.Rotation.Y, s.Rotation.Z })).ToList() }); } public static Sidecar Parse(string? text, out List<string> errors) { errors = new List<string>(); Sidecar sidecar = new Sidecar(); if (string.IsNullOrWhiteSpace(text)) { return sidecar; } try { if (!(MiniJson.Parse(text) is IDictionary<string, object> dictionary)) { throw new FormatException("JSON root is not an object."); } object value; int num = ((dictionary.TryGetValue("version", out value) && value is double num2) ? ((int)num2) : 0); if (num != 1) { errors.Add("sidecar version " + num + " is not " + 1 + "; starting empty and leaving the file alone"); return new Sidecar { readOnly = true }; } bool flag = default(bool); int num3; if (dictionary.TryGetValue("importedRestockRegen", out var value2)) { if (value2 is bool) { flag = (bool)value2; num3 = 1; } else { num3 = 0; } } else { num3 = 0; } sidecar.ImportedRestockRegen = (byte)((uint)num3 & (flag ? 1u : 0u)) != 0; foreach (object item in List(dictionary, "regenerateNow")) { if (item is string value3 && Enum.TryParse<DelveKind>(value3, out var result)) { sidecar.RegenerateNow.Add(result); } } foreach (object item2 in List(dictionary, "visits")) { if (item2 is IList<object> { Count: 3 } list && list[0] is double num4 && list[1] is double num5) { Stamp? stamp = Read(list[2]); if (stamp.HasValue) { Stamp valueOrDefault = stamp.GetValueOrDefault(); sidecar.Visits[((int)num4, (int)num5)] = valueOrDefault; continue; } } errors.Add("skipped a visit entry"); } foreach (object item3 in List(dictionary, "spots")) { if (item3 is IList<object> { Count: 7 } list2 && list2.All((object n) => n is double)) { double[] array = list2.Cast<double>().ToArray(); sidecar.Spots.Add(new RecordSpot((int)array[0], new Vec3((float)array[1], (float)array[2], (float)array[3]), new Vec3((float)array[4], (float)array[5], (float)array[6]))); } else { errors.Add("skipped a spot entry"); } } return sidecar; } catch (FormatException ex) { errors.Add("sidecar unreadable (" + ex.Message + "); starting empty and leaving the file alone"); return new Sidecar { readOnly = true }; } } public ImportCount ImportRestockRegen(RecordFile restockRegen) { if (restockRegen == null) { throw new ArgumentNullException("restockRegen"); } int num = 0; int num2 = 0; foreach (RecordSpot spot in restockRegen.Spots) { if (!Spots.Any((RecordSpot s) => Same(s, spot))) { Spots.Add(spot); num++; } } foreach (KeyValuePair<(int, int), int> visit in restockRegen.Visits) { if (!Visits.ContainsKey(visit.Key)) { Visits[visit.Key] = Stamp.DayOnly(visit.Value); num2++; } } ImportedRestockRegen = true; return new ImportCount(num, num2); } private static bool Same(RecordSpot a, RecordSpot b) { if (a.Prefab != b.Prefab) { return false; } if (!drifting.Value.Contains(a.Prefab)) { return Vec3.SqrDistance(a.Position, b.Position) < 0.04f; } return Vec3.Flat(a.Position, b.Position) < 6f; } private static IEnumerable<object?> List(IDictionary<string, object?> root, string key) { if (!root.TryGetValue(key, out object value) || !(value is IList<object> result)) { return Enumerable.Empty<object>(); } return result; } private static object StampValue(Stamp s) { Dictionary<string, object> dictionary = new Dictionary<string, object>(); if (s.HasDay) { dictionary["day"] = s.Day; } if (s.HasUtc) { dictionary["utc"] = s.Utc.ToString("o", CultureInfo.InvariantCulture); } return dictionary; } private static Stamp? Read(object? value) { if (!(value is IDictionary<string, object> dictionary)) { return null; } object value2; bool flag = dictionary.TryGetValue("day", out value2) && value2 is double; DateTime result = default(DateTime); object value3; bool flag2 = dictionary.TryGetValue("utc", out value3) && value3 is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out result) && result.Kind == DateTimeKind.Utc; if (flag && flag2) { return Stamp.At((double)value2, result); } if (flag) { return Stamp.DayOnly((double)value2); } if (flag2) { return Stamp.UtcOnly(result); } return null; } } public static class WorldClock { public const double DawnFraction = 0.15; public static Stamp Now(double worldSeconds, double dayLengthSeconds, DateTime utcNow) { Check(dayLengthSeconds); return Stamp.At(Math.Max(0.0, worldSeconds) / dayLengthSeconds, utcNow); } public static int DayIndex(double worldSeconds, double dayLengthSeconds) { Check(dayLengthSeconds); return (int)Math.Floor(Math.Max(0.0, worldSeconds) / dayLengthSeconds); } public static bool DawnBetween(double previousSeconds, double nowSeconds, double dayLengthSeconds) { Check(dayLengthSeconds); if (!(nowSeconds > previousSeconds)) { return false; } double num = 0.15 * dayLengthSeconds; return Math.Floor((previousSeconds - num) / dayLengthSeconds + 1.0) * dayLengthSeconds + num <= nowSeconds; } public static bool ShouldSweep(ClockKind clock, bool dawn, bool regenerateNow) { return regenerateNow || clock == ClockKind.RealHours || dawn; } public static Stamp FromGameTicks(long ticks, double dayLengthSeconds) { Check(dayLengthSeconds); if (ticks <= 0) { return default(Stamp); } return Stamp.DayOnly((double)ticks / 10000000.0 / dayLengthSeconds); } private static void Check(double dayLengthSeconds) { if (!(dayLengthSeconds > 0.0) || double.IsInfinity(dayLengthSeconds)) { throw new ArgumentException("Expected a positive day length.", "dayLengthSeconds"); } } } public static class WorldRules { public const float ClearanceHeight = 30f; public static bool HeldByPlayer(bool hasOwner, bool ownerIsServer, bool ownerConnected, bool inOwnerActiveArea) { return hasOwner && !ownerIsServer && ownerConnected && inOwnerActiveArea; } public static bool BuiltNear(Vec3 spot, float clearance, IEnumerable<Vec3> playerBuilt) { if (clearance <= 0f || playerBuilt == null) { return false; } foreach (Vec3 item in playerBuilt) { if (Vec3.Flat(item, spot) < clearance && Math.Abs(item.Y - spot.Y) < 30f) { return true; } } return false; } } } namespace ExtraPotions.Delve.Data { public sealed class DungeonsFile { public const string FileName = "Delve.Dungeons.yml"; public static DungeonsFile Empty { get; } = new DungeonsFile(new Dictionary<string, DungeonMode>(), Array.Empty<string>()); public IReadOnlyDictionary<string, DungeonMode> Modes { get; } public IReadOnlyList<string> Errors { get; } private DungeonsFile(IReadOnlyDictionary<string, DungeonMode> modes, IReadOnlyList<string> errors) { Modes = modes; Errors = errors; } public DungeonMode ModeOf(string generator) { if (generator == null || !Modes.TryGetValue(generator, out var value)) { return DungeonModes.Default(generator); } return value; } public static DungeonsFile Parse(string? text) { List<string> list = new List<string>(); Dictionary<string, DungeonMode> dictionary = new Dictionary<string, DungeonMode>(StringComparer.Ordinal); if (MiniYaml.Read(text, list).TryGetValue("modes", out IReadOnlyList<YamlEntry> value)) { foreach (YamlEntry item in value) { if (item.Scalar != null && Enum.TryParse<DungeonMode>(item.Scalar, ignoreCase: true, out var result) && Enum.IsDefined(typeof(DungeonMode), result) && !int.TryParse(item.Scalar, out var result2)) { dictionary[item.Name] = result; continue; } string[] obj = new string[5] { "line ", null, null, null, null }; result2 = item.Line; obj[1] = result2.ToString(); obj[2] = ": '"; obj[3] = item.Name; obj[4] = "' needs Off, Resources or Full; the default stays"; list.Add(string.Concat(obj)); } } return new DungeonsFile(dictionary, list); } public static Dictionary<string, DungeonMode>? WithMissing(IReadOnlyDictionary<string, DungeonMode> written, IEnumerable<string> generators) { Dictionary<string, DungeonMode> dictionary = written.ToDictionary<KeyValuePair<string, DungeonMode>, string, DungeonMode>((KeyValuePair<string, DungeonMode> p) => p.Key, (KeyValuePair<string, DungeonMode> p) => p.Value, StringComparer.Ordinal); int count = dictionary.Count; foreach (string generator in generators) { if (!string.IsNullOrEmpty(generator) && !dictionary.ContainsKey(generator)) { dictionary[generator] = DungeonModes.Default(generator); } } if (dictionary.Count != count) { return dictionary; } return null; } public static string Write(IReadOnlyDictionary<string, DungeonMode> modes) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("# Delve dungeon modes: Off, Resources (partial regrow and boss return) or Full (wipe and regenerate).\n"); stringBuilder.Append("# Types not listed keep Delve's default (Resources where something regrows, Off otherwise).\n"); stringBuilder.Append("modes:\n"); foreach (KeyValuePair<string, DungeonMode> item in modes.OrderBy<KeyValuePair<string, DungeonMode>, string>((KeyValuePair<string, DungeonMode> p) => p.Key, StringComparer.Ordinal)) { stringBuilder.Append(" ").Append(item.Key).Append(": ") .Append(item.Value) .Append('\n'); } return stringBuilder.ToString(); } } public static class MiniJson { private sealed class Reader { private const int MaxDepth = 64; private readonly string s; private int i; public bool End => i >= s.Length; public Reader(string text) { s = text; } public FormatException Error(string what) { return new FormatException("JSON " + what + " at offset " + i + "."); } public void Space() { while (i < s.Length && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r')) { i++; } } public object? Value(int depth) { if (depth > 64) { throw Error("nesting too deep"); } if (End) { throw Error("value expected"); } char c = s[i]; switch (c) { case '{': return Object(depth); case '[': return Array(depth); case '"': return String(); default: if (Word("true")) { return true; } if (Word("false")) { return false; } if (Word("null")) { return null; } switch (c) { case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return Number(); default: throw Error("value expected"); } } } private bool Word(string word) { if (string.CompareOrdinal(s, i, word, 0, word.Length) != 0) { return false; } i += word.Length; return true; } private double Number() { int num = i; if (s[i] == '-') { i++; } while (i < s.Length && "0123456789.eE+-".IndexOf(s[i]) >= 0) { i++; } if (!double.TryParse(s.Substring(num, i - num), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { throw Error("bad number"); } return result; } private string String() { i++; StringBuilder stringBuilder = new StringBuilder(); while (!End) { char c = s[i++]; switch (c) { case '"': return stringBuilder.ToString(); default: stringBuilder.Append(c); break; case '\\': if (End) { throw Error("bad escape"); } switch (s[i++]) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { if (i + 4 > s.Length || !int.TryParse(s.Substring(i, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { throw Error("bad unicode escape"); } stringBuilder.Append((char)result); i += 4; break; } default: throw Error("bad escape"); } break; } } throw Error("unterminated string"); } private IDictionary<string, object?> Object(int depth) { i++; Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal); Space(); if (!End && s[i] == '}') { i++; return dictionary; } while (true) { Space(); if (End || s[i] != '"') { throw Error("key expected"); } string key = String(); Space(); if (End || s[i] != ':') { throw Error("colon expected"); } i++; Space(); dictionary[key] = Value(depth + 1); Space(); if (End) { throw Error("unterminated object"); } if (s[i] != ',') { break; } i++; } if (s[i] == '}') { i++; return dictionary; } throw Error("comma or brace expected"); } private IList<object?> Array(int depth) { i++; List<object> list = new List<object>(); Space(); if (!End && s[i] == ']') { i++; return list; } while (true) { Space(); list.Add(Value(depth + 1)); Space(); if (End) { throw Error("unterminated array"); } if (s[i] != ',') { break; } i++; } if (s[i] == ']') { i++; return list; } throw Error("comma or bracket expected"); } } public static object? Parse(string text) { if (text == null) { throw new ArgumentNullException("text"); } Reader reader = new Reader(text); reader.Space(); object? result = reader.Value(0); reader.Space(); if (!reader.End) { throw reader.Error("trailing text"); } return result; } public static string Write(object? value) { StringBuilder stringBuilder = new StringBuilder(); WriteValue(stringBuilder, value, 0); stringBuilder.Append('\n'); return stringBuilder.ToString(); } private static void WriteValue(StringBuilder text, object? value, int depth) { if (value != null) { if (!(value is bool flag)) { if (!(value is string s)) { if (!(value is double d)) { if (!(value is float num)) { if (!(value is int num2)) { if (!(value is long num3)) { if (!(value is IDictionary<string, object> dictionary)) { if (value is IEnumerable enumerable) { text.Append('['); bool flag2 = false; foreach (object item in enumerable) { if (flag2) { text
plugins/Delve/Delve.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.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExtraPotions.Delve.Chests; using ExtraPotions.Delve.Commands; using ExtraPotions.Delve.Config; using ExtraPotions.Delve.Conversion; using ExtraPotions.Delve.Data; using ExtraPotions.Delve.Engine; using ExtraPotions.Delve.Integration; using ExtraPotions.Delve.Localization; using ExtraPotions.Delve.Regrow; using ExtraPotions.Delve.Reset; using ExtraPotions.Delve.Respawn; using ExtraPotions.Delve.Rooms; using ExtraPotions.Delve.Surface; using ExtraPotions.Meadworks.Contracts; using ExtraPotions.Meadworks.Crucible; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.1.15.0")] [assembly: AssemblyInformationalVersion("0.1.15")] [assembly: AssemblyProduct("Delve")] [assembly: AssemblyTitle("Delve")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.15.0")] [module: UnverifiableCode] [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 ExtraPotions.Delve.Regrow { internal sealed class SweepOccupancy<TSite, TPlayer> where TSite : class { private readonly Func<IReadOnlyList<TPlayer>, HashSet<TSite>> project; private IReadOnlyList<TPlayer>? players; private HashSet<TSite>? occupied; private long geometryRevision; private float insideRadius; internal SweepOccupancy(Func<IReadOnlyList<TPlayer>, HashSet<TSite>> project) { this.project = project; } internal bool Contains(TSite site, IReadOnlyList<TPlayer> snapshot, long revision, float radius) { if (occupied == null || players != snapshot || geometryRevision != revision || !insideRadius.Equals(radius)) { HashSet<TSite> hashSet = project(snapshot); players = snapshot; geometryRevision = revision; insideRadius = radius; occupied = hashSet; } return occupied.Contains(site); } } } namespace ExtraPotions.Delve.Runtime { public sealed class DelveChests : IDelveService { [HarmonyPatch(typeof(Container), "SetInUse")] private static class SeeLocalUse { private static bool Prepare() { return !DelveModules.StepAside.SteppedAside((DelveModule)2); } private static void Postfix(Container __instance) { ObserveLocal(__instance); } } [HarmonyPatch(typeof(Container), "Save")] private static class SeeLocalSave { private static bool Prepare() { return !DelveModules.StepAside.SteppedAside((DelveModule)2); } private static void Postfix(Container __instance) { ObserveLocal(__instance); } } [HarmonyPatch(typeof(ZDO), "Deserialize")] private static class SeeOpenings { private static bool Prepare() { return !DelveModules.StepAside.SteppedAside((DelveModule)2); } private static void Postfix(ZDO __instance) { DelveChests current = DelveChests.current; if (current == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { current.Observe(__instance); } catch (Exception ex) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Warning("Delve chest update: " + ex.GetType().Name + ": " + ex.Message); } } } } private static readonly int sinceDay = StringExtensionMethods.GetStableHashCode("delve.chest.since_day"); private static readonly int sinceUtc = StringExtensionMethods.GetStableHashCode("delve.chest.since_utc"); private static readonly int firstBy = StringExtensionMethods.GetStableHashCode("delve.chest.first_by"); private static readonly int firstDay = StringExtensionMethods.GetStableHashCode("delve.chest.first_day"); private static readonly int lastBy = StringExtensionMethods.GetStableHashCode("delve.chest.last_by"); private static readonly int lastDay = StringExtensionMethods.GetStableHashCode("delve.chest.last_day"); private static DelveChests? current; private DelveEngine? engine; private readonly Dictionary<int, Container> prefabs = new Dictionary<int, Container>(); private readonly ChestExclusions exclusions = new ChestExclusions(); private readonly Dictionary<ZDOID, Stamp?> dryStamps = new Dictionary<ZDOID, Stamp?>(); private readonly Dictionary<ZDOID, Stamp> seen = new Dictionary<ZDOID, Stamp>(); private readonly HashSet<ZDOID> open = new HashSet<ZDOID>(); private readonly HashSet<ZDOID> announced = new HashSet<ZDOID>(); private readonly Dictionary<ZDOID, Record> pending = new Dictionary<ZDOID, Record>(); public IReadOnlyList<DelveKind> Kinds { get; } = (IReadOnlyList<DelveKind>)(object)new DelveKind[1] { (DelveKind)1 }; public void Start(DelveEngine e) { engine = e; current = this; prefabs.Clear(); exclusions.Update(e.Settings.Get<string>(Chests.Exclude)); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Container component = prefab.GetComponent<Container>(); if (!((Object)(object)component == (Object)null) && component.m_defaultItems != null && component.m_defaultItems.m_drops.Count != 0) { prefabs[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = component; if (exclusions.Allows(((Object)prefab).name) && component.m_autoDestroyEmpty) { e.Log.Info("Delve chests: " + ((Object)prefab).name + " deletes itself when emptied and cannot be refilled."); } } } e.Log.Verbose("Delve chests: " + prefabs.Count + " indexed loot chest prefabs, " + exclusions.Count + " excluded names."); } public void Stop(DelveEngine e) { Flush(); if (current == this) { current = null; } engine = null; prefabs.Clear(); exclusions.Clear(); dryStamps.Clear(); seen.Clear(); open.Clear(); announced.Clear(); pending.Clear(); } private ChestOptions Options(DelveEngine e) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown var (val, num) = e.ClockOf((DelveKind)1); return new ChestOptions(val, num, e.Settings.Get<bool>(Chests.ResetOpened), e.Settings.Get<bool>(Chests.RestockLoaded)); } private Stamp? GetStamp(ZDO zdo, bool dryRun) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (!dryRun || !dryStamps.TryGetValue(zdo.m_uid, out var value)) { return DelveKeys.Decode(zdo.GetFloat(sinceDay, 0f), zdo.GetLong(sinceUtc, 0L)); } return value; } private void SetStamp(ZDO zdo, Stamp? stamp, bool dryRun) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (dryRun) { dryStamps[zdo.m_uid] = stamp; return; } zdo.Set(sinceDay, DelveKeys.EncodeDay(stamp)); zdo.Set(sinceUtc, DelveKeys.EncodeUtc(stamp)); } public void Check(DelveEngine e, ISet<DelveKind> sweeping) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0126: 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_0143: Expected I4, but got Unknown //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Expected O, but got Unknown //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) Flush(); exclusions.Update(e.Settings.Get<string>(Chests.Exclude)); if (!sweeping.Contains((DelveKind)1)) { return; } bool flag = e.DryRun((DelveKind)1); ChestOptions val = Options(e); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; long start = DelveCheckTiming.Start(e.CheckTiming); List<ZDO> list = ZDOMan.instance.m_objectsByID.Values.ToList(); e.CheckTiming?.Stage("chest_snapshot", start, (DelveKind)1, list.Count); foreach (ZDO item in list) { if (!prefabs.TryGetValue(item.GetPrefab(), out Container value) || !exclusions.Allows(((Object)value).name)) { continue; } num++; if (!DelveDungeonIndex.Allows(item.GetPosition(), (DelveKind)1)) { num6++; continue; } ChestFacts val3 = Facts(item, flag); ChestDecision val4 = ChestRefill.Decide(val3, e.Now, val, e.RegenerateNow((DelveKind)1)); ChestAction action = val4.Action; switch (action - 3) { case 0: num5++; break; case 1: if (!val4.KeepOpening) { seen.Remove(item.m_uid); } SetStamp(item, null, flag); break; case 2: seen.Remove(item.m_uid); SetStamp(item, val4.NewStamp, flag); break; case 3: num2++; break; case 4: { seen.Remove(item.m_uid); if (e.Budget <= 0) { num3++; if (val4.NewStamp.HasValue) { SetStamp(item, val4.NewStamp, flag); } break; } e.Budget--; string rolled; if (flag) { SetStamp(item, null, dryRun: true); e.Record((DelveKind)1, "refilled " + ((Object)value).name + " at " + Where(item) + ", replacing " + val3.ItemCount + " stacks", dryRun: true); num4++; } else if (Restock(item, value, out rolled)) { SetStamp(item, null, dryRun: false); e.Record((DelveKind)1, "refilled " + ((Object)value).name + " at " + Where(item) + ": " + rolled, dryRun: false); num4++; } else if (val4.NewStamp.HasValue) { SetStamp(item, val4.NewStamp, dryRun: false); } break; } } } e.Swept((DelveKind)1, num3); e.Report((DelveKind)1, new KindReport(num, num2, num3, num4, (IReadOnlyDictionary<GuardResult, int>)new Dictionary<GuardResult, int> { [(GuardResult)2] = num5 }, true, flag, (num6 > 0) ? (num6 + " in Full dungeons") : null)); } private ChestFacts Facts(ZDO zdo, bool dryRun) { //IL_0007: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown Stamp value; Stamp? val = (seen.TryGetValue(zdo.m_uid, out value) ? new Stamp?(value) : ((Stamp?)null)); return new ChestFacts(ChestInventory.ItemCount(zdo.GetByteArray(ZDOVars.s_items, (byte[])null)), GetStamp(zdo, dryRun), val, zdo.GetLong(ZDOVars.s_creator, 0L) != 0, DelveWorld.HeldByPlayer(zdo), zdo.GetInt(ZDOVars.s_inUse, 0) == 1, LiveInventory(zdo)); } private bool LiveInventory(ZDO zdo) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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) if (zdo == null) { return false; } if (DelveWorld.HeldByPlayer(zdo)) { return true; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return false; } ZNetView val = instance.FindInstance(zdo); if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent<Container>() != (Object)null) { return true; } Vector3 position = zdo.GetPosition(); IReadOnlyList<DelvePlayer> readOnlyList2; if (engine == null) { IReadOnlyList<DelvePlayer> readOnlyList = DelveWorld.Players(); readOnlyList2 = readOnlyList; } else { readOnlyList2 = engine.Players; } foreach (DelvePlayer item in readOnlyList2) { if (ZNetScene.InActiveArea(position, item.Position)) { return true; } } return false; } private bool Restock(ZDO zdo, Container prefab, out string rolled) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown rolled = ""; if (!ChestRefill.MayReplaceItems(DelveWorld.HeldByPlayer(zdo), LiveInventory(zdo))) { return false; } Inventory val = new Inventory(prefab.m_name, (Sprite)null, prefab.m_width, prefab.m_height); List<string> list = new List<string>(); foreach (ItemData dropListItem in prefab.m_defaultItems.GetDropListItems()) { list.Add((((Object)(object)dropListItem.m_dropPrefab != (Object)null) ? ((Object)dropListItem.m_dropPrefab).name : "?") + "x" + dropListItem.m_stack); val.AddItem(dropListItem); } if (val.NrOfItems() == 0) { return false; } if (!ChestRefill.MayReplaceItems(DelveWorld.HeldByPlayer(zdo), LiveInventory(zdo))) { return false; } ZPackage val3 = new ZPackage(); val.Save(val3); zdo.Set(ZDOVars.s_items, val3.GetArray()); rolled = string.Join(" ", list); return true; } private static string Where(ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Vector3 position = zdo.GetPosition(); return "(" + position.x.ToString("0") + ", " + position.z.ToString("0") + ")"; } private void Flush() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0056: 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) foreach (ZDOID item in pending.Keys.ToList()) { ZDOMan instance = ZDOMan.instance; ZDO val2 = ((instance != null) ? instance.GetZDO(item) : null); if (val2 == null) { pending.Remove(item); } else if (!DelveWorld.HeldByPlayer(val2)) { Record val3 = pending[item]; val2.Set(firstBy, val3.FirstBy); val2.Set(firstDay, ChestHistory.Encode(val3.FirstDay), false); val2.Set(lastBy, val3.LastBy); val2.Set(lastDay, ChestHistory.Encode(val3.LastDay), false); pending.Remove(item); } } } private Record Read(ZDO zdo) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown if (!pending.TryGetValue(zdo.m_uid, out Record value)) { return new Record(zdo.GetString(firstBy, ""), ChestHistory.Decode(zdo.GetInt(firstDay, 0)), zdo.GetString(lastBy, ""), ChestHistory.Decode(zdo.GetInt(lastDay, 0))); } return value; } private static HistoryWords Words() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown return new HistoryWords(DelveText.ServerText("history_many"), DelveText.ServerText("history_once"), DelveText.ServerText("history_you"), DelveText.ServerText("history_today"), DelveText.ServerText("history_yesterday"), DelveText.ServerText("history_days_ago")); } private void Observe(ZDO zdo, bool localHook = false) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Invalid comparison between Unknown and I4 //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Invalid comparison between Unknown and I4 //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Invalid comparison between Unknown and I4 //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Invalid comparison between Unknown and I4 //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Invalid comparison between Unknown and I4 //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Invalid comparison between Unknown and I4 //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) DelveEngine delveEngine = engine; if (delveEngine == null || !delveEngine.Running || !delveEngine.Active((DelveKind)1)) { return; } exclusions.Update(delveEngine.Settings.Get<string>(Chests.Exclude)); if (!prefabs.TryGetValue(zdo.GetPrefab(), out Container value) || !exclusions.Allows(((Object)value).name) || zdo.GetLong(ZDOVars.s_creator, 0L) != 0L) { return; } ZDOID uid = zdo.m_uid; bool dryRun = delveEngine.DryRun((DelveKind)1); int? num = ChestInventory.ItemCount(zdo.GetByteArray(ZDOVars.s_items, (byte[])null)); bool num2 = zdo.GetInt(ZDOVars.s_inUse, 0) == 1; if (!num2) { open.Remove(uid); announced.Remove(uid); } long owner = zdo.GetOwner(); ZNetPeer val = ((owner != 0L && owner != ZDOMan.GetSessionID()) ? ZNet.instance.GetPeer(owner) : null); bool flag = !ZNet.instance.IsDedicated() && (Object)(object)Player.m_localPlayer != (Object)null; ChestOpener val2 = ChestObservation.OpenerOf(num2, owner, ZDOMan.GetSessionID(), val != null, localHook, flag); long? num3 = (((int)val2 == 1) ? new long?(val.m_uid) : (((int)val2 == 2) ? new long?(ZNet.GetUID()) : ((long?)null))); string text = (((int)val2 == 1) ? val.m_playerName : (((int)val2 == 2) ? Player.m_localPlayer.GetPlayerName() : "")); bool flag2 = num3.HasValue && open.Add(uid); Stamp val3 = WorldClock.Now(ZNet.instance.GetTimeSeconds(), (double)Math.Max(1L, EnvMan.instance.m_dayLengthSec), DateTime.UtcNow); ChestObservationResult val4 = ChestObservation.Observe(num, GetStamp(zdo, dryRun).HasValue, seen.ContainsKey(uid), num3.HasValue, flag2, delveEngine.Settings.Get<bool>(Chests.ResetOpened)); if ((int)val4.Seen == 1) { seen[uid] = val3; } else if ((int)val4.Seen == 2) { seen.Remove(uid); announced.Remove(uid); } if (!num3.HasValue) { return; } int num4 = WorldClock.DayIndex(ZNet.instance.GetTimeSeconds(), (double)Math.Max(1L, EnvMan.instance.m_dayLengthSec)); if (val4.TellHistory && delveEngine.Settings.Get<bool>(Chests.History)) { Record val5 = Read(zdo); string text2 = ChestHistory.Notice(val5, text, num4, Words(), DelveNotices.On); if (text2 != null) { DelveNotices.Tell(num3.Value, text2, (ServerNoticePlacement)0); } pending[uid] = ChestHistory.Opened(val5, text, num4); } if (val4.NoticeKey != null && DelveNotices.On && announced.Add(uid)) { ChestOptions val6 = Options(delveEngine); int num5 = ChestRefill.UnitsLeft(Facts(zdo, dryRun), val3, val6); string key = ChestObservation.Key(num == 0, num5); string text3 = ((num5 > 0) ? DelveText.ServerFormat(key, DelveNotices.Duration(val6.Clock, num5)) : DelveText.ServerText(key)); DelveNotices.Tell(num3.Value, text3, (ServerNoticePlacement)1); } } private static void ObserveLocal(Container container) { DelveChests delveChests = current; if (delveChests == null || (Object)(object)container == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZNet.instance.IsDedicated()) { return; } ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } try { delveChests.Observe(nview.GetZDO(), localHook: true); } catch (Exception ex) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Warning("Delve local chest update: " + ex.GetType().Name + ": " + ex.Message); } } } } public static class DelveKickCommands { [HarmonyPatch(typeof(ZNet), "RPC_Kick")] private static class RemoteKick { private static bool Prefix(ZNet __instance, ZRpc rpc, string user) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) DelveCommand val = DelveCommands.Parse(user); if (val == null || !Enabled) { return true; } try { string hostName = rpc.GetSocket().GetHostName(); bool isAdmin = __instance.ListContainsId(__instance.m_adminList, hostName); bool cheatLockedForCaller = !MeadworksMischief.DecideForPeer(rpc, (MischiefCategory)2).Allowed; ZNetPeer peer = __instance.GetPeer(rpc); if ((int)val.Verb == 3 && peer == null) { __instance.RemotePrint(rpc, "Delve: the surface reset needs your position; join the world first."); return false; } foreach (string item in Run(val, isAdmin, cheatLockedForCaller, peer?.m_playerName ?? "?", (Vector3)((peer != null) ? peer.GetRefPos() : default(Vector3)), peer?.m_uid ?? 0)) { __instance.RemotePrint(rpc, item); if (peer != null) { DelveNotices.Tell(peer.m_uid, item, (ServerNoticePlacement)0); } } } catch (Exception ex) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Error((object)("Delve admin command failed: " + ex.GetType().Name + ": " + ex.Message)); } __instance.RemotePrint(rpc, "Delve: the command failed, see the server log."); } return false; } } [HarmonyPatch(typeof(ZNet), "Kick")] private static class LocalKick { private static bool Prefix(ZNet __instance, string user) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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) if (!__instance.IsServer()) { return true; } DelveCommand val = DelveCommands.Parse(user); if (val == null || !Enabled) { return true; } try { Vector3 position = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : __instance.GetReferencePosition()); foreach (string item in Run(val, isAdmin: true, cheatLockedForCaller: false, "the host", position, ZNet.GetUID(), delegate(string line) { Console instance2 = Console.instance; if (instance2 != null) { instance2.Print(line); } })) { Console instance = Console.instance; if (instance != null) { instance.Print(item); } DelveNotices.Tell(ZNet.GetUID(), item, (ServerNoticePlacement)0); } } catch (Exception ex) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Error((object)("Delve admin command failed: " + ex.GetType().Name + ": " + ex.Message)); } } return false; } } private static bool Enabled { get { ConfigBlockReason reason; if (Plugin.Ready && Plugin.Settings != null && Plugin.Settings.Get<bool>(General.KickCommandFallback)) { return DelveModules.ProductActive(out reason); } return false; } } public static IReadOnlyList<string> Run(DelveCommand command, bool isAdmin, bool cheatLockedForCaller, string caller, Vector3 position = default(Vector3), long callerUid = 0L, Action<string>? print = null) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected I4, but got Unknown //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Invalid comparison between Unknown and I4 //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Invalid comparison between Unknown and I4 //IL_0180: Unknown result type (might be due to invalid IL or missing references) DelveEngine engine = Plugin.Engine; DelveConfig settings = Plugin.Settings; if (!isAdmin) { return new string[1] { DelveCommands.Reply((RegenerateVerdict)1, (IReadOnlyList<DelveKind>)Array.Empty<DelveKind>()) }; } MeadworksLog? log = Plugin.Log; if (log != null) { log.Info("Delve admin command from " + caller + ": " + ((object)command.Verb/*cast due to .constrained prefix*/).ToString() + ((command.Kinds.Count > 0) ? (" " + string.Join(",", command.Kinds)) : "")); } CommandVerb verb = command.Verb; switch (verb - 1) { case 0: return engine?.StatusLines().ToList() ?? new List<string> { "Delve status: not started." }; case 1: if (engine != null && settings != null && engine.Running) { RegenerateVerdict val = settings.RegenerateFor(isAdmin: true, cheatLockedForCaller); if ((int)val == 3) { MeadworksBlockedOptions.TryUse("com.meadworks.delve", ((DelveSetting)Cheats.RegenerateWithoutWaiting).Id, (ConfigScope)2, (string)null, true); } bool skipDelay = (int)DelveCommands.Mode(val) == 0; IReadOnlyList<DelveKind> readOnlyList = engine.Request(command.Kinds.Where(engine.Active), skipDelay); engine.ConsiderClaimClear(command.Kinds, readOnlyList, skipDelay); if (readOnlyList.Count != 0) { return new string[1] { DelveCommands.Reply(val, readOnlyList) }; } return new string[1] { "Delve: none of those kinds is on (switched off or stepped aside)." }; } return new string[1] { "Delve: the regeneration engine is not running on this server." }; case 2: return DelveSurface.Begin(position, command.Scope, callerUid, caller, print); default: return DelveCommands.Help(); } } } public sealed class DelveConfig { private const string Product = "com.meadworks.delve"; private readonly ConfigFile file; private readonly Dictionary<DelveSetting, ConfigEntryBase> entries = new Dictionary<DelveSetting, ConfigEntryBase>(); public ConfigFile File => file; private DelveConfig(ConfigFile file) { this.file = file; bool saveOnConfigSet = file.SaveOnConfigSet; file.SaveOnConfigSet = false; try { foreach (DelveSetting item in DelveSettings.All) { BindSetting(item); } } finally { file.SaveOnConfigSet = saveOnConfigSet; } file.Save(); } public static DelveConfig Bind(ConfigFile file, MeadworksLog log) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (file == null) { throw new ArgumentNullException("file"); } if (log == null) { throw new ArgumentNullException("log"); } DelveConfig delveConfig = new DelveConfig(file); foreach (KeyValuePair<DelveSetting, ConfigEntryBase> item in delveConfig.entries.Where<KeyValuePair<DelveSetting, ConfigEntryBase>>((KeyValuePair<DelveSetting, ConfigEntryBase> p) => (int)p.Key.Scope != 0 && !p.Key.Hidden)) { MeadworksBlockedOptions.Track("com.meadworks.delve", item.Value, Map(item.Key.Scope), true); } log.Verbose("Configuration bound: " + DelveSettings.Visible.Count + " settings (ServerAuthoritative plus one cheat)."); return delveConfig; } public static ConfigScope Map(SettingScope scope) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown return (ConfigScope)((int)scope switch { 0 => 0, 1 => 1, 2 => 2, _ => throw new ArgumentException("Unknown scope.", "scope"), }); } public ConfigEntryBase Entry(DelveSetting setting) { if (!entries.TryGetValue(setting ?? throw new ArgumentNullException("setting"), out ConfigEntryBase value)) { throw new ArgumentException("Not a Delve setting.", "setting"); } return value; } public T Get<T>(DelveSetting<T> setting) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 ConfigEntry<T> val = (ConfigEntry<T>)(object)Entry((DelveSetting)(object)setting); if ((int)((DelveSetting)setting).Scope == 2) { return DelveCheats.Effective<T>(setting, CheatLocked(), val.Value); } if ((int)((DelveSetting)setting).Scope == 1) { return setting.Normalize(MeadworksConfig.Effective<T>(val, "com.meadworks.delve", (ConfigScope)1, setting.Default, true)); } return setting.Normalize(val.Value); } public double Delay(DelveKind kind) { //IL_0000: 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) KindSettings val = DelveSettings.Kind(kind); return ClockRules.Clamp(Get<ClockKind>(val.Clock), (double)Get<float>(val.Delay), 30.0); } public RegenerateVerdict Regenerate(bool isAdmin) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 DelveSetting<bool> regenerateWithoutWaiting = Cheats.RegenerateWithoutWaiting; RegenerateVerdict val = DelveCheats.Regenerate(isAdmin, CheatLocked(), ((ConfigEntry<bool>)(object)Entry((DelveSetting)(object)regenerateWithoutWaiting)).Value); if ((int)val == 3) { MeadworksBlockedOptions.TryUse("com.meadworks.delve", ((DelveSetting)regenerateWithoutWaiting).Id, (ConfigScope)2, (string)null, true); } return val; } public RegenerateVerdict RegenerateFor(bool isAdmin, bool lockedForCaller) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) DelveSetting<bool> regenerateWithoutWaiting = Cheats.RegenerateWithoutWaiting; return DelveCheats.Regenerate(isAdmin, lockedForCaller || CheatLocked(), ((ConfigEntry<bool>)(object)Entry((DelveSetting)(object)regenerateWithoutWaiting)).Value); } private static bool CheatLocked() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 return (int)MeadworksAuthority.Decide("com.meadworks.delve", (ConfigScope)2, true).Source == 2; } public void Shutdown() { foreach (ConfigEntryBase value in entries.Values) { MeadworksBlockedOptions.Untrack(value); } entries.Clear(); } private void BindSetting(DelveSetting setting) { ConfigEntryBase value; if (!(setting is DelveSetting<bool> setting2)) { if (!(setting is DelveSetting<int> val)) { if (!(setting is DelveSetting<float> val2)) { if (!(setting is DelveSetting<string> setting3)) { if (!(setting is DelveSetting<ClockKind> setting4)) { throw new NotSupportedException(setting.Id + ": unsupported value type " + setting.ValueType.Name + "."); } value = (ConfigEntryBase)(object)Bind<ClockKind>(setting4, null); } else { value = (ConfigEntryBase)(object)Bind<string>(setting3, null); } } else { value = (ConfigEntryBase)(object)Bind<float>(val2, (AcceptableValueBase?)(object)new AcceptableValueRange<float>((float)((DelveSetting)val2).Minimum, (float)((DelveSetting)val2).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<int>(val, (AcceptableValueBase?)(object)new AcceptableValueRange<int>((int)((DelveSetting)val).Minimum, (int)((DelveSetting)val).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<bool>(setting2, null); } entries.Add(setting, value); } private ConfigDescription Describe(DelveSetting setting, AcceptableValueBase? acceptable) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown ConfigDependency val = null; if ((object)setting == Rooms.MinRooms || (object)setting == Rooms.MaxRooms) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Rooms.Enabled)), (Func<string>)(() => "Requires " + ((DelveSetting)Rooms.Enabled).Section + ": " + ((DelveSetting)Rooms.Enabled).Key + "."), (Func<bool>)null); } if ((object)setting == Rooms.ForestCryptMin || (object)setting == Rooms.ForestCryptMax) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Rooms.Enabled) && Get<bool>(Rooms.ForestCryptOverride)), (Func<string>)(() => "Requires Rooms: Enabled and " + ((DelveSetting)Rooms.ForestCryptOverride).Key + "."), (Func<bool>)null); } if ((object)setting == Rooms.SunkenCryptMin || (object)setting == Rooms.SunkenCryptMax) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Rooms.Enabled) && Get<bool>(Rooms.SunkenCryptOverride)), (Func<string>)(() => "Requires Rooms: Enabled and " + ((DelveSetting)Rooms.SunkenCryptOverride).Key + "."), (Func<bool>)null); } if ((object)setting == Rooms.CaveMin || (object)setting == Rooms.CaveMax) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Rooms.Enabled) && Get<bool>(Rooms.CaveOverride)), (Func<string>)(() => "Requires Rooms: Enabled and " + ((DelveSetting)Rooms.CaveOverride).Key + "."), (Func<bool>)null); } return new ConfigDescription(setting.Description, acceptable, (val == null) ? Array.Empty<object>() : new object[1] { val }); } private ConfigEntry<T> Bind<T>(DelveSetting<T> setting, AcceptableValueBase? acceptable) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if ((int)((DelveSetting)setting).Scope == 2) { return MeadworksConfig.BindCheat<T>(file, ((DelveSetting)setting).Section, ((DelveSetting)setting).Key, setting.Safe, ((DelveSetting)setting).Description, (MischiefCategory)2, acceptable); } if (((DelveSetting)setting).Hidden) { return MeadworksConfig.BindHidden<T>(file, ((DelveSetting)setting).Section, ((DelveSetting)setting).Key, setting.Default, ((DelveSetting)setting).Description, Map(((DelveSetting)setting).Scope), acceptable); } return MeadworksConfig.Bind<T>(file, ((DelveSetting)setting).Section, ((DelveSetting)setting).Key, setting.Default, Describe((DelveSetting)(object)setting, acceptable), Map(((DelveSetting)setting).Scope)); } } public static class DelveBases { private static readonly Dictionary<int, (float Radius, bool Ward)> areas = new Dictionary<int, (float, bool)>(); private static float largest; public static void Index() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) areas.Clear(); largest = 0f; if ((Object)(object)ZNetScene.instance == (Object)null) { return; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } float num = 0f; bool item = false; PrivateArea componentInChildren = prefab.GetComponentInChildren<PrivateArea>(true); if ((Object)(object)componentInChildren != (Object)null) { num = componentInChildren.m_radius; item = true; } EffectArea[] componentsInChildren = prefab.GetComponentsInChildren<EffectArea>(true); foreach (EffectArea val in componentsInChildren) { if ((val.m_type & 4) != 0) { SphereCollider component = ((Component)val).GetComponent<SphereCollider>(); if ((Object)(object)component != (Object)null) { num = Mathf.Max(num, component.radius * ((Component)val).transform.lossyScale.x); } } } if (!(num <= 0f)) { areas[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = (num, item); largest = Mathf.Max(largest, num); } } } public static void Clear() { areas.Clear(); largest = 0f; } public static List<Area> Near(Vector3 spot) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) List<Area> list = new List<Area>(); if (areas.Count == 0 || ZDOMan.instance == null) { return list; } foreach (ZDO item in DelveWorld.ObjectsNear(spot, largest + 8f)) { if (areas.TryGetValue(item.GetPrefab(), out (float, bool) value) && item.GetLong(ZDOVars.s_creator, 0L) != 0L && (!value.Item2 || item.GetBool(ZDOVars.s_enabled, false))) { list.Add(new Area(DelveWorld.ToVec(item.GetPosition()), value.Item1)); } } return list; } } internal sealed class DelveCheckTiming { private readonly long started = Stopwatch.GetTimestamp(); private readonly int gc0 = GC.CollectionCount(0); private readonly int gc1 = GC.CollectionCount(1); private readonly int gc2 = GC.CollectionCount(2); private readonly List<(string Name, DelveKind? Kind, long Ticks, int Scanned, int Candidates, int Chars)> stages = new List<(string, DelveKind?, long, int, int, int)>(); private readonly List<(DelveKind Kind, int Tracked, int Waiting, int Deferred, int Acted)> reports = new List<(DelveKind, int, int, int, int)>(); internal static DelveCheckTiming? Begin(bool enabled) { if (!enabled) { return null; } return new DelveCheckTiming(); } internal static long Start(DelveCheckTiming? timing) { if (timing != null) { return Stopwatch.GetTimestamp(); } return 0L; } internal void Stage(string name, long start, DelveKind? kind = null, int scanned = 0, int candidates = 0, int chars = 0) { long item = Stopwatch.GetTimestamp() - start; stages.Add((name, kind, item, scanned, candidates, chars)); } internal void Report(DelveKind kind, KindReport report) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) reports.Add((kind, report.Tracked, report.Waiting, report.Deferred, report.Acted)); } internal string Finish(int frame, int thread, bool dawn, double day, int worldZdos, IEnumerable<DelveKind> sweeping, int budgetLeft, bool completed, bool sidecarDirty, int serviceFailures) { //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) long num = Stopwatch.GetTimestamp() - started; int value = GC.CollectionCount(0) - gc0; int value2 = GC.CollectionCount(1) - gc1; int value3 = GC.CollectionCount(2) - gc2; StringBuilder stringBuilder = new StringBuilder("stall-timing event=delve-check"); stringBuilder.Append(" frame=").Append(frame).Append(" thread=") .Append(thread) .Append(" dawn=") .Append(dawn) .Append(" day=") .Append(day.ToString("F6", CultureInfo.InvariantCulture)) .Append(" world_zdos=") .Append(worldZdos) .Append(" ticks=") .Append(num) .Append(" frequency=") .Append(Stopwatch.Frequency) .Append(" ms=") .Append(((double)num * 1000.0 / (double)Stopwatch.Frequency).ToString("F3", CultureInfo.InvariantCulture)) .Append(" gc=") .Append(value) .Append('/') .Append(value2) .Append('/') .Append(value3) .Append(" completed=") .Append(completed) .Append(" service_failures=") .Append(serviceFailures) .Append(" budget_left=") .Append(budgetLeft) .Append(" sidecar_dirty=") .Append(sidecarDirty) .Append(" sweeping=["); bool flag = true; foreach (DelveKind item in sweeping) { if (!flag) { stringBuilder.Append(','); } stringBuilder.Append(item); flag = false; } stringBuilder.Append("] stages=["); for (int i = 0; i < stages.Count; i++) { if (i > 0) { stringBuilder.Append(';'); } (string, DelveKind?, long, int, int, int) tuple = stages[i]; stringBuilder.Append(tuple.Item1); if (tuple.Item2.HasValue) { stringBuilder.Append('.').Append(tuple.Item2.Value); } stringBuilder.Append(":ticks=").Append(tuple.Item3).Append(",scanned=") .Append(tuple.Item4) .Append(",candidates=") .Append(tuple.Item5) .Append(",chars=") .Append(tuple.Item6); } stringBuilder.Append("] reports=["); for (int j = 0; j < reports.Count; j++) { if (j > 0) { stringBuilder.Append(';'); } (DelveKind, int, int, int, int) tuple2 = reports[j]; stringBuilder.Append(tuple2.Item1).Append(":tracked=").Append(tuple2.Item2) .Append(",waiting=") .Append(tuple2.Item3) .Append(",deferred=") .Append(tuple2.Item4) .Append(",acted=") .Append(tuple2.Item5); } return stringBuilder.Append(']').ToString(); } } public static class DelveDungeonIndex { public const float InteriorHeight = 3000f; private static readonly Dictionary<int, string> generatorNames = new Dictionary<int, string>(); private static readonly List<(ZDO Zdo, string Name)> generators = new List<(ZDO, string)>(); private static DungeonsFile modes = DungeonsFile.Empty; private static DateTime modesStamp = DateTime.MinValue; private static int builtFrame = -1; public static string ModesPath => Path.Combine(DelveImport.DataDirectory, "Delve.Dungeons.yml"); internal static IReadOnlyList<(ZDO Zdo, string Name)> KnownGenerators => generators; public static void Index() { generatorNames.Clear(); generators.Clear(); builtFrame = -1; if ((Object)(object)ZNetScene.instance == (Object)null) { return; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<DungeonGenerator>() != (Object)null) { generatorNames[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = ((Object)prefab).name; } } } public static void ListAll() { try { string modesPath = ModesPath; DungeonsFile val = (File.Exists(modesPath) ? DungeonsFile.Parse(File.ReadAllText(modesPath)) : DungeonsFile.Empty); if (val.Errors.Count > 0) { return; } Dictionary<string, DungeonMode> dictionary = DungeonsFile.WithMissing(val.Modes, (IEnumerable<string>)generatorNames.Values); if (dictionary != null) { Directory.CreateDirectory(DelveImport.DataDirectory); File.WriteAllText(modesPath, DungeonsFile.Write((IReadOnlyDictionary<string, DungeonMode>)dictionary)); MeadworksLog? log = Plugin.Log; if (log != null) { log.Info("Delve Delve.Dungeons.yml: listed " + (dictionary.Count - val.Modes.Count) + " more dungeon types."); } } } catch (Exception ex) { MeadworksLog? log2 = Plugin.Log; if (log2 != null) { log2.Warning("Delve Delve.Dungeons.yml not updated: " + ex.GetType().Name + ": " + ex.Message); } } } public static bool IsGenerator(int prefab) { return generatorNames.ContainsKey(prefab); } public static string? NameOf(int prefab) { if (!generatorNames.TryGetValue(prefab, out string value)) { return null; } return value; } public static IReadOnlyList<(ZDO Zdo, string Name)> Generators() { if (builtFrame == Time.frameCount || ZDOMan.instance == null) { return generators; } builtFrame = Time.frameCount; generators.Clear(); foreach (ZDO value2 in ZDOMan.instance.m_objectsByID.Values) { if (generatorNames.TryGetValue(value2.GetPrefab(), out string value)) { generators.Add((value2, value)); } } return generators; } public static (ZDO Zdo, string Name)? GeneratorOf(Vector3 point) { //IL_0000: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (point.y < 3000f) { return null; } (ZDO, string)? result = null; float num = 300f; foreach (var item in Generators()) { Vector3 position = item.Zdo.GetPosition(); Vector2 val = new Vector2(position.x - point.x, position.z - point.z); float magnitude = ((Vector2)(ref val)).magnitude; if (magnitude < num && Mathf.Abs(position.y - point.y) < 500f) { result = item; num = magnitude; } } return result; } public static DungeonMode ModeOf(string generator) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) try { string modesPath = ModesPath; DateTime dateTime = (File.Exists(modesPath) ? File.GetLastWriteTimeUtc(modesPath) : DateTime.MinValue); if (dateTime != modesStamp) { modesStamp = dateTime; modes = ((dateTime == DateTime.MinValue) ? DungeonsFile.Empty : DungeonsFile.Parse(File.ReadAllText(modesPath))); foreach (string error in modes.Errors) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Warning("Delve Delve.Dungeons.yml: " + error); } } } } catch (Exception ex) { MeadworksLog? log2 = Plugin.Log; if (log2 != null) { log2.Warning("Delve Delve.Dungeons.yml unreadable: " + ex.GetType().Name + "; keeping the modes read before."); } } return modes.ModeOf(generator); } public static bool Allows(Vector3 point, DelveKind kind) { //IL_0000: 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_0021: Unknown result type (might be due to invalid IL or missing references) (ZDO, string)? tuple = GeneratorOf(point); if (tuple.HasValue) { return DungeonModes.Allows(ModeOf(tuple.Value.Item2), kind); } return true; } public static void Clear() { generatorNames.Clear(); generators.Clear(); builtFrame = -1; } } public interface IDelveService { IReadOnlyList<DelveKind> Kinds { get; } void Start(DelveEngine engine); void Check(DelveEngine engine, ISet<DelveKind> sweeping); void Stop(DelveEngine engine); } public sealed class DelveEngine { private readonly int ownerThread = Thread.CurrentThread.ManagedThreadId; private readonly List<IDelveService> services = new List<IDelveService>(); private readonly HashSet<DelveKind> carrying = new HashSet<DelveKind>(); private readonly HashSet<DelveKind> evaluateNow = new HashSet<DelveKind>(); private readonly ClaimClearWatch claimClear = new ClaimClearWatch(); private bool claimClearReady; private object? world; private float nextCheck; private double lastSeconds = double.NaN; private string? sidecarPath; private bool sidecarDirty; internal DelveCheckTiming? CheckTiming { get; private set; } public MeadworksLog Log { get; } public EngineStatus Status { get; } public Sidecar Sidecar { get; private set; } = new Sidecar(); public bool Running { get; private set; } public Stamp Now { get; private set; } public double DayLengthSeconds { get; private set; } = 1800.0; public IReadOnlyList<DelvePlayer> Players { get; private set; } = Array.Empty<DelvePlayer>(); public int Budget { get; set; } public DelveConfig Settings => Plugin.Settings ?? throw new InvalidOperationException("Delve settings are not bound."); public DelveEngine(MeadworksLog log) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown Log = log ?? throw new ArgumentNullException("log"); Status = new EngineStatus(20); } public void Add(IDelveService service) { services.Add(service ?? throw new ArgumentNullException("service")); } public T? Service<T>() where T : class, IDelveService { return services.OfType<T>().FirstOrDefault(); } public bool Active(DelveKind kind) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return DelveModules.Active(kind); } public bool DryRun(DelveKind kind) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!Settings.Get<bool>(General.DryRunAll)) { return Settings.Get<bool>(DelveSettings.Kind(kind).DryRun); } return true; } public (ClockKind Clock, double Delay) ClockOf(DelveKind kind) { //IL_0006: 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_001c: Unknown result type (might be due to invalid IL or missing references) return (Clock: Settings.Get<ClockKind>(DelveSettings.Kind(kind).Clock), Delay: Settings.Delay(kind)); } public bool RegenerateNow(DelveKind kind) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) return Sidecar.RegenerateNow.Contains(kind); } public void Swept(DelveKind kind, int deferred) { //IL_000b: 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_0024: 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) if (Sidecar.RegenerateNow.Remove(kind)) { sidecarDirty = true; } if (deferred > 0) { carrying.Add(kind); } else { carrying.Remove(kind); } if (claimClear.NoteSwept(kind, deferred)) { claimClearReady = true; } } public void ConsiderClaimClear(IEnumerable<DelveKind> asked, IReadOnlyCollection<DelveKind> accepted, bool skipDelay) { bool flag = Plugin.Settings != null && Plugin.Settings.Get<bool>(General.DryRunAll); claimClear.Request(BestiaryClaimReset.CoversEveryKind(asked), flag, skipDelay, accepted); } public void NoteClaimFailure(DelveKind kind) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) claimClear.NoteFailure(kind); } public void Report(DelveKind kind, KindReport report) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Status.Set(kind, report); CheckTiming?.Report(kind, report); } public unsafe void Record(DelveKind kind, string what, bool dryRun) { //IL_0006: 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) Status.Record(kind, what, dryRun, Now); if (dryRun) { Log.Info("Delve dry run: would have " + what); } else { Log.Verbose("Delve " + ((object)(*(DelveKind*)(&kind))/*cast due to .constrained prefix*/).ToString() + ": " + what); } } public void MarkSidecarDirty() { sidecarDirty = true; } public IReadOnlyList<DelveKind> Request(IEnumerable<DelveKind> kinds, bool skipDelay) { //IL_0017: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) List<DelveKind> list = new List<DelveKind>(); foreach (DelveKind kind in kinds) { if (!services.Any((IDelveService s) => s.Kinds.Contains(kind))) { continue; } if (skipDelay) { if (Sidecar.RegenerateNow.Add(kind)) { sidecarDirty = true; } } else { evaluateNow.Add(kind); } list.Add(kind); } nextCheck = 0f; return list; } private void FinishClaimClear() { if (claimClearReady) { claimClearReady = false; BestiaryClaims.ClearFinished(); } } public void Update() { object obj = CurrentWorld(); if (obj == null || !DelveModules.ProductActive(out var _)) { if (Running) { Stop(); } return; } if (!Running || obj != world) { if (Running) { Stop(); } Start(obj); } nextCheck = Mathf.Min(nextCheck, Time.unscaledTime + (float)Settings.Get<int>(General.CheckIntervalSeconds)); if (!(Time.unscaledTime < nextCheck)) { nextCheck = Time.unscaledTime + (float)Settings.Get<int>(General.CheckIntervalSeconds); Check(); } } public void Stop() { claimClear.Cancel(); claimClearReady = false; foreach (IDelveService service in services) { try { service.Stop(this); } catch (Exception ex) { Log.Warning("Delve stop " + service.GetType().Name + ": " + ex.GetType().Name + ": " + ex.Message); } } SaveSidecar(); DelveBases.Clear(); DelveDungeonIndex.Clear(); Running = false; world = null; lastSeconds = double.NaN; carrying.Clear(); evaluateNow.Clear(); } private static object? CurrentWorld() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null) { return null; } if (ZNet.World == null) { return null; } return ZNet.World; } private void Start(object current) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) world = current; Running = true; DayLengthSeconds = Math.Max(1L, EnvMan.instance.m_dayLengthSec); Now = WorldClock.Now(ZNet.instance.GetTimeSeconds(), DayLengthSeconds, DateTime.UtcNow); Status.Resize(Settings.Get<int>(General.StatusReportLines)); LoadSidecar(); DelveBases.Index(); DelveDungeonIndex.Index(); DelveDungeonIndex.ListAll(); foreach (IDelveService service in services) { try { service.Start(this); } catch (Exception ex) { Log.Error((object)("Delve start " + service.GetType().Name + ": " + ex.GetType().Name + ": " + ex.Message)); } } Log.Info("Delve regeneration engine started on the authority" + (ZNet.instance.IsDedicated() ? " (dedicated server)." : ".")); } private void Check() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) CheckTiming = DelveCheckTiming.Begin(Log.IsEnabled((LogSeverity)4)); bool flag = false; bool completed = false; int num = 0; HashSet<DelveKind> hashSet = null; try { double timeSeconds = ZNet.instance.GetTimeSeconds(); DayLengthSeconds = Math.Max(1L, EnvMan.instance.m_dayLengthSec); Now = WorldClock.Now(timeSeconds, DayLengthSeconds, DateTime.UtcNow); flag = !double.IsNaN(lastSeconds) && WorldClock.DawnBetween(lastSeconds, timeSeconds, DayLengthSeconds); lastSeconds = timeSeconds; Players = DelveWorld.Players(); Budget = Settings.Get<int>(General.MaxActionsPerTick); Status.Resize(Settings.Get<int>(General.StatusReportLines)); claimClear.BeginCheck(); claimClearReady = false; hashSet = new HashSet<DelveKind>(); foreach (DelveKind value in Enum.GetValues(typeof(DelveKind))) { if (Active(value) && (carrying.Contains(value) || evaluateNow.Contains(value) || WorldClock.ShouldSweep(ClockOf(value).Clock, flag, RegenerateNow(value)))) { hashSet.Add(value); } } evaluateNow.Clear(); foreach (IDelveService service in services) { long start = DelveCheckTiming.Start(CheckTiming); try { service.Check(this, hashSet); } catch (Exception ex) { num++; Log.Error((object)("Delve check " + service.GetType().Name + ": " + ex.GetType().Name + ": " + ex.Message)); } finally { CheckTiming?.Stage(service.GetType().Name, start); } } FinishClaimClear(); foreach (DelveKind value2 in Enum.GetValues(typeof(DelveKind))) { if (!Active(value2)) { Status.Set(value2, new KindReport(0, 0, 0, 0, (IReadOnlyDictionary<GuardResult, int>)null, false, false, InactiveReason(value2))); } } if (sidecarDirty) { SaveSidecar(); } completed = true; } finally { DelveCheckTiming checkTiming = CheckTiming; CheckTiming = null; if (checkTiming != null) { try { MeadworksLog log = Log; int frameCount = Time.frameCount; int managedThreadId = Thread.CurrentThread.ManagedThreadId; bool dawn = flag; Stamp now = Now; double day = ((Stamp)(ref now)).Day; int worldZdos = ZDOMan.instance?.m_objectsByID.Count ?? 0; IEnumerable<DelveKind> enumerable = hashSet; log.Debug(checkTiming.Finish(frameCount, managedThreadId, dawn, day, worldZdos, enumerable ?? Array.Empty<DelveKind>(), Budget, completed, sidecarDirty, num)); } catch (Exception) { } } } } private static string InactiveReason(DelveKind kind) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) DelveModule val = ModuleStepAside.ModuleOf(kind); if (DelveModules.StepAside.SteppedAside(val)) { return "stepped aside"; } return "switched off"; } public IEnumerable<string> StatusLines() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) bool flag = Plugin.Settings != null && Settings.Get<bool>(General.DryRunAll); if (!Running) { return new string[1] { "Delve status: the engine is not running (no world on this authority)." }; } return Status.Lines(Now, flag, (Func<GuardResult, string>)((GuardResult g) => DelveText.Text(DelveTextKeys.Guard(g)))); } private void LoadSidecar() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) sidecarPath = null; Sidecar = new Sidecar(); try { World val = ZNet.World; string worldsSaveRootPath = SaveSystem.GetWorldsSaveRootPath((FileSource)(((int)val.m_fileSource == 4) ? 2 : ((int)val.m_fileSource))); sidecarPath = Path.Combine(worldsSaveRootPath, val.m_name + ".delve.json"); string text = (File.Exists(sidecarPath) ? File.ReadAllText(sidecarPath) : null); List<string> list = default(List<string>); Sidecar = Sidecar.Parse(text, ref list); foreach (string item in list) { Log.Warning("Delve " + Path.GetFileName(sidecarPath) + ": " + item + "."); } } catch (Exception ex) { Log.Warning("Delve sidecar unavailable: " + ex.GetType().Name + ": " + ex.Message + "; world object spots are kept in memory only."); } sidecarDirty = false; } public void SaveSidecar() { if (sidecarPath == null || !sidecarDirty || Sidecar.ReadOnly) { return; } long start = DelveCheckTiming.Start(CheckTiming); try { string text = sidecarPath + ".tmp"; long start2 = DelveCheckTiming.Start(CheckTiming); string text2; try { text2 = Sidecar.Write(); } finally { CheckTiming?.Stage("sidecar_serialize", start2); } long start3 = DelveCheckTiming.Start(CheckTiming); try { File.WriteAllText(text, text2); } finally { DelveCheckTiming? checkTiming = CheckTiming; if (checkTiming != null) { int length = text2.Length; checkTiming.Stage("sidecar_write", start3, null, 0, 0, length); } } long start4 = DelveCheckTiming.Start(CheckTiming); try { if (File.Exists(sidecarPath)) { File.Delete(sidecarPath); } File.Move(text, sidecarPath); } finally { CheckTiming?.Stage("sidecar_publish", start4); } sidecarDirty = false; } catch (Exception ex) { Log.Warning("Delve sidecar write failed: " + ex.GetType().Name + ": " + ex.Message); } finally { CheckTiming?.Stage("sidecar_save", start); } } public GuardInput Guard(Vector3 center, float playerRadius, float clearance, bool held = false, Box? interior = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown GuardInput val = new GuardInput(DelveWorld.ToVec(center), playerRadius) { Interior = interior, Players = Players.Select((DelvePlayer p) => DelveWorld.ToVec(p.Position)).ToList(), HeldByPlayer = held, Clearance = clearance }; IReadOnlyList<Vec3> built; if (!(clearance > 0f)) { IReadOnlyList<Vec3> readOnlyList = Array.Empty<Vec3>(); built = readOnlyList; } else { IReadOnlyList<Vec3> readOnlyList = DelveWorld.PlayerBuiltNear(center, clearance); built = readOnlyList; } val.Built = built; val.BaseGuard = Settings.Get<bool>(General.PlayerBaseGuard); val.Bases = (Settings.Get<bool>(General.PlayerBaseGuard) ? DelveBases.Near(center) : new List<Area>()); return val; } public SchedulerOptions SchedulerOptions() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown return new SchedulerOptions(Math.Max(1, Budget), Settings.Get<bool>(General.DryRunAll), (Func<DelveKind, ValueTuple<ClockKind, double>>)((DelveKind k) => ClockOf(k)), (Func<DelveKind, bool>)((DelveKind k) => Settings.Get<bool>(DelveSettings.Kind(k).DryRun))); } private bool CanReadSites() { ConfigBlockReason reason; if (Thread.CurrentThread.ManagedThreadId == ownerThread && Plugin.Ready && Plugin.Engine == this && Running && Plugin.Session.AuthorityReady && CurrentWorld() == world) { return DelveModules.ProductActive(out reason); } return false; } public IReadOnlyList<(string Id, DelveKind Kind, string Prefab, Vec3 Position, (ClockKind Clock, double? Remaining, bool Due, bool DryRun, bool Requested) Timing)> WorldSites() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!CanReadSites()) { return Array.Empty<(string, DelveKind, string, Vec3, (ClockKind, double?, bool, bool, bool))>(); } DelveWorldObjects delveWorldObjects = Service<DelveWorldObjects>(); if (delveWorldObjects == null) { return Array.Empty<(string, DelveKind, string, Vec3, (ClockKind, double?, bool, bool, bool))>(); } Stamp now = WorldClock.Now(ZNet.instance.GetTimeSeconds(), (double)Math.Max(1L, EnvMan.instance.m_dayLengthSec), DateTime.UtcNow); return (from s in delveWorldObjects.ReadSites(this, now) orderby s.Kind select s).ThenBy<(string, DelveKind, string, Vec3, (ClockKind, double?, bool, bool, bool)), string>(((string Id, DelveKind Kind, string Prefab, Vec3 Position, (ClockKind Clock, double? Remaining, bool Due, bool DryRun, bool Requested) Timing) s) => s.Id, StringComparer.Ordinal).Take(10000).ToArray(); } public bool RequestDungeon(string siteId, bool skipDelay, Func<bool> stillAllowed) { if (stillAllowed == null || !CanReadSites() || !Active((DelveKind)0)) { return false; } try { if (!stillAllowed()) { return false; } } catch { return false; } DelveFullReset delveFullReset = Service<DelveFullReset>(); if (delveFullReset == null || !delveFullReset.Request(this, siteId, skipDelay, stillAllowed)) { return false; } nextCheck = 0f; return true; } public IReadOnlyList<DungeonSiteReport> DungeonSites() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (!CanReadSites()) { return Array.Empty<DungeonSiteReport>(); } Stamp now = WorldClock.Now(ZNet.instance.GetTimeSeconds(), (double)Math.Max(1L, EnvMan.instance.m_dayLengthSec), DateTime.UtcNow); List<DungeonSiteReport> list = new List<DungeonSiteReport>(); DelveDungeons delveDungeons = Service<DelveDungeons>(); if (delveDungeons != null) { list.AddRange(delveDungeons.ReadSites(this, now)); } DelveFullReset delveFullReset = Service<DelveFullReset>(); if (delveFullReset != null) { list.AddRange(delveFullReset.ReadSites(this, now)); } return list.OrderBy((DungeonSiteReport s) => s.Kind).ThenBy<DungeonSiteReport, string>((DungeonSiteReport s) => s.Id, StringComparer.Ordinal).ToArray(); } } public static class DelveNotices { public static bool On { get { if (Plugin.Settings != null) { return Plugin.Settings.Get<bool>(Notices.Notify); } return false; } } public static string Duration(ClockKind clock, int units) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) var (text, num) = NoticeText.Duration(clock, units); if (num != 1 || (!(text == "days_one") && !(text == "hours_one"))) { return DelveText.ServerFormat(text, num); } return DelveText.ServerText(text); } public static bool Tell(DelvePlayer player, string text, ServerNoticePlacement placement = (ServerNoticePlacement)1) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (On) { return Tell(player.Uid, text, placement); } return false; } public static bool Tell(long peer, string text, ServerNoticePlacement placement) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(text)) { return MeadworksServerNotices.TrySend("com.meadworks.delve", peer, text, placement); } return false; } } public static class DelveServices { public static DelveEngine Create(MeadworksLog log) { DelveEngine delveEngine = new DelveEngine(log); delveEngine.Add(new DelveChests()); delveEngine.Add(new DelveDungeons()); delveEngine.Add(new DelveKallRoom()); delveEngine.Add(new DelveWorldObjects()); delveEngine.Add(new DelveSpawners()); delveEngine.Add(new DelveFullReset()); return delveEngine; } } public readonly struct DelvePlayer { public long Uid { get; } public string Name { get; } public Vector3 Position { get; } public ZNetPeer? Peer { get; } public DelvePlayer(long uid, string name, Vector3 position, ZNetPeer? peer) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Uid = uid; Name = name; Position = position; Peer = peer; } } public static class DelveWorld { private static readonly List<ZDO> nearby = new List<ZDO>(); public static Vec3 ToVec(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vec3(v.x, v.y, v.z); } public static Vector3 ToVector(Vec3 v) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(((Vec3)(ref v)).X, ((Vec3)(ref v)).Y, ((Vec3)(ref v)).Z); } public static ZDO? Create(int prefabHash, Vector3 position, Vector3 eulerRotation) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_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) if ((Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash); ZNetView val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ZNetView>() : null); if ((Object)(object)val == (Object)null) { return null; } ZDO obj = ZDOMan.instance.CreateNewZDO(position, prefabHash); obj.Persistent = val.m_persistent; obj.Type = val.m_type; obj.Distant = val.m_distant; obj.SetPrefab(prefabHash); obj.SetRotation(Quaternion.Euler(eulerRotation)); return obj; } public static void Remove(ZDO zdo) { if (zdo != null && ZDOMan.instance != null) { zdo.SetOwner(ZDOMan.GetSessionID()); ZDOMan.instance.DestroyZDO(zdo); } } public static bool HeldByPlayer(ZDO? zdo) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || (Object)(object)ZNet.instance == (Object)null) { return false; } long owner = zdo.GetOwner(); bool flag = zdo.HasOwner(); bool flag2 = owner == ZDOMan.GetSessionID(); if (!flag || flag2) { return WorldRules.HeldByPlayer(flag, flag2, false, false); } ZNetPeer peer = ZNet.instance.GetPeer(owner); return WorldRules.HeldByPlayer(true, false, peer != null, peer != null && ZNetScene.InActiveArea(zdo.GetPosition(), peer.GetRefPos())); } public static List<DelvePlayer> Players() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) List<DelvePlayer> list = new List<DelvePlayer>(); ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return list; } foreach (ZNetPeer peer in instance.GetPeers()) { if (peer != null && peer.IsReady()) { list.Add(new DelvePlayer(peer.m_uid, peer.m_playerName ?? "", peer.GetRefPos(), peer)); } } if (!instance.IsDedicated() && (Object)(object)Player.m_localPlayer != (Object)null) { list.Add(new DelvePlayer(ZNet.GetUID(), Player.m_localPlayer.GetPlayerName() ?? "", ((Component)Player.m_localPlayer).transform.position, null)); } return list; } public static List<Vec3> PlayerBuiltNear(Vector3 spot, float radius) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) List<Vec3> list = new List<Vec3>(); if (radius <= 0f || ZDOMan.instance == null) { return list; } foreach (ZDO item in ObjectsNear(spot, radius)) { Vector3 position = item.GetPosition(); if (item.GetLong(ZDOVars.s_creator, 0L) != 0L) { Vector2 val = new Vector2(position.x - spot.x, position.z - spot.z); if (((Vector2)(ref val)).magnitude < radius) { list.Add(ToVec(position)); } } } return list; } public static List<ZDO> ObjectsNear(Vector3 spot, float radius) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) nearby.Clear(); if (ZDOMan.instance == null) { return new List<ZDO>(); } int num = Mathf.Max(1, Mathf.CeilToInt(radius / 64f)); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(spot), new SimulationDistance(num, 0, true), nearby, (List<ZDO>)null); return new List<ZDO>(nearby); } } internal static class BestiaryClaims { public static void ClearFinished() { if (Plugin.Settings != null && Plugin.Settings.Get<bool>(General.DryRunAll)) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Info("Delve left Bestiary region claims in place (Dry Run All)."); } } else { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { Func<int> func = default(Func<int>); if (!Plugin.Services.Capabilities.TryResolve<Func<int>>("meadworks.bestiary.world", BestiaryClaimReset.Version, ref func) || func == null) { MeadworksLog? log2 = Plugin.Log; if (log2 != null) { log2.Info("Delve region claims were not cleared: meadworks.bestiary.world is not published."); } return; } int num = BestiaryClaimReset.Apply(func, true); MeadworksLog? log3 = Plugin.Log; if (log3 != null) { log3.Info("Delve region claims cleared: " + num + "."); } } catch (Exception ex) { MeadworksLog? log4 = Plugin.Log; if (log4 != null) { log4.Warning("Delve could not clear Bestiary region claims: " + ex.GetType().Name + ": " + ex.Message); } } } } } public static class DelveAdmin { private static readonly string[] OptionKeys = new string[4] { "from", "caller", "platform", "version" }; private static readonly Tuple<bool, IReadOnlyList<string>> Unhandled = Tuple.Create(item1: false, (IReadOnlyList<string>)Array.Empty<string>()); public static void Publish(ProductRegistration registration) { registration.AddCapability<Func<string, IReadOnlyList<string>, Tuple<bool, IReadOnlyList<string>>>>("meadworks.delve.admin", DelveAdminContract.ContractVersion, (Func<string, IReadOnlyList<string>, Tuple<bool, IReadOnlyList<string>>>)Run); } public unsafe static Tuple<bool, IReadOnlyList<string>> Run(string operation, IReadOnlyList<string> arguments) { //IL_0586: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Invalid comparison between Unknown and I4 DelveEngine engine = Plugin.Engine; DelveConfig settings = Plugin.Settings; ZNet instance = ZNet.instance; if (operation == null || arguments == null || arguments.Any((string a) => a == null) || !Plugin.Ready || engine == null || settings == null || !engine.Running || (Object)(object)instance == (Object)null || !instance.IsServer()) { return Unhandled; } Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); List<string> list = new List<string>(); foreach (string argument in arguments) { int num = argument.IndexOf('='); if (num > 0 && Array.IndexOf(OptionKeys, argument.Substring(0, num)) >= 0) { dictionary[argument.Substring(0, num)] = argument.Substring(num + 1); } else { list.Add(argument); } } string value; int result; int count = ((dictionary.TryGetValue("from", out value) && int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result)) ? result : 0); ZNetPeer val = null; if (dictionary.TryGetValue("caller", out var value2) && (!long.TryParse(value2, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result2) || (val = instance.GetPeer(result2)) == null)) { return Unhandled; } switch (operation) { case "status": return Done(Status(engine, settings)); case "actions": return Done(engine.Status.LastActions.Select(Clean)); case "dry_run": { if (list.Count != 2 || !TryKind(list[0], out var kind) || (list[1] != "on" && list[1] != "off")) { return Unhandled; } string value6; return Done(new string[1] { WriteDryRun(settings, kind, list[1] == "on", dictionary.TryGetValue("version", out value6) ? value6 : null) }); } case "request": { List<DelveKind> list2 = new List<DelveKind>(); foreach (string item in list) { if (!TryKind(item, out var kind2)) { return Unhandled; } list2.Add(kind2); } if (list2.Count == 0) { return Unhandled; } RegenerateVerdict val2 = settings.RegenerateFor(isAdmin: true, Locked(val)); bool skipDelay = (int)val2 == 0; IReadOnlyList<DelveKind> readOnlyList = engine.Request(list2.Where(engine.Active), skipDelay); engine.ConsiderClaimClear(list2, readOnlyList, skipDelay); return Done(new string[1] { Line(new string[2] { "requested", ((object)(*(RegenerateVerdict*)(&val2))/*cast due to .constrained prefix*/).ToString() }.Concat<string>(readOnlyList.Select((DelveKind k) => ((object)(*(DelveKind*)(&k))/*cast due to .constrained prefix*/).ToString()))) }); } case "sites": { if (!TryXz(list, out var x2, out var z2)) { return Unhandled; } return Done(engine.DungeonSites().Select(delegate(DungeonSiteReport s) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) string[] obj = new string[10] { "site", ((object)s.Kind/*cast due to .constrained prefix*/).ToString(), s.Id, s.Generator, Bit(s.Requested || s.EvaluationPending), Bit(s.Due), s.Remaining.HasValue ? R(s.Remaining.Value) : "", ((object)s.Clock/*cast due to .constrained prefix*/).ToString(), null, null }; Vec3 entrance = s.Entrance; double num2 = Math.Pow((double)((Vec3)(ref entrance)).X - (double)x2, 2.0); entrance = s.Entrance; obj[8] = R(Math.Sqrt(num2 + Math.Pow((double)((Vec3)(ref entrance)).Z - (double)z2, 2.0))); obj[9] = Bit(s.DryRun); return Line(obj); }).Skip(count)); } case "nature": { if (!TryXz(list, out var _, out var _)) { return Unhandled; } return Done((from s in engine.WorldSites() select Line("site", ((object)(*(DelveKind*)(&s.Kind))/*cast due to .constrained prefix*/).ToString(), s.Id, s.Prefab, Bit(s.Timing.Requested), Bit(s.Timing.Due), s.Timing.Remaining.HasValue ? R(s.Timing.Remaining.Value) : "", ((object)(*(ClockKind*)(&s.Timing.Clock))/*cast due to .constrained prefix*/).ToString(), ((object)(*(Vec3*)(&s.Position))/*cast due to .constrained prefix*/).ToString(), Bit(s.Timing.DryRun))).Skip(count)); } case "nearest": { if (!TryXz(list, out var x, out var z) || (val != null && !dictionary.ContainsKey("platform"))) { return Unhandled; } return Done(new string[1] { Nearest(engine, settings, instance, x, z, val, (val != null) ? dictionary["platform"] : "") }); } case "surface_nature": case "surface_resources": { if (list.Count != 4 || !Float(list[0], out var value3) || !Float(list[1], out var value4) || !Float(list[2], out var value5)) { return Unhandled; } return Done(DelveSurface.Begin(new Vector3(value3, value4, value5), (SurfaceScope)(operation == "surface_nature"), val?.m_uid ?? ZNet.GetUID(), list[3], (val == null) ? ((Action<string>)delegate(string line) { Console instance2 = Console.instance; if (instance2 != null) { instance2.Print(line); } }) : null)); } default: return Unhandled; } } private unsafe static IEnumerable<string> Status(DelveEngine engine, DelveConfig settings) { yield return Line("version", Version(settings)); foreach (DelveKind value in Enum.GetValues(typeof(DelveKind))) { KindReport val2 = engine.Status.Get(value); (ClockKind, double) tuple = engine.ClockOf(value); yield return Line("kind", ((object)value/*cast due to .constrained prefix*/).ToString(), Bit(settings.Get<bool>(DelveSettings.Kind(value).DryRun)), Bit(val2 != null), I((val2 != null) ? val2.Tracked : 0), I((val2 != null) ? val2.Waiting : 0), I((val2 != null) ? val2.Deferred : 0), I((val2 != null) ? val2.Acted : 0), I((val2 != null) ? val2.Blocked.Values.Sum() : 0), R(tuple.Item2), ((object)(*(ClockKind*)(&tuple.Item1))/*cast due to .constrained prefix*/).ToString(), Bit(engine.DryRun(value)), Bit(engine.Active(value)), ((val2 != null) ? val2.Note : null) ?? ""); } } private static string Version(DelveConfig settings) { string s = "\n" + settings.Get<bool>(General.DryRunAll) + "\n" + string.Join("\n", from DelveKind kind in Enum.GetValues(typeof(DelveKind)) select settings.Entry((DelveSetting)(object)DelveSettings.Kind(kind).DryRun).BoxedValue?.ToString() + ":" + settings.Get<bool>(DelveSettings.Kind(kind).DryRun)); using SHA256 sHA = SHA256.Create(); return string.Concat(from b in sHA.ComputeHash(ReadBounded(settings.File.ConfigFilePath).Concat(Encoding.UTF8.GetBytes(s)).ToArray()) select b.ToString("x2", CultureInfo.InvariantCulture)); } private static byte[] ReadBounded(string path) { if ((File.GetAttributes(path) & FileAttributes.ReparsePoint) != FileAttributes.None || (File.GetAttributes(Path.GetDirectoryName(path)) & FileAttributes.ReparsePoint) != FileAttributes.None) { throw new IOException("LinkedConfig"); } using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); if (fileStream.Length > 1048576) { throw new IOException("ConfigTooLarge"); } using MemoryStream memoryStream = new MemoryStream(); byte[] array = new byte[4096]; int num; while ((num = fileStream.Read(array, 0, array.Length)) > 0) { if (memoryStream.Length + num > 1048576) { throw new IOException("ConfigTooLarge"); } memoryStream.Write(array, 0, num); } return memoryStream.ToArray(); } private static string WriteDryRun(DelveConfig settings, DelveKind kind, bool enabled, string? version) { //IL_0048: 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_0106: Unknown result type (might be due to invalid IL or missing references) if (version != null && version != Version(settings)) { return "stale"; } ConfigEntry<bool> entry = (ConfigEntry<bool>)(object)settings.Entry((DelveSetting)(object)DelveSettings.Kind(kind).DryRun); IConversionTarget target = MeadworksConversion.Target(settings.File); bool started = false; try { ConfigAmendResult obj = MeadworksConfig.AmendBatch("com.meadworks.delve", (IReadOnlyList<ConfigEntryBase>)(object)new ConfigEntryBase[1] { (ConfigEntryBase)entry }, (Action)delegate { if (version != null && version != Version(settings)) { throw new InvalidOperationException("StaleDelveSource"); } target.Backup(); if (version != null && version != Version(settings)) { throw new InvalidOperationException("StaleDelveSource"); } started = true; target.WriteAtomic((IReadOnlyDictionary<string, object>)new Dictionary<string, object> { [((ConfigEntryBase)entry).Definition.Section + "." + ((ConfigEntryBase)entry).Definition.Key] = enabled }); }, (string)null); bool value = new ConfigFile(settings.File.ConfigFilePath, false).Bind<bool>(((ConfigEntryBase)entry).Definition, false, ((ConfigEntryBase)entry).Description).Value; return (obj.Applied && entry.Value == enabled && value == enabled && settings.Get<bool>(DelveSettings.Kind(kind).DryRun) == enabled) ? "saved" : (started ? "unknown" : "failed"); } catch (InvalidOperationException ex) when (!started && ex.Message == "StaleDelveSource") { return "stale"; } catch { return started ? "unknown" : "failed"; } } private static bool Locked(ZNetPeer? peer) { if (peer == null) { return !MeadworksMischief.Decide("com.meadworks.delve", (MischiefCategory)2).Allowed; } return !MeadworksMischief.DecideForPeer(peer.m_rpc, (MischiefCategory)2).Allowed; } private static string Nearest(DelveEngine engine, DelveConfig settings, ZNet net, float x, float z, ZNetPeer? peer, string platform) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Invalid comparison between Unknown and I4 long generation = Plugin.Session.Generation; ZRpc rpc = peer?.m_rpc; ISocket socket = peer?.m_socket; long? peerUid = peer?.m_uid; object obj; if (peer != null) { ZDOMan instance = ZDOMan.instance; obj = ((instance != null) ? instance.GetZDO(peer.m_characterID) : null); } else { Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { obj = null; } else { ZNetView component = ((Component)localPlayer).GetComponent<ZNetView>(); obj = ((component != null) ? component.GetZDO() : null); } } ZDO character = (ZDO)obj; if (character == null || ((ZDOID)(ref character.m_uid)).IsNone() || (peer != null && (character.GetOwner() != peer.m_uid || socket == null || socket.GetHostName() != platform))) { return "position_unknown"; } DungeonSiteReport val = (from s in engine.DungeonSites() where (int)s.Kind == 0 select s).OrderBy(delegate(DungeonSiteReport s) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) Vec3 entrance = s.Entrance; double num = Math.Pow((double)((Vec3)(ref entrance)).X - (double)x, 2.0); entrance = s.Entrance; return num + Math.Pow((double)((Vec3)(ref entrance)).Z - (double)z, 2.0); }).ThenBy<DungeonSiteReport, string>((DungeonSiteReport s) => s.Id, StringComparer.Ordinal).FirstOrDefault(); if (val == null) { return "none"; } bool skip = (int)settings.RegenerateFor(isAdmin: true, Locked(peer)) == 0; if (!engine.RequestDungeon(val.Id, skip, StillAllowed)) { return Line("refused", val.Id); } return Line("queued", val.Generator, val.Id); bool StillAllowed() { //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Invalid comparison between Unknown and I4 //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) if (ZNet.instance != net || !net.IsServer() || !Plugin.Session.AuthorityReady || Plugin.Session.Generation != generation || Plugin.Engine != engine || Plugin.Settings != settings) { return false; } if (peer != null && (!peer.IsReady() || peer.m_rpc == null || !peer.m_rpc.IsConnected() || peer.m_socket == null || peer.m_rpc != rpc || peer.m_socket != socket || peer.m_uid != peerUid || peer.m_characterID != character.m_uid || !net.GetPeers().Any((ZNetPeer p) => p == peer) || peer.m_socket.GetHostName() != platform || !net.IsAdmin(platform))) { return false; } object obj2; if (peer != null) { ZDOMan instance2 = ZDOMan.instance; obj2 = ((instance2 != null) ? instance2.GetZDO(peer.m_characterID) : null); } else { Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 == null) { obj2 = null; } else { ZNetView component2 = ((Component)localPlayer2).GetComponent<ZNetView>(); obj2 = ((component2 != null) ? component2.GetZDO() : null); } } ZDO val2 = (ZDO)obj2; if (val2 != character || (peer != null && val2.GetOwner() != peer.m_uid)) { return false; } if (skip) { return (int)settings.RegenerateFor(isAdmin: true, Locked(peer)) == 0; } return true; } } private static Tuple<bool, IReadOnlyList<string>> Done(IEnumerable<string> lines) { return Tuple.Create(item1: true, (IReadOnlyList<string>)lines.Take(4096).ToArray()); } private static bool TryKind(string text, out DelveKind kind) { kind = (DelveKind)0; if (Enum.GetNames(typeof(DelveKind)).Contains(text)) { return Enum.TryParse<DelveKind>(text, ignoreCase: false, out kind); } return false; } private static bool TryXz(List<string> positional, out float x, out float z) { x = (z = 0f); if (positional.Count == 2 && Float(positional[0], out x)) { return Float(positional[1], out z); } return false; } private static bool Float(string text, out float value) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static string Bit(bool value) { if (!value) { return "0"; } return "1"; } private static string I(int value) { return value.ToString(CultureInfo.InvariantCulture); } private static string R(double value) { return value.ToString("R", CultureInfo.InvariantCulture); } private static string Clean(string? text) { return (text ?? "").Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' '); } private static string Line(params string[] fields) { return Line((IEnumerable<string>)fields); } private static string Line(IEnumerable<string> fields) { return string.Join("\t", fields.Select(Clean)); } } public sealed class DelveImport : IDisposable { private readonly ConfigFile config; private readonly DelveConfig settings; private readonly MeadworksLog log; private readonly List<IDisposable> registration