From 748afb6f1088047d6aaac8e64eb4b640ad3b9d6d Mon Sep 17 00:00:00 2001 From: wixoaGit Date: Thu, 16 Jan 2025 00:01:20 -0500 Subject: [PATCH 1/4] Implement `get_steps_to()` and `get_step_to()` --- Content.Tests/ContentUnitTest.cs | 1 + Content.Tests/DummyDreamMapManager.cs | 6 +- DMCompiler/DMStandard/_Standard.dm | 10 +- OpenDreamRuntime/AtomManager.cs | 1 + OpenDreamRuntime/DreamManager.cs | 1 + OpenDreamRuntime/Input/MouseInputSystem.cs | 5 +- .../Map/DreamMapManager.Pathfinding.cs | 105 ++++++++++++++++++ OpenDreamRuntime/{ => Map}/DreamMapManager.cs | 10 +- OpenDreamRuntime/Objects/DreamObject.cs | 1 + .../Objects/DreamObjectDefinition.cs | 2 +- OpenDreamRuntime/Objects/DreamObjectTree.cs | 1 + OpenDreamRuntime/Objects/Types/DreamList.cs | 1 + .../Objects/Types/DreamObjectTurf.cs | 5 +- OpenDreamRuntime/Procs/DMProc.cs | 1 + .../Procs/Native/DreamProcNative.cs | 6 +- .../Procs/Native/DreamProcNativeHelpers.cs | 16 ++- .../Procs/Native/DreamProcNativeRoot.cs | 55 ++++++++- OpenDreamRuntime/Procs/NativeProc.cs | 3 +- OpenDreamRuntime/ServerContentIoC.cs | 40 +++---- OpenDreamRuntime/WalkManager.cs | 1 + 20 files changed, 223 insertions(+), 48 deletions(-) create mode 100644 OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs rename OpenDreamRuntime/{ => Map}/DreamMapManager.cs (98%) diff --git a/Content.Tests/ContentUnitTest.cs b/Content.Tests/ContentUnitTest.cs index c102584fb6..b309b09f73 100644 --- a/Content.Tests/ContentUnitTest.cs +++ b/Content.Tests/ContentUnitTest.cs @@ -3,6 +3,7 @@ using System.Reflection; using OpenDreamClient; using OpenDreamRuntime; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Rendering; using OpenDreamShared; using OpenDreamShared.Rendering; diff --git a/Content.Tests/DummyDreamMapManager.cs b/Content.Tests/DummyDreamMapManager.cs index 7a5de816d4..76b812bee5 100644 --- a/Content.Tests/DummyDreamMapManager.cs +++ b/Content.Tests/DummyDreamMapManager.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using DMCompiler.Json; -using OpenDreamRuntime; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs; @@ -54,4 +54,8 @@ public void SetWorldSize(Vector2i size) { } public EntityUid GetZLevelEntity(int z) { return EntityUid.Invalid; } + + public IEnumerable CalculateSteps((int X, int Y, int Z) loc, (int X, int Y, int Z) dest, int distance) { + yield break; + } } diff --git a/DMCompiler/DMStandard/_Standard.dm b/DMCompiler/DMStandard/_Standard.dm index 614ef790b2..65cd2fca98 100644 --- a/DMCompiler/DMStandard/_Standard.dm +++ b/DMCompiler/DMStandard/_Standard.dm @@ -28,6 +28,8 @@ proc/flist(Path) as /list proc/floor(A) as num proc/fract(n) as num proc/ftime(File, IsCreationTime = 0) as num +proc/get_step_to(Ref, Trg, Min=0) as num +proc/get_steps_to(Ref, Trg, Min=0) as /list proc/gradient(A, index) proc/hascall(Object, ProcName) as num proc/hearers(Depth = world.view, Center = usr) as /list @@ -161,14 +163,6 @@ proc/replacetextEx_char(Haystack as text, Needle, Replacement, Start = 1, End = var/step_dir = get_dir(Ref, Trg) return step(Ref, step_dir, Speed) -/proc/get_step_to(Ref, Trg, Min=0) - set opendream_unimplemented = TRUE - CRASH("/get_step_to() is not implemented") - -/proc/get_steps_to(Ref, Trg, Min=0) as /list - set opendream_unimplemented = TRUE - CRASH("/get_steps_to() is not implemented") - /proc/walk_away(Ref,Trg,Max=5,Lag=0,Speed=0) set opendream_unimplemented = TRUE CRASH("/walk_away() is not implemented") diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 7c5a3bdbc4..20ccee2894 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs.Native; diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 4264263a12..48176de307 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -5,6 +5,7 @@ using System.Threading; using DMCompiler.Bytecode; using DMCompiler.Json; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs; diff --git a/OpenDreamRuntime/Input/MouseInputSystem.cs b/OpenDreamRuntime/Input/MouseInputSystem.cs index 8956e300cf..ce7c748620 100644 --- a/OpenDreamRuntime/Input/MouseInputSystem.cs +++ b/OpenDreamRuntime/Input/MouseInputSystem.cs @@ -1,6 +1,5 @@ -using System.Collections.Specialized; -using System.Text; -using System.Web; +using System.Text; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects.Types; using OpenDreamShared.Input; diff --git a/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs b/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs new file mode 100644 index 0000000000..32f116ee60 --- /dev/null +++ b/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs @@ -0,0 +1,105 @@ +using System.Diagnostics.CodeAnalysis; +using OpenDreamRuntime.Procs.Native; +using OpenDreamShared.Dream; + +namespace OpenDreamRuntime.Map; + +public partial class DreamMapManager { + private sealed class PathFindNode : IDisposable, IEquatable { + private static readonly Stack Pool = new(); + + public int X, Y; + public PathFindNode? Parent; + public int NeededSteps; + + public static PathFindNode GetNode(int x, int y) { + if (!Pool.TryPop(out var node)) { + node = new(); + } + + node.Parent = null; + node.X = x; + node.Y = y; + node.NeededSteps = 0; + return node; + } + + public void Dispose() { + Pool.Push(this); + } + + public bool Equals(PathFindNode? other) { + if (other is null) + return false; + return X == other.X && Y == other.Y; + } + + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] + public override int GetHashCode() { + return HashCode.Combine(X, Y); + } + } + + public IEnumerable CalculateSteps((int X, int Y, int Z) loc, (int X, int Y, int Z) dest, int distance) { + int z = loc.Z; + if (z != dest.Z) // Different Z-levels are unreachable + yield break; + + HashSet explored = new(); + Queue toExplore = new(); + + toExplore.Enqueue(PathFindNode.GetNode(loc.X, loc.Y)); + + void Explore(PathFindNode current, int offsetX, int offsetY, int currentStep) { + var next = PathFindNode.GetNode(current.X + offsetX, current.Y + offsetY); + if (explored.Contains(next)) + return; + if (!TryGetCellAt(new(next.X, next.Y), z, out var cell) || cell.Turf.IsDense) + return; + + if (!toExplore.Contains(next)) + toExplore.Enqueue(next); + else if (next.NeededSteps >= currentStep + 1) + return; + + next.NeededSteps = currentStep + 1; + next.Parent = current; + } + + while (toExplore.TryDequeue(out var node)) { + var distX = node.X - dest.X; + var distY = node.Y - dest.Y; + if (Math.Sqrt(distX * distX + distY * distY) <= distance) { // Path to the destination was found + Stack path = new(); + + while (node.Parent != null) { + var stepDir = DreamProcNativeHelpers.GetDir((node.Parent.X, node.Parent.Y, z), (node.X, node.Y, z)); + + node = node.Parent; + path.Push(stepDir); + } + + while (path.TryPop(out var step)) { + yield return step; + } + + break; + } + + explored.Add(node); + Explore(node, 1, 0, node.NeededSteps); + Explore(node, 1, 1, node.NeededSteps); + Explore(node, 0, 1, node.NeededSteps); + Explore(node, -1, 1, node.NeededSteps); + Explore(node, -1, 0, node.NeededSteps); + Explore(node, -1, -1, node.NeededSteps); + Explore(node, 0, -1, node.NeededSteps); + Explore(node, 1, -1, node.NeededSteps); + } + + foreach (var node in explored) + node.Dispose(); + foreach (var node in toExplore) + node.Dispose(); + } +} diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/Map/DreamMapManager.cs similarity index 98% rename from OpenDreamRuntime/DreamMapManager.cs rename to OpenDreamRuntime/Map/DreamMapManager.cs index 2b9959e137..408b0e0da6 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/Map/DreamMapManager.cs @@ -10,12 +10,12 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Utility; -using Level = OpenDreamRuntime.IDreamMapManager.Level; -using Cell = OpenDreamRuntime.IDreamMapManager.Cell; +using Level = OpenDreamRuntime.Map.IDreamMapManager.Level; +using Cell = OpenDreamRuntime.Map.IDreamMapManager.Cell; -namespace OpenDreamRuntime; +namespace OpenDreamRuntime.Map; -public sealed class DreamMapManager : IDreamMapManager { +public sealed partial class DreamMapManager : IDreamMapManager { [Dependency] private readonly DreamManager _dreamManager = default!; [Dependency] private readonly AtomManager _atomManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; @@ -497,4 +497,6 @@ public Cell(DreamObjectArea area, DreamObjectTurf turf) { public void SetZLevels(int levels); public void SetWorldSize(Vector2i size); public EntityUid GetZLevelEntity(int z); + + public IEnumerable CalculateSteps((int X, int Y, int Z) loc, (int X, int Y, int Z) dest, int distance); } diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 646a14e50c..19794ab9b0 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Runtime.CompilerServices; using DMCompiler.Bytecode; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Rendering; using OpenDreamRuntime.Resources; diff --git a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs index 670299e238..9c89943200 100644 --- a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs +++ b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs @@ -1,9 +1,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Procs; using OpenDreamRuntime.Rendering; using OpenDreamRuntime.Resources; -using OpenDreamShared.Dream; using Robust.Server.GameObjects; using Robust.Server.GameStates; using Robust.Server.Player; diff --git a/OpenDreamRuntime/Objects/DreamObjectTree.cs b/OpenDreamRuntime/Objects/DreamObjectTree.cs index f2492129f4..7963174639 100644 --- a/OpenDreamRuntime/Objects/DreamObjectTree.cs +++ b/OpenDreamRuntime/Objects/DreamObjectTree.cs @@ -2,6 +2,7 @@ using System.Text.Json; using System.Threading.Tasks; using DMCompiler.Json; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs; using OpenDreamRuntime.Procs.DebugAdapter; diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 2268672dee..07b8cbc262 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Runtime.CompilerServices; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Procs; using OpenDreamRuntime.Rendering; using OpenDreamShared.Dream; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index a46781d320..b5ccd7404e 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -1,4 +1,5 @@ -using OpenDreamShared.Dream; +using OpenDreamRuntime.Map; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Objects.Types; @@ -8,6 +9,8 @@ public sealed class DreamObjectTurf : DreamObjectAtom { public ImmutableAppearance Appearance; public IDreamMapManager.Cell Cell; + public bool IsDense => GetVariable("density").IsTruthy(); + public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z) : base(objectDefinition) { X = x; Y = y; diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 5f9f039a09..9b8a536854 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -5,6 +5,7 @@ using DMCompiler.Bytecode; using DMCompiler.DM; using DMCompiler.Json; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs.DebugAdapter; diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs index c9da8e3c87..6ed7ebd2f3 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs @@ -32,6 +32,8 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) { objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_floor); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_fract); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_ftime); + objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_get_step_to); + objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_get_steps_to); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_hascall); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_hearers); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_html_decode); @@ -145,14 +147,14 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) { objectTree.SetNativeProc(objectTree.Savefile, DreamProcNativeSavefile.NativeProc_ExportText); objectTree.SetNativeProc(objectTree.Savefile, DreamProcNativeSavefile.NativeProc_Flush); - + objectTree.SetNativeProc(objectTree.World, DreamProcNativeWorld.NativeProc_Export); objectTree.SetNativeProc(objectTree.World, DreamProcNativeWorld.NativeProc_GetConfig); objectTree.SetNativeProc(objectTree.World, DreamProcNativeWorld.NativeProc_Profile); objectTree.SetNativeProc(objectTree.World, DreamProcNativeWorld.NativeProc_SetConfig); objectTree.SetNativeProc(objectTree.World, DreamProcNativeWorld.NativeProc_ODHotReloadInterface); objectTree.SetNativeProc(objectTree.World, DreamProcNativeWorld.NativeProc_ODHotReloadResource); - + objectTree.SetNativeProc(objectTree.Database, DreamProcNativeDatabase.NativeProc_Close); objectTree.SetNativeProc(objectTree.Database, DreamProcNativeDatabase.NativeProc_Error); objectTree.SetNativeProc(objectTree.Database, DreamProcNativeDatabase.NativeProc_ErrorMsg); diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs index f94f3de8a0..70bc2c820f 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using OpenDreamRuntime.Objects.Types; using System.Text; +using OpenDreamRuntime.Map; namespace OpenDreamRuntime.Procs.Native; @@ -541,21 +542,26 @@ public static AtomDirection GetDir(AtomManager atomManager, DreamObjectAtom loc1 var loc1Pos = atomManager.GetAtomPosition(loc1); var loc2Pos = atomManager.GetAtomPosition(loc2); - if (loc1Pos.Z != loc2Pos.Z) // They must be on the same z-level + return GetDir(loc1Pos, loc2Pos); + } + + /// + public static AtomDirection GetDir((int X, int Y, int Z) loc1, (int X, int Y, int Z) loc2) { + if (loc1.Z != loc2.Z) // They must be on the same z-level return 0; AtomDirection direction = AtomDirection.None; // East or West - if (loc2Pos.X < loc1Pos.X) + if (loc2.X < loc1.X) direction |= AtomDirection.West; - else if (loc2Pos.X > loc1Pos.X) + else if (loc2.X > loc1.X) direction |= AtomDirection.East; // North or South - if (loc2Pos.Y < loc1Pos.Y) + if (loc2.Y < loc1.Y) direction |= AtomDirection.South; - else if (loc2Pos.Y > loc1Pos.Y) + else if (loc2.Y > loc1.Y) direction |= AtomDirection.North; return direction; diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs index 8c24545377..8f3d500afc 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs @@ -156,11 +156,12 @@ public static DreamValue NativeProc_animate(NativeProc.Bundle bundle, DreamObjec return DreamValue.Null; chainAnim = true; } - + bundle.LastAnimatedObject = new DreamValue(obj); if(obj.IsSubtypeOf(bundle.ObjectTree.Filter)) {//TODO animate filters return DreamValue.Null; } + // TODO: Is this the correct behavior for invalid time? if (!bundle.GetArgument(1, "time").TryGetValueAsFloat(out float time)) return DreamValue.Null; @@ -700,7 +701,6 @@ public static DreamValue NativeProc_file2text(NativeProc.Bundle bundle, DreamObj DreamValue file = bundle.GetArgument(0, "File"); DreamResource? resource; - if (file.TryGetValueAsString(out var rscPath)) { resource = bundle.ResourceManager.LoadResource(rscPath); } else if (!file.TryGetValueAsDreamResource(out resource)) { @@ -993,11 +993,62 @@ public static DreamValue NativeProc_ftime(NativeProc.Bundle bundle, DreamObject? if (isCreationTime.IsTruthy()) { return new DreamValue((fi.CreationTime - new DateTime(2000, 1, 1)).TotalMilliseconds / 100); } + return new DreamValue((fi.LastWriteTime - new DateTime(2000, 1, 1)).TotalMilliseconds / 100); } + throw new Exception("Invalid path argument"); } + [DreamProc("get_step_to")] + [DreamProcParameter("Ref", Type = DreamValueTypeFlag.DreamObject)] + [DreamProcParameter("Trg", Type = DreamValueTypeFlag.DreamObject)] + [DreamProcParameter("Min", Type = DreamValueTypeFlag.Float, DefaultValue = 0)] + public static DreamValue NativeProc_get_step_to(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) { + var refArg = bundle.GetArgument(0, "Ref"); + var trgArg = bundle.GetArgument(1, "Trg"); + var minArg = (int)bundle.GetArgument(2, "Min").UnsafeGetValueAsFloat(); + + if (!refArg.TryGetValueAsDreamObject(out var refAtom)) + return DreamValue.Null; + if (!trgArg.TryGetValueAsDreamObject(out var trgAtom)) + return DreamValue.Null; + + var loc = bundle.AtomManager.GetAtomPosition(refAtom); + var dest = bundle.AtomManager.GetAtomPosition(trgAtom); + var steps = bundle.MapManager.CalculateSteps(loc, dest, minArg); + + // We perform a whole path-find then return only the first step + // Truly, DM's most optimized proc + return new((int)steps.FirstOrDefault()); + } + + [DreamProc("get_steps_to")] + [DreamProcParameter("Ref", Type = DreamValueTypeFlag.DreamObject)] + [DreamProcParameter("Trg", Type = DreamValueTypeFlag.DreamObject)] + [DreamProcParameter("Min", Type = DreamValueTypeFlag.Float, DefaultValue = 0)] + public static DreamValue NativeProc_get_steps_to(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) { + var refArg = bundle.GetArgument(0, "Ref"); + var trgArg = bundle.GetArgument(1, "Trg"); + var minArg = (int)bundle.GetArgument(2, "Min").UnsafeGetValueAsFloat(); + + if (!refArg.TryGetValueAsDreamObject(out var refAtom)) + return DreamValue.Null; + if (!trgArg.TryGetValueAsDreamObject(out var trgAtom)) + return DreamValue.Null; + + var loc = bundle.AtomManager.GetAtomPosition(refAtom); + var dest = bundle.AtomManager.GetAtomPosition(trgAtom); + var steps = bundle.MapManager.CalculateSteps(loc, dest, minArg); + var result = bundle.ObjectTree.CreateList(); + + foreach (var step in steps) { + result.AddValue(new((int)step)); + } + + return new(result); + } + [DreamProc("hascall")] [DreamProcParameter("Object", Type = DreamValueTypeFlag.DreamObject)] [DreamProcParameter("ProcName", Type = DreamValueTypeFlag.String)] diff --git a/OpenDreamRuntime/Procs/NativeProc.cs b/OpenDreamRuntime/Procs/NativeProc.cs index ae80f6895d..e6a46858b0 100644 --- a/OpenDreamRuntime/Procs/NativeProc.cs +++ b/OpenDreamRuntime/Procs/NativeProc.cs @@ -4,7 +4,7 @@ using DMCompiler.DM; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Resources; -using System.Threading; +using OpenDreamRuntime.Map; namespace OpenDreamRuntime.Procs; @@ -60,6 +60,7 @@ public readonly ref struct Bundle { public WalkManager WalkManager => Proc._walkManager; public DreamObjectTree ObjectTree => Proc._objectTree; private readonly DreamThread _thread; + public DreamValue? LastAnimatedObject { get => _thread.LastAnimatedObject; set => _thread.LastAnimatedObject = value; diff --git a/OpenDreamRuntime/ServerContentIoC.cs b/OpenDreamRuntime/ServerContentIoC.cs index a7bde642e9..ced7a28efd 100644 --- a/OpenDreamRuntime/ServerContentIoC.cs +++ b/OpenDreamRuntime/ServerContentIoC.cs @@ -1,29 +1,29 @@ -using OpenDreamRuntime.Objects; +using OpenDreamRuntime.Map; +using OpenDreamRuntime.Objects; using OpenDreamRuntime.Procs; using OpenDreamRuntime.Procs.DebugAdapter; -using OpenDreamRuntime.Rendering; using OpenDreamRuntime.Resources; -namespace OpenDreamRuntime { - public static class ServerContentIoC { - public static void Register(bool unitTests = false) { - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); +namespace OpenDreamRuntime; - #if DEBUG - IoCManager.Register(); - #endif +public static class ServerContentIoC { + public static void Register(bool unitTests = false) { + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); - if (!unitTests) { - // Unit tests use their own version - IoCManager.Register(); - } +#if DEBUG + IoCManager.Register(); +#endif + + if (!unitTests) { + // Unit tests use their own version + IoCManager.Register(); } } } diff --git a/OpenDreamRuntime/WalkManager.cs b/OpenDreamRuntime/WalkManager.cs index 3a9ea09579..560342d20a 100644 --- a/OpenDreamRuntime/WalkManager.cs +++ b/OpenDreamRuntime/WalkManager.cs @@ -1,4 +1,5 @@ using System.Threading; +using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs; using OpenDreamRuntime.Procs.Native; From e3da82ac9c6e433a4e6ac2c9124dd7e241a828bd Mon Sep 17 00:00:00 2001 From: wixoaGit Date: Thu, 16 Jan 2025 11:56:53 -0500 Subject: [PATCH 2/4] Don't leave map bounds --- .../Map/DreamMapManager.Pathfinding.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs b/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs index 32f116ee60..6d4569a5bb 100644 --- a/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs +++ b/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs @@ -50,8 +50,13 @@ public IEnumerable CalculateSteps((int X, int Y, int Z) loc, (int toExplore.Enqueue(PathFindNode.GetNode(loc.X, loc.Y)); - void Explore(PathFindNode current, int offsetX, int offsetY, int currentStep) { - var next = PathFindNode.GetNode(current.X + offsetX, current.Y + offsetY); + void Explore(PathFindNode current, int offsetX, int offsetY) { + var nextX = current.X + offsetX; + var nextY = current.Y + offsetY; + if (nextX < 1 || nextX > Size.X || nextY < 1 || nextY > Size.Y) + return; // This is outside of map bounds + + var next = PathFindNode.GetNode(nextX, nextY); if (explored.Contains(next)) return; if (!TryGetCellAt(new(next.X, next.Y), z, out var cell) || cell.Turf.IsDense) @@ -59,10 +64,10 @@ void Explore(PathFindNode current, int offsetX, int offsetY, int currentStep) { if (!toExplore.Contains(next)) toExplore.Enqueue(next); - else if (next.NeededSteps >= currentStep + 1) + else if (next.NeededSteps >= current.NeededSteps + 1) return; - next.NeededSteps = currentStep + 1; + next.NeededSteps = current.NeededSteps + 1; next.Parent = current; } @@ -87,14 +92,14 @@ void Explore(PathFindNode current, int offsetX, int offsetY, int currentStep) { } explored.Add(node); - Explore(node, 1, 0, node.NeededSteps); - Explore(node, 1, 1, node.NeededSteps); - Explore(node, 0, 1, node.NeededSteps); - Explore(node, -1, 1, node.NeededSteps); - Explore(node, -1, 0, node.NeededSteps); - Explore(node, -1, -1, node.NeededSteps); - Explore(node, 0, -1, node.NeededSteps); - Explore(node, 1, -1, node.NeededSteps); + Explore(node, 1, 0); + Explore(node, 1, 1); + Explore(node, 0, 1); + Explore(node, -1, 1); + Explore(node, -1, 0); + Explore(node, -1, -1); + Explore(node, 0, -1); + Explore(node, 1, -1); } foreach (var node in explored) From b458b8174d5d05eb75c829eafbc720eb26bffb8c Mon Sep 17 00:00:00 2001 From: wixoaGit Date: Thu, 16 Jan 2025 16:42:45 -0500 Subject: [PATCH 3/4] Fix results when there's no path found --- .../Procs/Native/DreamProcNativeRoot.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs index 8f3d500afc..129329b619 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs @@ -1020,7 +1020,15 @@ public static DreamValue NativeProc_get_step_to(NativeProc.Bundle bundle, DreamO // We perform a whole path-find then return only the first step // Truly, DM's most optimized proc - return new((int)steps.FirstOrDefault()); + var direction = steps.FirstOrDefault(); + var stepLoc = direction switch { + // The ref says get_step_to() returns 0 if there's no change, but it also says it returns null. + // I wasn't able to get it to return 0 so null it is. + 0 => null, + _ => DreamProcNativeHelpers.GetStep(bundle.AtomManager, bundle.MapManager, refAtom, direction) + }; + + return new(stepLoc); } [DreamProc("get_steps_to")] @@ -1046,7 +1054,8 @@ public static DreamValue NativeProc_get_steps_to(NativeProc.Bundle bundle, Dream result.AddValue(new((int)step)); } - return new(result); + // Null if there are no steps + return new(result.GetLength() > 0 ? result : null); } [DreamProc("hascall")] From 1d7926a17a34db7359fed88e74450f5b09ff8de4 Mon Sep 17 00:00:00 2001 From: wixoaGit Date: Thu, 16 Jan 2025 22:27:59 -0500 Subject: [PATCH 4/4] Pre-allocate stack size --- OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs b/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs index 32f116ee60..f1933c848a 100644 --- a/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs +++ b/OpenDreamRuntime/Map/DreamMapManager.Pathfinding.cs @@ -70,7 +70,7 @@ void Explore(PathFindNode current, int offsetX, int offsetY, int currentStep) { var distX = node.X - dest.X; var distY = node.Y - dest.Y; if (Math.Sqrt(distX * distX + distY * distY) <= distance) { // Path to the destination was found - Stack path = new(); + Stack path = new(node.NeededSteps); while (node.Parent != null) { var stepDir = DreamProcNativeHelpers.GetDir((node.Parent.X, node.Parent.Y, z), (node.X, node.Y, z));