diff --git a/DMCompiler/Compiler/DM/DMASTHelper.cs b/DMCompiler/Compiler/DM/DMASTHelper.cs index a37890f734..0040d2ef66 100644 --- a/DMCompiler/Compiler/DM/DMASTHelper.cs +++ b/DMCompiler/Compiler/DM/DMASTHelper.cs @@ -72,7 +72,7 @@ public void HashFile(DMASTFile node) { } public DMASTProcDefinition GetProcByPath(string path) { - var h = Hash(new DMASTProcDefinition(Location.Unknown, new OpenDreamShared.Dream.DreamPath(path), new DMASTDefinitionParameter[0], null)); + var h = Hash(new DMASTProcDefinition(Location.Unknown, new DreamPath(path), new DMASTDefinitionParameter[0], null)); return nodes[h][0] as DMASTProcDefinition; } public void HashDefine(DMASTNode node) { diff --git a/DMCompiler/Compiler/DM/DMPath.cs b/DMCompiler/Compiler/DM/DMPath.cs index 6c048ce93f..adc53c7be3 100644 --- a/DMCompiler/Compiler/DM/DMPath.cs +++ b/DMCompiler/Compiler/DM/DMPath.cs @@ -1,12 +1,7 @@ - -using System.Collections.Generic; -using OpenDreamShared.Dream; +using System.Collections.Generic; -namespace DMCompiler.Compiler.DM -{ - - public abstract class VarDeclInfo - { +namespace DMCompiler.Compiler.DM { + public abstract class VarDeclInfo { public DreamPath? TypePath; public string VarName; diff --git a/DMCompiler/Compiler/DMM/DMMParser.cs b/DMCompiler/Compiler/DMM/DMMParser.cs index aad08e810f..e91136570e 100644 --- a/DMCompiler/Compiler/DMM/DMMParser.cs +++ b/DMCompiler/Compiler/DMM/DMMParser.cs @@ -1,6 +1,5 @@ using System; using OpenDreamShared.Compiler; -using OpenDreamShared.Dream; using OpenDreamShared.Json; using DMCompiler.DM; using System.Collections.Generic; diff --git a/DMCompiler/DM/DMExpression.cs b/DMCompiler/DM/DMExpression.cs index eec3d0e949..78cffa0001 100644 --- a/DMCompiler/DM/DMExpression.cs +++ b/DMCompiler/DM/DMExpression.cs @@ -2,7 +2,6 @@ using DMCompiler.DM.Visitors; using OpenDreamShared.Compiler; using DMCompiler.Compiler.DM; -using OpenDreamShared.Dream; using System.Diagnostics.CodeAnalysis; using DMCompiler.Bytecode; diff --git a/DMCompiler/DM/Expressions/Binary.cs b/DMCompiler/DM/Expressions/Binary.cs index 9d1772f401..6a29020177 100644 --- a/DMCompiler/DM/Expressions/Binary.cs +++ b/DMCompiler/DM/Expressions/Binary.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using DMCompiler.Bytecode; using OpenDreamShared.Compiler; -using OpenDreamShared.Dream; namespace DMCompiler.DM.Expressions { abstract class BinaryOp : DMExpression { diff --git a/DMCompiler/DM/Expressions/Constant.cs b/DMCompiler/DM/Expressions/Constant.cs index 824f7680c7..aa65c8b80f 100644 --- a/DMCompiler/DM/Expressions/Constant.cs +++ b/DMCompiler/DM/Expressions/Constant.cs @@ -1,5 +1,4 @@ using OpenDreamShared.Compiler; -using OpenDreamShared.Dream; using OpenDreamShared.Json; using System; using System.Collections.Generic; diff --git a/DMCompiler/DM/Expressions/LValue.cs b/DMCompiler/DM/Expressions/LValue.cs index 83c5bf5f42..50bcf8390b 100644 --- a/DMCompiler/DM/Expressions/LValue.cs +++ b/DMCompiler/DM/Expressions/LValue.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using DMCompiler.Bytecode; using OpenDreamShared.Compiler; -using OpenDreamShared.Dream; namespace DMCompiler.DM.Expressions { abstract class LValue : DMExpression { diff --git a/DMCompiler/DreamPath.cs b/DMCompiler/DreamPath.cs new file mode 100644 index 0000000000..28a1fe6426 --- /dev/null +++ b/DMCompiler/DreamPath.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; + +namespace DMCompiler; + +// TODO: This is fairly ugly structure and should probably be removed/redone with something much nicer. +// It's heavily embedded into a lot of corners of the compiler though. +public struct DreamPath { + public static readonly DreamPath Root = new DreamPath("/"); + public static readonly DreamPath Exception = new DreamPath("/exception"); + public static readonly DreamPath List = new DreamPath("/list"); + public static readonly DreamPath Regex = new DreamPath("/regex"); + public static readonly DreamPath Savefile = new DreamPath("/savefile"); + public static readonly DreamPath Sound = new DreamPath("/sound"); + public static readonly DreamPath Image = new DreamPath("/image"); + public static readonly DreamPath Icon = new DreamPath("/icon"); + public static readonly DreamPath MutableAppearance = new DreamPath("/mutable_appearance"); + public static readonly DreamPath World = new DreamPath("/world"); + public static readonly DreamPath Client = new DreamPath("/client"); + public static readonly DreamPath Datum = new DreamPath("/datum"); + public static readonly DreamPath Matrix = new DreamPath("/matrix"); + public static readonly DreamPath Atom = new DreamPath("/atom"); + public static readonly DreamPath Area = new DreamPath("/area"); + public static readonly DreamPath Turf = new DreamPath("/turf"); + public static readonly DreamPath Movable = new DreamPath("/atom/movable"); + public static readonly DreamPath Obj = new DreamPath("/obj"); + public static readonly DreamPath Mob = new DreamPath("/mob"); + public static readonly DreamPath Filter = new DreamPath("/dm_filter"); + + public enum PathType { + Absolute, + Relative, + + //TODO: These really shouldn't be here + DownwardSearch, + UpwardSearch + } + + [JsonIgnore] + public string? LastElement { + get => Elements.Length > 0 ? Elements.Last() : null; + } + + [JsonIgnore] + public string[] Elements { + get => _elements; + set { + _elements = value; + _pathString = null; + } + } + + public string PathString { + get { + if (_pathString != null) return _pathString; + + _pathString = Type switch { + PathType.Absolute => "/", + PathType.DownwardSearch => ":", + PathType.UpwardSearch => ".", + _ => string.Empty + }; + + // Elements is usually small enough for this to be faster than StringBuilder + _pathString += string.Join("/", Elements); + + return _pathString; + } + set => SetFromString(value); + } + + public PathType Type; + + private string[] _elements; + private string? _pathString; + + public DreamPath(string path) { + Type = PathType.Absolute; + _elements = Array.Empty(); // Set in SetFromString() + _pathString = null; + + SetFromString(path); + } + + public DreamPath(PathType type, string[] elements) { + Type = type; + _elements = elements; + _pathString = null; + + Normalize(true); + } + + public void SetFromString(string rawPath) { + char pathTypeChar = rawPath[0]; + string[] tempElements = rawPath.Split('/', StringSplitOptions.RemoveEmptyEntries); + bool skipFirstChar = false; + + switch (pathTypeChar) { + case '/': + Type = PathType.Absolute; + // No need to skip the first char, as it will end up as an empty entry in tempElements + break; + case ':': + Type = PathType.DownwardSearch; + skipFirstChar = true; + break; + case '.': + Type = PathType.UpwardSearch; + skipFirstChar = true; + break; + default: + Type = PathType.Relative; + break; + } + + if (skipFirstChar) { + // Skip the '/', ':' or '.' if needed + tempElements[0] = tempElements[0][1..]; + } + + Elements = tempElements; + Normalize(false); + } + + /// + /// Checks if the DreamPath is a descendant of another. NOTE: For type inheritance, use IsSubtypeOf() + /// + /// Path to compare to. + public bool IsDescendantOf(DreamPath path) { + if (path.Elements.Length > Elements.Length) return false; + + for (int i = 0; i < path.Elements.Length; i++) { + if (Elements[i] != path.Elements[i]) return false; + } + + return true; + } + + public DreamPath AddToPath(string path) { + string rawPath = PathString; + + if (!rawPath.EndsWith('/') && !path.StartsWith('/')) { + path = '/' + path; + } + + return new DreamPath(rawPath + path); + } + + public int FindElement(string element) { + return Array.IndexOf(Elements, element); + } + + public string[] GetElements(int elementStart, int elementEnd = -1) { + if (elementEnd < 0) elementEnd = Elements.Length + elementEnd + 1; + + string[] elements = new string[elementEnd - elementStart]; + Array.Copy(Elements, elementStart, elements, 0, elements.Length); + + return elements; + } + + public DreamPath FromElements(int elementStart, int elementEnd = -1) { + string[] elements = GetElements(elementStart, elementEnd); + string rawPath = String.Empty; + + if (elements.Length >= 1) { + rawPath = elements.Aggregate((string first, string second) => { + return first + "/" + second; + }); + } + + rawPath = "/" + rawPath; + return new DreamPath(rawPath); + } + + public DreamPath RemoveElement(int elementIndex) { + if (elementIndex < 0) elementIndex += Elements.Length; + + List elements = new List(); + elements.AddRange(GetElements(0, elementIndex)); + elements.AddRange(GetElements(Math.Min(elementIndex + 1, Elements.Length), -1)); + return new DreamPath(Type, elements.ToArray()); + } + + public DreamPath Combine(DreamPath path) { + switch (path.Type) { + case PathType.Relative: return new DreamPath(PathString + "/" + path.PathString); + case PathType.Absolute: return path; + default: return new DreamPath(PathString + path.PathString); + } + } + + public override string ToString() { + return PathString; + } + + public override bool Equals(object? obj) => obj is DreamPath other && Equals(other); + + public bool Equals(DreamPath other) { + if (other.Elements.Length != Elements.Length) return false; + + for (int i = 0; i < Elements.Length; i++) { + if (Elements[i] != other.Elements[i]) return false; + } + + return true; + } + + public override int GetHashCode() { + int hashCode = 0; + for (int i = 0; i < Elements.Length; i++) { + hashCode += Elements[i].GetHashCode(); + } + + return hashCode; + } + + public static bool operator ==(DreamPath lhs, DreamPath rhs) => lhs.Equals(rhs); + + public static bool operator !=(DreamPath lhs, DreamPath rhs) => !(lhs == rhs); + + private void Normalize(bool canHaveEmptyEntries) { + if (canHaveEmptyEntries && _elements.Contains("")) { + // Slow path :( + _elements = _elements.Where(el => !string.IsNullOrEmpty(el)).ToArray(); + } + + var writeIdx = Array.IndexOf(_elements, ".."); + if (writeIdx == -1) return; + + for (var i = writeIdx; i < _elements.Length; i++) { + var elem = _elements[i]; + if (elem == "..") { + writeIdx -= 1; + } else { + _elements[writeIdx] = elem; + writeIdx += 1; + } + } + + Elements = _elements[..writeIdx]; + } +} diff --git a/DMDisassembler/DMType.cs b/DMDisassembler/DMType.cs index d88695b709..6d200a4412 100644 --- a/DMDisassembler/DMType.cs +++ b/DMDisassembler/DMType.cs @@ -1,27 +1,26 @@ -using OpenDreamShared.Dream; -using OpenDreamShared.Json; +using OpenDreamShared.Json; using System.Collections.Generic; -namespace DMDisassembler { - class DMType { - public DreamPath Path; - public DreamTypeJson Json; - public DMProc InitProc; - public Dictionary Procs; +namespace DMDisassembler; - public DMType(DreamTypeJson json) { - Json = json; - Path = new DreamPath(Json.Path); +internal class DMType { + public string Path; + public DreamTypeJson Json; + public DMProc InitProc; + public Dictionary Procs; - InitProc = Json.InitProc.HasValue ? Program.Procs[Json.InitProc.Value] : null; + public DMType(DreamTypeJson json) { + Json = json; + Path = Json.Path; - Procs = new(json.Procs?.Count ?? 0); - if (Json.Procs != null) { - foreach (List procIds in Json.Procs) { - DMProc proc = Program.Procs[procIds[^1]]; + InitProc = Json.InitProc.HasValue ? Program.Procs[Json.InitProc.Value] : null; - Procs.Add(proc.Name, proc); - } + Procs = new(json.Procs?.Count ?? 0); + if (Json.Procs != null) { + foreach (List procIds in Json.Procs) { + DMProc proc = Program.Procs[procIds[^1]]; + + Procs.Add(proc.Name, proc); } } } diff --git a/DMDisassembler/Program.cs b/DMDisassembler/Program.cs index 16ee005a75..92b266eed8 100644 --- a/DMDisassembler/Program.cs +++ b/DMDisassembler/Program.cs @@ -2,214 +2,213 @@ using System.Collections.Generic; using System.IO; using System.Text.Json; -using OpenDreamShared.Dream; using OpenDreamShared.Json; -namespace DMDisassembler { - class Program { - public static DreamCompiledJson CompiledJson; - public static DMProc GlobalInitProc = null; - public static List Procs = null; - public static Dictionary AllTypes = null; +namespace DMDisassembler; - private static readonly string NoTypeSelectedMessage = "No type is selected"; +internal class Program { + public static DreamCompiledJson CompiledJson; + public static DMProc GlobalInitProc = null; + public static List Procs = null; + public static Dictionary AllTypes = null; - private static DMType _selectedType = null; + private static readonly string NoTypeSelectedMessage = "No type is selected"; - static void Main(string[] args) { - if (args.Length == 0 || Path.GetExtension(args[0]) != ".json") { - Console.WriteLine("The json output of DMCompiler must be provided as an argument"); + private static DMType _selectedType = null; - return; - } + static void Main(string[] args) { + if (args.Length == 0 || Path.GetExtension(args[0]) != ".json") { + Console.WriteLine("The json output of DMCompiler must be provided as an argument"); - string compiledJsonText = File.ReadAllText(args[0]); + return; + } - CompiledJson = JsonSerializer.Deserialize(compiledJsonText); - if (CompiledJson.GlobalInitProc != null) GlobalInitProc = new DMProc(CompiledJson.GlobalInitProc); - LoadAllProcs(); - LoadAllTypes(); + string compiledJsonText = File.ReadAllText(args[0]); - bool acceptingCommands = true; - while (acceptingCommands) { - if (_selectedType != null) { - Console.Write(_selectedType.Path); - } - Console.Write("> "); + CompiledJson = JsonSerializer.Deserialize(compiledJsonText); + if (CompiledJson.GlobalInitProc != null) GlobalInitProc = new DMProc(CompiledJson.GlobalInitProc); + LoadAllProcs(); + LoadAllTypes(); - string input = Console.ReadLine(); - if (input == null) { - // EOF - break; - } - string[] split = input.Split(" "); - string command = split[0].ToLower(); - - switch (command) { - case "q": acceptingCommands = false; break; - case "search": Search(split); break; - case "sel": - case "select": Select(split); break; - case "list": List(split); break; - case "d": - case "decompile": Decompile(split); break; - case "test-all": TestAll(); break; - default: Console.WriteLine("Invalid command \"" + command + "\""); break; - } + bool acceptingCommands = true; + while (acceptingCommands) { + if (_selectedType != null) { + Console.Write(_selectedType.Path); } - } - - private static void Search(string[] args) { - if (args.Length < 3) { - Console.WriteLine("search type|proc [name]"); + Console.Write("> "); - return; + string input = Console.ReadLine(); + if (input == null) { + // EOF + break; } + string[] split = input.Split(" "); + string command = split[0].ToLower(); - string type = args[1]; - string name = args[2]; - if (type == "type") { - foreach (DreamPath typePath in AllTypes.Keys) { - if (typePath.PathString.Contains(name)) Console.WriteLine(typePath); - } - } else if (type == "proc") { - if (_selectedType == null) { - Console.WriteLine(NoTypeSelectedMessage); + switch (command) { + case "q": acceptingCommands = false; break; + case "search": Search(split); break; + case "sel": + case "select": Select(split); break; + case "list": List(split); break; + case "d": + case "decompile": Decompile(split); break; + case "test-all": TestAll(); break; + default: Console.WriteLine("Invalid command \"" + command + "\""); break; + } + } + } - return; - } + private static void Search(string[] args) { + if (args.Length < 3) { + Console.WriteLine("search type|proc [name]"); - foreach (string procName in _selectedType.Procs.Keys) { - if (procName.Contains(name)) Console.WriteLine(procName); - } - } else { - Console.WriteLine("Invalid search type \"" + type + "\""); - } + return; } - private static void Select(string[] args) { - if (args.Length < 2) { - Console.WriteLine("select [type]"); + string type = args[1]; + string name = args[2]; + if (type == "type") { + foreach (string typePath in AllTypes.Keys) { + if (typePath.Contains(name)) Console.WriteLine(typePath); + } + } else if (type == "proc") { + if (_selectedType == null) { + Console.WriteLine(NoTypeSelectedMessage); return; } - string type = args[1]; - if (AllTypes.TryGetValue(new DreamPath(type), out DMType dmType)) { - _selectedType = dmType; - } else { - Console.WriteLine("Invalid type \"" + type + "\""); + foreach (string procName in _selectedType.Procs.Keys) { + if (procName.Contains(name)) Console.WriteLine(procName); } + } else { + Console.WriteLine("Invalid search type \"" + type + "\""); } + } - private static void List(string[] args) { - if (args.Length < 2) { - Console.WriteLine("list procs|globals"); + private static void Select(string[] args) { + if (args.Length < 2) { + Console.WriteLine("select [type]"); - return; - } + return; + } - string what = args[1]; - switch (what) { - case "procs": - if (_selectedType == null) { - Console.WriteLine(NoTypeSelectedMessage); - break; - } + string type = args[1]; + if (AllTypes.TryGetValue(type, out DMType dmType)) { + _selectedType = dmType; + } else { + Console.WriteLine("Invalid type \"" + type + "\""); + } + } - foreach (string procName in _selectedType.Procs.Keys) { - Console.WriteLine(procName); - } + private static void List(string[] args) { + if (args.Length < 2) { + Console.WriteLine("list procs|globals"); - break; - case "globals": - if (CompiledJson.Globals == null) { - Console.WriteLine("There are no globals"); - break; - } - - for (int i = 0; i < CompiledJson.Globals.GlobalCount; i++) { - Console.Write(i); - Console.Write(": "); - Console.WriteLine(CompiledJson.Globals.Names[i]); - } + return; + } + string what = args[1]; + switch (what) { + case "procs": + if (_selectedType == null) { + Console.WriteLine(NoTypeSelectedMessage); break; - } - } + } - private static void Decompile(string[] args) { - if (args.Length < 2) { - Console.WriteLine("decompile [name]"); + foreach (string procName in _selectedType.Procs.Keys) { + Console.WriteLine(procName); + } - return; - } + break; + case "globals": + if (CompiledJson.Globals == null) { + Console.WriteLine("There are no globals"); + break; + } - string name = args[1]; - if (name == "" || (name == "" && (_selectedType == null || _selectedType.Path == DreamPath.Root))) { - if (GlobalInitProc != null) { - Console.WriteLine(GlobalInitProc.Decompile()); - } else { - Console.WriteLine("There is no global init proc"); + for (int i = 0; i < CompiledJson.Globals.GlobalCount; i++) { + Console.Write(i); + Console.Write(": "); + Console.WriteLine(CompiledJson.Globals.Names[i]); } - return; - } + break; + } + } - if (_selectedType == null) { - Console.WriteLine(NoTypeSelectedMessage); - return; - } + private static void Decompile(string[] args) { + if (args.Length < 2) { + Console.WriteLine("decompile [name]"); - if (name == "") { - if (_selectedType.InitProc != null) { - Console.WriteLine(_selectedType.InitProc.Decompile()); - } else { - Console.WriteLine("Selected type does not have an init proc"); - } - } else if (_selectedType.Procs.TryGetValue(name, out DMProc proc)) { - Console.WriteLine(proc.Decompile()); + return; + } + + string name = args[1]; + if (name == "" || (name == "" && (_selectedType == null || _selectedType.Path == "/"))) { + if (GlobalInitProc != null) { + Console.WriteLine(GlobalInitProc.Decompile()); } else { - Console.WriteLine("No procs named \"" + name + "\""); + Console.WriteLine("There is no global init proc"); } + + return; } - private static void LoadAllProcs() { - Procs = new List(CompiledJson.Procs.Length); + if (_selectedType == null) { + Console.WriteLine(NoTypeSelectedMessage); + return; + } - foreach (ProcDefinitionJson procDef in CompiledJson.Procs) { - Procs.Add(new DMProc(procDef)); + if (name == "") { + if (_selectedType.InitProc != null) { + Console.WriteLine(_selectedType.InitProc.Decompile()); + } else { + Console.WriteLine("Selected type does not have an init proc"); } + } else if (_selectedType.Procs.TryGetValue(name, out DMProc proc)) { + Console.WriteLine(proc.Decompile()); + } else { + Console.WriteLine("No procs named \"" + name + "\""); } + } - private static void LoadAllTypes() { - AllTypes = new Dictionary(CompiledJson.Types.Length); + private static void LoadAllProcs() { + Procs = new List(CompiledJson.Procs.Length); - foreach (DreamTypeJson json in CompiledJson.Types) { - AllTypes.Add(new DreamPath(json.Path), new DMType(json)); - } + foreach (ProcDefinitionJson procDef in CompiledJson.Procs) { + Procs.Add(new DMProc(procDef)); + } + } - //Add global procs to the root type - DMType globalType = AllTypes[DreamPath.Root]; - foreach (int procId in CompiledJson.GlobalProcs) { - var proc = Procs[procId]; + private static void LoadAllTypes() { + AllTypes = new Dictionary(CompiledJson.Types.Length); - globalType.Procs.Add(proc.Name, proc); - } + foreach (DreamTypeJson json in CompiledJson.Types) { + AllTypes.Add(json.Path, new DMType(json)); } - private static void TestAll() { - int errored = 0, all = 0; - foreach (DMProc proc in Procs) { - string value = proc.Decompile(); - if (proc.exception != null) { - Console.WriteLine("Error disassembling " + proc.Name); - Console.WriteLine(value); - ++errored; - } - ++all; + //Add global procs to the root type + DMType globalType = AllTypes["/"]; + foreach (int procId in CompiledJson.GlobalProcs) { + var proc = Procs[procId]; + + globalType.Procs.Add(proc.Name, proc); + } + } + + private static void TestAll() { + int errored = 0, all = 0; + foreach (DMProc proc in Procs) { + string value = proc.Decompile(); + if (proc.exception != null) { + Console.WriteLine("Error disassembling " + proc.Name); + Console.WriteLine(value); + ++errored; } - Console.WriteLine($"Errors in {errored}/{all} procs"); + ++all; } + Console.WriteLine($"Errors in {errored}/{all} procs"); } } diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index f796d038d2..50f7307c8f 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -18,7 +18,7 @@ public enum ProcStatus { public abstract class DreamProc { public readonly int Id; - public readonly DreamPath OwningType; + public readonly TreeEntry OwningType; public readonly string Name; public readonly bool IsVerb; @@ -38,7 +38,7 @@ public abstract class DreamProc { private readonly string? _verbName; private readonly string? _verbDesc; - protected DreamProc(int id, DreamPath owningType, string name, DreamProc? superProc, ProcAttributes attributes, List? argumentNames, List? argumentTypes, string? verbName, string? verbCategory, string? verbDesc, sbyte invisibility, bool isVerb = false) { + protected DreamProc(int id, TreeEntry owningType, string name, DreamProc? superProc, ProcAttributes attributes, List? argumentNames, List? argumentTypes, string? verbName, string? verbCategory, string? verbDesc, sbyte invisibility, bool isVerb = false) { Id = id; OwningType = owningType; Name = name; @@ -88,7 +88,7 @@ public DreamValue GetField(string field) { public override string ToString() { var procElement = (SuperProc == null) ? (IsVerb ? "verb/" : "proc/") : String.Empty; // Has "proc/" only if it's not an override - return OwningType == DreamPath.Root ? $"/{procElement}{Name}" : $"{OwningType}/{procElement}{Name}"; + return $"{OwningType.Path}{(OwningType.Path.EndsWith('/') ? string.Empty : "/")}{procElement}{Name}"; } } diff --git a/OpenDreamRuntime/DreamValue.cs b/OpenDreamRuntime/DreamValue.cs index e179338265..43ca2e9b03 100644 --- a/OpenDreamRuntime/DreamValue.cs +++ b/OpenDreamRuntime/DreamValue.cs @@ -380,7 +380,7 @@ public string Stringify() { return rscPath.ResourcePath; case DreamValueType.DreamType: TryGetValueAsType(out var type); - return type.Path.PathString; + return type.Path; case DreamValueType.DreamProc: var proc = MustGetValueAsProc(); @@ -460,7 +460,7 @@ public override void Write(Utf8JsonWriter writer, DreamValue value, JsonSerializ if (dreamObject == null) { writer.WriteNull("Value"); } else { - writer.WriteString("Value", dreamObject.ObjectDefinition.Type.PathString); + writer.WriteString("Value", dreamObject.ObjectDefinition.Type); if (dreamObject is not DreamObjectIcon icon) { throw new NotImplementedException($"Json serialization for {value} is not implemented"); @@ -502,7 +502,7 @@ public override DreamValue Read(ref Utf8JsonReader reader, Type typeToConvert, J if (objectTypePath == null) { value = DreamValue.Null; } else { - var objectDef = _objectTree.GetTreeEntry(new DreamPath(objectTypePath)).ObjectDefinition; + var objectDef = _objectTree.GetTreeEntry(objectTypePath).ObjectDefinition; if (!objectDef.IsSubtypeOf(_objectTree.Icon)) { throw new NotImplementedException($"Json deserialization for type {objectTypePath} is not implemented"); } diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 303559e56d..0b41ba1d01 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -427,7 +427,7 @@ public override string ToString() { return $"{ObjectDefinition.Type}{{name=\"{name}\"}}"; } - return ObjectDefinition.Type.ToString(); + return ObjectDefinition.Type; } } } diff --git a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs index c6eab43de4..123fd002d6 100644 --- a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs +++ b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs @@ -31,7 +31,7 @@ public sealed class DreamObjectDefinition { public readonly MetaDataSystem? MetaDataSystem; public readonly TreeEntry TreeEntry; - public DreamPath Type => TreeEntry.Path; + public string Type => TreeEntry.Path; public DreamObjectDefinition? Parent => TreeEntry.ParentEntry?.ObjectDefinition; public int? InitializationProc; public bool NoConstructors { diff --git a/OpenDreamRuntime/Objects/DreamObjectTree.cs b/OpenDreamRuntime/Objects/DreamObjectTree.cs index fc3b4d7c3a..22f19fbccd 100644 --- a/OpenDreamRuntime/Objects/DreamObjectTree.cs +++ b/OpenDreamRuntime/Objects/DreamObjectTree.cs @@ -46,7 +46,7 @@ public sealed class DreamObjectTree { public TreeEntry Obj { get; private set; } public TreeEntry Mob { get; private set; } - private readonly Dictionary _pathToType = new(); + private readonly Dictionary _pathToType = new(); private Dictionary _globalProcIds; [Dependency] private readonly AtomManager _atomManager = default!; @@ -67,6 +67,12 @@ public sealed class DreamObjectTree { private MetaDataSystem? _metaDataSystem; public void LoadJson(DreamCompiledJson json) { + var types = json.Types ?? Array.Empty(); + if (types.Length == 0 || types[0].Path != "/") + throw new ArgumentException("The first type must be root!", nameof(json)); + + Root = new("/", 0); + _entitySystemManager.TryGetEntitySystem(out _appearanceSystem); _entitySystemManager.TryGetEntitySystem(out _transformSystem); _entitySystemManager.TryGetEntitySystem(out _pvsOverrideSystem); @@ -74,21 +80,18 @@ public void LoadJson(DreamCompiledJson json) { Strings = json.Strings ?? new(); if (json.GlobalInitProc is { } initProcDef) { - GlobalInitProc = new DMProc(0, DreamPath.Root, initProcDef, "", _dreamManager, _atomManager, _dreamMapManager, _dreamDebugManager, _dreamResourceManager, this, _procScheduler); + GlobalInitProc = new DMProc(0, Root, initProcDef, "", _dreamManager, _atomManager, _dreamMapManager, _dreamDebugManager, _dreamResourceManager, this, _procScheduler); } else { GlobalInitProc = null; } - var types = json.Types ?? Array.Empty(); var procs = json.Procs; var globalProcs = json.GlobalProcs; - // Load procs first so types can set their init proc's super proc - LoadProcsFromJson(types, procs, globalProcs); - LoadTypesFromJson(types); + LoadTypesFromJson(types, procs, globalProcs); } - public TreeEntry GetTreeEntry(DreamPath path) { + public TreeEntry GetTreeEntry(string path) { if (!_pathToType.TryGetValue(path, out TreeEntry? type)) { throw new Exception($"Object '{path}' does not exist"); } @@ -100,7 +103,7 @@ public TreeEntry GetTreeEntry(int typeId) { return Types[typeId]; } - public bool TryGetTreeEntry(DreamPath path, [NotNullWhen(true)] out TreeEntry? treeEntry) { + public bool TryGetTreeEntry(string path, [NotNullWhen(true)] out TreeEntry? treeEntry) { return _pathToType.TryGetValue(path, out treeEntry); } @@ -247,40 +250,41 @@ public DreamValue GetDreamValueFromJsonElement(object? value) { } } - private void LoadTypesFromJson(DreamTypeJson[] types) { - Dictionary pathToTypeId = new(); + private void LoadTypesFromJson(DreamTypeJson[] types, ProcDefinitionJson[]? procs, int[]? globalProcs) { Types = new TreeEntry[types.Length]; //First pass: Create types and set them up for initialization - for (int i = 0; i < Types.Length; i++) { - DreamPath path = new DreamPath(types[i].Path); + Types[0] = Root; + for (int i = 1; i < Types.Length; i++) { + var path = types[i].Path; var type = new TreeEntry(path, i); Types[i] = type; _pathToType[path] = type; - pathToTypeId[path] = i; } - Root = GetTreeEntry(DreamPath.Root); - World = GetTreeEntry(DreamPath.World); - List = GetTreeEntry(DreamPath.List); - Client = GetTreeEntry(DreamPath.Client); - Datum = GetTreeEntry(DreamPath.Datum); - Sound = GetTreeEntry(DreamPath.Sound); - Matrix = GetTreeEntry(DreamPath.Matrix); - Exception = GetTreeEntry(DreamPath.Exception); - Savefile = GetTreeEntry(DreamPath.Savefile); - Regex = GetTreeEntry(DreamPath.Regex); - Filter = GetTreeEntry(DreamPath.Filter); - Icon = GetTreeEntry(DreamPath.Icon); - Image = GetTreeEntry(DreamPath.Image); - MutableAppearance = GetTreeEntry(DreamPath.MutableAppearance); - Atom = GetTreeEntry(DreamPath.Atom); - Area = GetTreeEntry(DreamPath.Area); - Turf = GetTreeEntry(DreamPath.Turf); - Movable = GetTreeEntry(DreamPath.Movable); - Obj = GetTreeEntry(DreamPath.Obj); - Mob = GetTreeEntry(DreamPath.Mob); + World = GetTreeEntry("/world"); + List = GetTreeEntry("/list"); + Client = GetTreeEntry("/client"); + Datum = GetTreeEntry("/datum"); + Sound = GetTreeEntry("/sound"); + Matrix = GetTreeEntry("/matrix"); + Exception = GetTreeEntry("/exception"); + Savefile = GetTreeEntry("/savefile"); + Regex = GetTreeEntry("/regex"); + Filter = GetTreeEntry("/dm_filter"); + Icon = GetTreeEntry("/icon"); + Image = GetTreeEntry("/image"); + MutableAppearance = GetTreeEntry("/mutable_appearance"); + Atom = GetTreeEntry("/atom"); + Area = GetTreeEntry("/area"); + Turf = GetTreeEntry("/turf"); + Movable = GetTreeEntry("/atom/movable"); + Obj = GetTreeEntry("/obj"); + Mob = GetTreeEntry("/mob"); + + // Load procs first so types can set their init proc's super proc + LoadProcsFromJson(procs, globalProcs); //Second pass: Set each type's parent and children for (int i = 0; i < Types.Length; i++) { @@ -300,7 +304,7 @@ private void LoadTypesFromJson(DreamTypeJson[] types) { //Thus, the enumeration of GetAllDescendants() uint treeIndex = 0; foreach (TreeEntry type in GetAllDescendants(Root)) { - int typeId = pathToTypeId[type.Path]; + int typeId = type.Id; DreamTypeJson jsonType = types[typeId]; var definition = new DreamObjectDefinition(_dreamManager, this, _atomManager, _dreamMapManager, _mapManager, _dreamResourceManager, _walkManager, _entityManager, _playerManager, _serializationManager, _appearanceSystem, _transformSystem, _pvsOverrideSystem, _metaDataSystem, type); @@ -341,7 +345,7 @@ private void LoadTypesFromJson(DreamTypeJson[] types) { //Fifth pass: Set atom's name and text foreach (TreeEntry type in GetAllDescendants(Atom)) { if (type.ObjectDefinition.Variables["name"].IsNull) - type.ObjectDefinition.Variables["name"] = new(type.Path.LastElement!.Replace("_", " ")); + type.ObjectDefinition.Variables["name"] = new(type.Name.Replace("_", " ")); if (type.ObjectDefinition.Variables["text"].IsNull && type.ObjectDefinition.Variables["name"].TryGetValueAsString(out var name)) { type.ObjectDefinition.Variables["text"] = new DreamValue(string.IsNullOrEmpty(name) ? string.Empty : name[..1]); @@ -379,19 +383,19 @@ private void LoadVariablesFromJson(DreamObjectDefinition objectDefinition, Dream } } - public DreamProc LoadProcJson(int id, DreamTypeJson[] types, ProcDefinitionJson procDefinition) { - DreamPath owningType = new DreamPath(types[procDefinition.OwningTypeId].Path); + public DreamProc LoadProcJson(int id, ProcDefinitionJson procDefinition) { + TreeEntry owningType = Types[procDefinition.OwningTypeId]; return new DMProc(id, owningType, procDefinition, null, _dreamManager, _atomManager, _dreamMapManager, _dreamDebugManager, _dreamResourceManager, this, _procScheduler); } - private void LoadProcsFromJson(DreamTypeJson[] types, ProcDefinitionJson[]? jsonProcs, int[]? jsonGlobalProcs) { + private void LoadProcsFromJson(ProcDefinitionJson[]? jsonProcs, int[]? jsonGlobalProcs) { Procs.Clear(); if (jsonProcs != null) { Procs.EnsureCapacity(jsonProcs.Length); foreach (var proc in jsonProcs) { - Procs.Add(LoadProcJson(Procs.Count, types, proc)); + Procs.Add(LoadProcJson(Procs.Count, proc)); } } @@ -406,7 +410,7 @@ private void LoadProcsFromJson(DreamTypeJson[] types, ProcDefinitionJson[]? json } } - internal NativeProc CreateNativeProc(DreamPath owningType, NativeProc.HandlerFn func) { + internal NativeProc CreateNativeProc(TreeEntry owningType, NativeProc.HandlerFn func) { var (name, defaultArgumentValues, argumentNames) = NativeProc.GetNativeInfo(func); var proc = new NativeProc(Procs.Count, owningType, name, argumentNames, defaultArgumentValues, func, _dreamManager, _atomManager, _dreamMapManager, _dreamResourceManager, _walkManager, this); @@ -414,7 +418,7 @@ internal NativeProc CreateNativeProc(DreamPath owningType, NativeProc.HandlerFn return proc; } - private AsyncNativeProc CreateAsyncNativeProc(DreamPath owningType, Func> func) { + private AsyncNativeProc CreateAsyncNativeProc(TreeEntry owningType, Func> func) { var (name, defaultArgumentValues, argumentNames) = NativeProc.GetNativeInfo(func); var proc = new AsyncNativeProc(Procs.Count, owningType, name, argumentNames, defaultArgumentValues, func); @@ -424,26 +428,26 @@ private AsyncNativeProc CreateAsyncNativeProc(DreamPath owningType, Func> func) { var (name, defaultArgumentValues, argumentNames) = NativeProc.GetNativeInfo(func); - var proc = new AsyncNativeProc(_globalProcIds[name], DreamPath.Root, name, argumentNames, defaultArgumentValues, func); + var proc = new AsyncNativeProc(_globalProcIds[name], Root, name, argumentNames, defaultArgumentValues, func); Procs[proc.Id] = proc; } internal void SetNativeProc(TreeEntry type, NativeProc.HandlerFn func) { - var proc = CreateNativeProc(type.Path, func); + var proc = CreateNativeProc(type, func); type.ObjectDefinition.SetProcDefinition(proc.Name, proc.Id); } public void SetNativeProc(TreeEntry type, Func> func) { - var proc = CreateAsyncNativeProc(type.Path, func); + var proc = CreateAsyncNativeProc(type, func); type.ObjectDefinition.SetProcDefinition(proc.Name, proc.Id); } @@ -464,7 +468,8 @@ private IEnumerable TraversePostOrder(TreeEntry from) { } public sealed class TreeEntry { - public DreamPath Path; + public readonly string Name; + public readonly string Path; public readonly int Id; public DreamObjectDefinition ObjectDefinition; public TreeEntry ParentEntry; @@ -481,7 +486,10 @@ public sealed class TreeEntry { /// public uint ChildCount; - public TreeEntry(DreamPath path, int id) { + public TreeEntry(string path, int id) { + int lastSlash = path.LastIndexOf('/'); + + Name = (lastSlash != -1) ? path.Substring(lastSlash + 1) : path; Path = path; Id = id; } @@ -493,6 +501,6 @@ public bool IsSubtypeOf(TreeEntry ancestor) { } public override string ToString() { - return Path.PathString; + return Path; } } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectSavefile.cs b/OpenDreamRuntime/Objects/Types/DreamObjectSavefile.cs index 64f61b92cc..1900d780a0 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectSavefile.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectSavefile.cs @@ -169,10 +169,12 @@ public override void OperatorIndexAssign(DreamValue index, DreamValue value) { } private void ChangeDirectory(string path) { - _currentDirPath = new DreamPath(_currentDirPath).AddToPath(path).PathString; - - if (!Directories.ContainsKey(_currentDirPath)) { - Directories.Add(_currentDirPath, new SavefileDirectory()); + if (path.StartsWith('/')) { + _currentDirPath = path; + } else { + _currentDirPath += path; } + + Directories.TryAdd(_currentDirPath, new SavefileDirectory()); } } diff --git a/OpenDreamRuntime/Procs/AsyncNativeProc.cs b/OpenDreamRuntime/Procs/AsyncNativeProc.cs index cc9576358f..ff86a77f2d 100644 --- a/OpenDreamRuntime/Procs/AsyncNativeProc.cs +++ b/OpenDreamRuntime/Procs/AsyncNativeProc.cs @@ -169,7 +169,7 @@ public DreamValue GetArgument(int argumentPosition, string argumentName) { private readonly Dictionary? _defaultArgumentValues; private readonly Func> _taskFunc; - public AsyncNativeProc(int id, DreamPath owningType, string name, List argumentNames, Dictionary defaultArgumentValues, Func> taskFunc) + public AsyncNativeProc(int id, TreeEntry owningType, string name, List argumentNames, Dictionary defaultArgumentValues, Func> taskFunc) : base(id, owningType, name, null, ProcAttributes.None, argumentNames, null, null, null, null, 0) { _defaultArgumentValues = defaultArgumentValues; _taskFunc = taskFunc; diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index d3d0ff4675..40a5f7049f 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -167,7 +167,7 @@ public static ProcStatus CreateObject(DMProcState state) { var val = state.Pop(); if (!val.TryGetValueAsType(out var objectType)) { if (val.TryGetValueAsString(out var pathString)) { - if (!state.Proc.ObjectTree.TryGetTreeEntry(new DreamPath(pathString), out objectType)) { + if (!state.Proc.ObjectTree.TryGetTreeEntry(pathString, out objectType)) { ThrowCannotCreateUnknownObject(val); } } else { diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index b8c155ed82..a5d2a3c796 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -29,7 +29,7 @@ public sealed class DMProc : DreamProc { private readonly int _maxStackSize; - public DMProc(int id, DreamPath owningType, ProcDefinitionJson json, string? name, DreamManager dreamManager, AtomManager atomManager, IDreamMapManager dreamMapManager, IDreamDebugManager dreamDebugManager, DreamResourceManager dreamResourceManager, DreamObjectTree objectTree, ProcScheduler procScheduler) + public DMProc(int id, TreeEntry owningType, ProcDefinitionJson json, string? name, DreamManager dreamManager, AtomManager atomManager, IDreamMapManager dreamMapManager, IDreamDebugManager dreamDebugManager, DreamResourceManager dreamResourceManager, DreamObjectTree objectTree, ProcScheduler procScheduler) : base(id, owningType, name ?? json.Name, null, json.Attributes, GetArgumentNames(json), GetArgumentTypes(json), json.VerbName, json.VerbCategory, json.VerbDesc, json.Invisibility, json.IsVerb) { Bytecode = json.Bytecode ?? Array.Empty(); LocalNames = json.Locals; @@ -425,8 +425,8 @@ public override void ReturnedInto(DreamValue value) { } public override void AppendStackFrame(StringBuilder builder) { - if (Proc.OwningType != DreamPath.Root) { - builder.Append(Proc.OwningType.ToString()); + if (Proc.OwningType != Proc.ObjectTree.Root) { + builder.Append(Proc.OwningType); builder.Append('/'); } diff --git a/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs b/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs index be15bd06db..46d5b59cf5 100644 --- a/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs +++ b/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs @@ -150,7 +150,7 @@ public void HandleFirstResume(DMProcState state) { // Check for a function breakpoint List? hit = null; - if (_possibleFunctionBreakpoints.TryGetValue((state.Proc.OwningType.PathString, state.Proc.Name), out var slot)) { + if (_possibleFunctionBreakpoints.TryGetValue((state.Proc.OwningType.Path, state.Proc.Name), out var slot)) { foreach (var bp in slot.Breakpoints) { if (TestBreakpoint(bp)) { hit ??= new(1); @@ -160,7 +160,7 @@ public void HandleFirstResume(DMProcState state) { } if (hit != null) { - Output($"Function breakpoint hit at {state.Proc.OwningType.PathString}::{state.Proc.Name}"); + Output($"Function breakpoint hit at {state.Proc.OwningType.Path}::{state.Proc.Name}"); Stop(state.Thread, new StoppedEvent { Reason = StoppedEvent.ReasonFunctionBreakpoint, HitBreakpointIds = hit @@ -422,7 +422,7 @@ private void InitializePossibleBreakpoints() { private IEnumerable<(string Type, string Proc)> IterateProcs() { foreach (var proc in _objectTree.Procs) { - yield return (proc.OwningType.PathString, proc.Name); + yield return (proc.OwningType.Path, proc.Name); } } @@ -692,7 +692,7 @@ private void HandleRequestScopes(DebugAdapterClient client, RequestScopes reques } private IEnumerable ExpandArguments(RequestVariables req, DMProcState dmFrame) { - if (dmFrame.Proc.OwningType != OpenDreamShared.Dream.DreamPath.Root) { + if (dmFrame.Proc.OwningType != _objectTree.Root) { yield return DescribeValue("src", new(dmFrame.Instance)); } yield return DescribeValue("usr", new(dmFrame.Usr)); diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs index c078826a2e..d4dabd739c 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs @@ -155,7 +155,7 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) { /// Sets a native proc that can be overriden by DM code /// private static void SetOverridableNativeProc(DreamObjectTree objectTree, TreeEntry type, NativeProc.HandlerFn func) { - var nativeProc = objectTree.CreateNativeProc(type.Path, func); + var nativeProc = objectTree.CreateNativeProc(type, func); var proc = objectTree.World.ObjectDefinition.GetProc(nativeProc.Name); if (proc.SuperProc == null) { // This proc was never overriden so just replace it diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs index 4489bcd9a6..224e5b5ea2 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs @@ -1110,7 +1110,7 @@ private static void JsonEncode(Utf8JsonWriter writer, DreamValue value) { } else if (value.TryGetValueAsString(out var text)) writer.WriteStringValue(text); else if (value.TryGetValueAsType(out var type)) - writer.WriteStringValue(type.Path.PathString); + writer.WriteStringValue(type.Path); else if (value.TryGetValueAsProc(out var proc)) writer.WriteStringValue(proc.ToString()); else if (value.TryGetValueAsDreamList(out var list)) { @@ -2508,17 +2508,15 @@ public static DreamValue NativeProc_text2num(NativeProc.Bundle bundle, DreamObje [DreamProc("text2path")] [DreamProcParameter("T", Type = DreamValueTypeFlag.String)] public static DreamValue NativeProc_text2path(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) { - if (!bundle.GetArgument(0, "T").TryGetValueAsString(out var text) || string.IsNullOrWhiteSpace(text)) { + if (!bundle.GetArgument(0, "T").TryGetValueAsString(out var path) || string.IsNullOrWhiteSpace(path)) { return DreamValue.Null; } - DreamPath path = new DreamPath(text); - bool isVerb = false; - int procElementIndex = path.FindElement("proc"); + int procElementIndex = path.IndexOf("/proc/", StringComparison.Ordinal); if (procElementIndex == -1) { - procElementIndex = path.FindElement("verb"); + procElementIndex = path.IndexOf("/verb/", StringComparison.Ordinal); if (procElementIndex != -1) isVerb = true; } @@ -2527,17 +2525,17 @@ public static DreamValue NativeProc_text2path(NativeProc.Bundle bundle, DreamObj string? procName = null; if (isProcPath) { - procName = path.LastElement; + procName = path.Substring(path.LastIndexOf('/') + 1); if (procElementIndex == 0) { // global procs - if (procName != null && bundle.ObjectTree.TryGetGlobalProc(procName, out var globalProc) && globalProc.IsVerb == isVerb) + if (bundle.ObjectTree.TryGetGlobalProc(procName, out var globalProc) && globalProc.IsVerb == isVerb) return new DreamValue(globalProc); else return DreamValue.Null; } } - DreamPath typePath = isProcPath ? path.FromElements(0, procElementIndex) : path; + string typePath = isProcPath ? path.Substring(0, procElementIndex) : path; if (!bundle.ObjectTree.TryGetTreeEntry(typePath, out var type) || type == bundle.ObjectTree.Root) return DreamValue.Null; @@ -2724,16 +2722,14 @@ public static DreamValue NativeProc_typesof(NativeProc.Bundle bundle, DreamObjec type = typeObj.ObjectDefinition.TreeEntry; } else if (typeValue.TryGetValueAsString(out var typeString)) { - DreamPath path = new DreamPath(typeString); - - if (path.LastElement == "proc") { - type = bundle.ObjectTree.GetTreeEntry(path.FromElements(0, -2)); + if (typeString.EndsWith("/proc")) { + type = bundle.ObjectTree.GetTreeEntry(typeString.Substring(0, typeString.Length - 5)); addingProcs = type.ObjectDefinition.Procs.Values; - } else if (path.LastElement == "verb") { - type = bundle.ObjectTree.GetTreeEntry(path.FromElements(0, -2)); + } else if (typeString.EndsWith("/verb")) { + type = bundle.ObjectTree.GetTreeEntry(typeString.Substring(0, typeString.Length - 5)); addingProcs = type.ObjectDefinition.Verbs; } else { - type = bundle.ObjectTree.GetTreeEntry(path); + type = bundle.ObjectTree.GetTreeEntry(typeString); } } else { continue; diff --git a/OpenDreamRuntime/Procs/NativeProc.cs b/OpenDreamRuntime/Procs/NativeProc.cs index a61de283a6..66384711b0 100644 --- a/OpenDreamRuntime/Procs/NativeProc.cs +++ b/OpenDreamRuntime/Procs/NativeProc.cs @@ -83,7 +83,7 @@ private DreamValue GetArgumentFallback(string argumentName) { private readonly Dictionary? _defaultArgumentValues; private readonly delegate* _handler; - public NativeProc(int id, DreamPath owningType, string name, List argumentNames, Dictionary defaultArgumentValues, HandlerFn handler, DreamManager dreamManager, AtomManager atomManager, IDreamMapManager mapManager, DreamResourceManager resourceManager, WalkManager walkManager, DreamObjectTree objectTree) + public NativeProc(int id, TreeEntry owningType, string name, List argumentNames, Dictionary defaultArgumentValues, HandlerFn handler, DreamManager dreamManager, AtomManager atomManager, IDreamMapManager mapManager, DreamResourceManager resourceManager, WalkManager walkManager, DreamObjectTree objectTree) : base(id, owningType, name, null, ProcAttributes.None, argumentNames, null, null, null, null, 0) { _defaultArgumentValues = defaultArgumentValues; _handler = (delegate*)handler.Method.MethodHandle.GetFunctionPointer(); diff --git a/OpenDreamShared/Dream/DreamPath.cs b/OpenDreamShared/Dream/DreamPath.cs deleted file mode 100644 index 66e8101294..0000000000 --- a/OpenDreamShared/Dream/DreamPath.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; - -namespace OpenDreamShared.Dream { - public struct DreamPath { - public static readonly DreamPath Root = new DreamPath("/"); - public static readonly DreamPath Exception = new DreamPath("/exception"); - public static readonly DreamPath List = new DreamPath("/list"); - public static readonly DreamPath Regex = new DreamPath("/regex"); - public static readonly DreamPath Savefile = new DreamPath("/savefile"); - public static readonly DreamPath Sound = new DreamPath("/sound"); - public static readonly DreamPath Image = new DreamPath("/image"); - public static readonly DreamPath Icon = new DreamPath("/icon"); - public static readonly DreamPath MutableAppearance = new DreamPath("/mutable_appearance"); - public static readonly DreamPath World = new DreamPath("/world"); - public static readonly DreamPath Client = new DreamPath("/client"); - public static readonly DreamPath Datum = new DreamPath("/datum"); - public static readonly DreamPath Matrix = new DreamPath("/matrix"); - public static readonly DreamPath Atom = new DreamPath("/atom"); - public static readonly DreamPath Area = new DreamPath("/area"); - public static readonly DreamPath Turf = new DreamPath("/turf"); - public static readonly DreamPath Movable = new DreamPath("/atom/movable"); - public static readonly DreamPath Obj = new DreamPath("/obj"); - public static readonly DreamPath Mob = new DreamPath("/mob"); - public static readonly DreamPath Filter = new DreamPath("/dm_filter"); - - public enum PathType { - Absolute, - Relative, - - //TODO: These really shouldn't be here - DownwardSearch, - UpwardSearch - } - - [JsonIgnore] - public string? LastElement { - get => Elements.Length > 0 ? Elements.Last() : null; - } - - [JsonIgnore] - public string[] Elements { - get => _elements; - set { - _elements = value; - _pathString = null; - } - } - - public string PathString { - get { - if (_pathString != null) return _pathString; - - _pathString = Type switch { - PathType.Absolute => "/", - PathType.DownwardSearch => ":", - PathType.UpwardSearch => ".", - _ => string.Empty - }; - - // Elements is usually small enough for this to be faster than StringBuilder - _pathString += string.Join("/", Elements); - - return _pathString; - } - set => SetFromString(value); - } - - public PathType Type; - - private string[] _elements; - private string? _pathString; - - public DreamPath(string path) { - Type = PathType.Absolute; - _elements = Array.Empty(); // Set in SetFromString() - _pathString = null; - - SetFromString(path); - } - - public DreamPath(PathType type, string[] elements) { - Type = type; - _elements = elements; - _pathString = null; - - Normalize(true); - } - - public void SetFromString(string rawPath) { - char pathTypeChar = rawPath[0]; - string[] tempElements = rawPath.Split('/', StringSplitOptions.RemoveEmptyEntries); - bool skipFirstChar = false; - - switch (pathTypeChar) { - case '/': - Type = PathType.Absolute; - // No need to skip the first char, as it will end up as an empty entry in tempElements - break; - case ':': - Type = PathType.DownwardSearch; - skipFirstChar = true; - break; - case '.': - Type = PathType.UpwardSearch; - skipFirstChar = true; - break; - default: - Type = PathType.Relative; - break; - } - - if (skipFirstChar) { - // Skip the '/', ':' or '.' if needed - tempElements[0] = tempElements[0][1..]; - } - - Elements = tempElements; - Normalize(false); - } - - /// - /// Checks if the DreamPath is a descendant of another. NOTE: For type inheritance, use IsSubtypeOf() - /// - /// Path to compare to. - public bool IsDescendantOf(DreamPath path) { - if (path.Elements.Length > Elements.Length) return false; - - for (int i = 0; i < path.Elements.Length; i++) { - if (Elements[i] != path.Elements[i]) return false; - } - - return true; - } - - public DreamPath AddToPath(string path) { - string rawPath = PathString; - - if (!rawPath.EndsWith('/') && !path.StartsWith('/')) { - path = '/' + path; - } - - return new DreamPath(rawPath + path); - } - - public int FindElement(string element) { - return Array.IndexOf(Elements, element); - } - - public string[] GetElements(int elementStart, int elementEnd = -1) { - if (elementEnd < 0) elementEnd = Elements.Length + elementEnd + 1; - - string[] elements = new string[elementEnd - elementStart]; - Array.Copy(Elements, elementStart, elements, 0, elements.Length); - - return elements; - } - - public DreamPath FromElements(int elementStart, int elementEnd = -1) { - string[] elements = GetElements(elementStart, elementEnd); - string rawPath = String.Empty; - - if (elements.Length >= 1) { - rawPath = elements.Aggregate((string first, string second) => { - return first + "/" + second; - }); - } - - rawPath = "/" + rawPath; - return new DreamPath(rawPath); - } - - public DreamPath RemoveElement(int elementIndex) { - if (elementIndex < 0) elementIndex += Elements.Length; - - List elements = new List(); - elements.AddRange(GetElements(0, elementIndex)); - elements.AddRange(GetElements(Math.Min(elementIndex + 1, Elements.Length), -1)); - return new DreamPath(Type, elements.ToArray()); - } - - public DreamPath Combine(DreamPath path) { - switch (path.Type) { - case PathType.Relative: return new DreamPath(PathString + "/" + path.PathString); - case PathType.Absolute: return path; - default: return new DreamPath(PathString + path.PathString); - } - } - - public override string ToString() { - return PathString; - } - - public override bool Equals(object? obj) => obj is DreamPath other && Equals(other); - - public bool Equals(DreamPath other) { - if (other.Elements.Length != Elements.Length) return false; - - for (int i = 0; i < Elements.Length; i++) { - if (Elements[i] != other.Elements[i]) return false; - } - - return true; - } - - public override int GetHashCode() { - int hashCode = 0; - for (int i = 0; i < Elements.Length; i++) { - hashCode += Elements[i].GetHashCode(); - } - - return hashCode; - } - - public static bool operator ==(DreamPath lhs, DreamPath rhs) => lhs.Equals(rhs); - - public static bool operator !=(DreamPath lhs, DreamPath rhs) => !(lhs == rhs); - - private void Normalize(bool canHaveEmptyEntries) { - if (canHaveEmptyEntries && _elements.Contains("")) { - // Slow path :( - _elements = _elements.Where(el => !string.IsNullOrEmpty(el)).ToArray(); - } - - var writeIdx = Array.IndexOf(_elements, ".."); - if (writeIdx == -1) return; - - for (var i = writeIdx; i < _elements.Length; i++) { - var elem = _elements[i]; - if (elem == "..") { - writeIdx -= 1; - } else { - _elements[writeIdx] = elem; - writeIdx += 1; - } - } - - Elements = _elements[..writeIdx]; - } - } -}