From 76c2d243f0e7a3f21ea4a0efc576de09d71667ca Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Mon, 18 Nov 2024 00:07:26 +0000 Subject: [PATCH 1/3] code reload --- OpenDreamRuntime/DreamManager.cs | 23 +++++++++++++++++++ .../Procs/DebugAdapter/DreamDebugManager.cs | 23 ++++++++++++++++++- .../Procs/DebugAdapter/Protocol/Request.cs | 1 + .../Protocol/RequestHotReloadBytecode.cs | 18 +++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 OpenDreamRuntime/Procs/DebugAdapter/Protocol/RequestHotReloadBytecode.cs diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 20aacd3428..f110b597e2 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -117,6 +117,29 @@ public void ProcessDelQueue() { } } + public void HotReloadJson(string? jsonPath) { + if (string.IsNullOrEmpty(jsonPath) || !File.Exists(jsonPath)) + throw new FileNotFoundException("Could not find the specified json file"); + + string jsonSource = File.ReadAllText(jsonPath); + DreamCompiledJson? json = JsonSerializer.Deserialize(jsonSource); + if (json == null) + throw new Exception("Failed to deserialize the json file"); + + if (!json.Metadata.Version.Equals(OpcodeVerifier.GetOpcodesHash())) + throw new Exception("Compiler opcode version does not match the runtime version!"); + + _compiledJson = json; + var rootPath = Path.GetFullPath(Path.GetDirectoryName(jsonPath)!); + var resources = _compiledJson.Resources ?? Array.Empty(); + _dreamResourceManager.Initialize(rootPath, resources); + if(!string.IsNullOrEmpty(_compiledJson.Interface) && !_dreamResourceManager.DoesFileExist(_compiledJson.Interface)) + throw new FileNotFoundException("Interface DMF not found at "+Path.Join(rootPath,_compiledJson.Interface)); + _objectTree.LoadJson(json); + DreamProcNative.SetupNativeProcs(_objectTree); + + } + public bool LoadJson(string? jsonPath) { if (string.IsNullOrEmpty(jsonPath) || !File.Exists(jsonPath)) return false; diff --git a/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs b/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs index 56339a2316..ba08dfddb2 100644 --- a/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs +++ b/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs @@ -6,6 +6,8 @@ using OpenDreamRuntime.Procs.DebugAdapter.Protocol; using OpenDreamRuntime.Resources; using Robust.Server; +using Robust.Shared.Configuration; +using OpenDreamShared; namespace OpenDreamRuntime.Procs.DebugAdapter; @@ -15,6 +17,7 @@ internal sealed class DreamDebugManager : IDreamDebugManager { [Dependency] private readonly DreamResourceManager _resourceManager = default!; [Dependency] private readonly ProcScheduler _procScheduler = default!; [Dependency] private readonly IBaseServer _server = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; private ISawmill _sawmill = default!; @@ -353,6 +356,9 @@ private void OnRequest(DebugAdapterClient client, Request req) { case RequestHotReloadResource requestHotReloadResource: HandleRequestHotReloadResource(client, requestHotReloadResource); break; + case RequestHotReloadBytecode requestHotReloadBytecode: + HandleRequestHotReloadBytecode(client, requestHotReloadBytecode); + break; default: req.RespondError(client, $"Unknown request \"{req.Command}\""); break; @@ -848,7 +854,7 @@ private void HandleRequestHotReloadResource(DebugAdapterClient client, RequestHo requestHotReloadResource.RespondError(client, "No file provided for a hot reload"); return; } - + _sawmill.Debug("Debug adapter triggered resource hot reload for "+requestHotReloadResource.Arguments.FilePath); try { _dreamManager.HotReloadResource(requestHotReloadResource.Arguments.FilePath); @@ -858,6 +864,21 @@ private void HandleRequestHotReloadResource(DebugAdapterClient client, RequestHo } } + private void HandleRequestHotReloadBytecode(DebugAdapterClient client, RequestHotReloadBytecode requestHotReloadBytecode) { + if (string.IsNullOrWhiteSpace(requestHotReloadBytecode.Arguments.FilePath)) { + _sawmill.Error("Debug adapter requested a bytecode hot reload but didn't provide a json file"); + requestHotReloadBytecode.RespondError(client, "No file provided for a hot reload"); + return; + } + _sawmill.Debug($"Debug adapter triggered bytecode hot reload for file {requestHotReloadBytecode.Arguments.FilePath}"); + try { + _dreamManager.HotReloadJson(_configManager.GetCVar(OpenDreamCVars.JsonPath)); + requestHotReloadBytecode.Respond(client); + } catch (Exception e) { + requestHotReloadBytecode.RespondError(client, e.Message); + } + } + private IEnumerable DisassemblySkipTake(List list, int midpoint, int offset, int count) { for (int i = midpoint + offset; i < midpoint + offset + count; ++i) { if (i < 0) { diff --git a/OpenDreamRuntime/Procs/DebugAdapter/Protocol/Request.cs b/OpenDreamRuntime/Procs/DebugAdapter/Protocol/Request.cs index d27c2d725b..09bc8476e6 100644 --- a/OpenDreamRuntime/Procs/DebugAdapter/Protocol/Request.cs +++ b/OpenDreamRuntime/Procs/DebugAdapter/Protocol/Request.cs @@ -37,6 +37,7 @@ public Request() : base("request") { } "disassemble" => json.Deserialize(), "hotreloadinterface" => json.Deserialize(), "hotreloadresource" => json.Deserialize(), + "hotreloadbytecode" => json.Deserialize(), // Caller will fail to recognize it and can respond with `success: false`. _ => request, }; diff --git a/OpenDreamRuntime/Procs/DebugAdapter/Protocol/RequestHotReloadBytecode.cs b/OpenDreamRuntime/Procs/DebugAdapter/Protocol/RequestHotReloadBytecode.cs new file mode 100644 index 0000000000..05ed0d1c16 --- /dev/null +++ b/OpenDreamRuntime/Procs/DebugAdapter/Protocol/RequestHotReloadBytecode.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using JetBrains.Annotations; + +namespace OpenDreamRuntime.Procs.DebugAdapter.Protocol; + +[UsedImplicitly] +public sealed class RequestHotReloadBytecode : Request { + [JsonPropertyName("arguments")] public required RequestHotReloadBytecodeArguments Arguments { get; set; } + + [UsedImplicitly] + public sealed class RequestHotReloadBytecodeArguments { + [JsonPropertyName("file")] public string? FilePath { get; set; } + } + + public void Respond(DebugAdapterClient client) { + client.SendMessage(Response.NewSuccess(this)); + } +} From 6fac16aed77a534b56e5341726807de19c3f4402 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Mon, 18 Nov 2024 23:27:41 +0000 Subject: [PATCH 2/3] update object defs on active objects --- OpenDreamRuntime/DreamManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index f110b597e2..706e0b926d 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -138,6 +138,10 @@ public void HotReloadJson(string? jsonPath) { _objectTree.LoadJson(json); DreamProcNative.SetupNativeProcs(_objectTree); + foreach(DreamObject dreamObject in IterateDatums()){ + dreamObject.ObjectDefinition = _objectTree.GetObjectDefinition(_objectTree.GetTreeEntry(dreamObject.ObjectDefinition.Type).Id); + } + } public bool LoadJson(string? jsonPath) { From 0ae999ea2c7967b2d1b1943294426562b6df520a Mon Sep 17 00:00:00 2001 From: amylizzle Date: Tue, 19 Nov 2024 14:09:48 +0000 Subject: [PATCH 3/3] add command --- OpenDreamRuntime/DreamManager.Connections.cs | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/OpenDreamRuntime/DreamManager.Connections.cs b/OpenDreamRuntime/DreamManager.Connections.cs index c3c6929016..c61023dbc5 100644 --- a/OpenDreamRuntime/DreamManager.Connections.cs +++ b/OpenDreamRuntime/DreamManager.Connections.cs @@ -358,3 +358,27 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) { dreamManager.HotReloadResource(args[0]); } } + +public sealed class HotReloadCodeCommand : IConsoleCommand { + // ReSharper disable once StringLiteralTypo + public string Command => "hotreloadcode"; + public string Description => "Reload a specified compiled json and hot reload the bytecode"; + public string Help => ""; + public bool RequireServerOrSingleplayer => true; + + public void Execute(IConsoleShell shell, string argStr, string[] args) { + if(!shell.IsLocal) { + shell.WriteError("You cannot use this command as a client. Execute it on the server console."); + return; + } + + if (args.Length != 1) { + shell.WriteError("This command requires a file path to reload as an argument! Example: hotreloadresource ./path/to/compiled.json"); + return; + } + + DreamManager dreamManager = IoCManager.Resolve(); + shell.WriteLine($"Reloading {args[0]}"); + dreamManager.HotReloadJson(args[0]); + } +}