From e86a0501bc655548aaadeb1d6475fa1652787098 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 26 Aug 2026 15:17:40 -0700 Subject: Issue #6191: implemented both recompile_all and reset_all commands through command interface. --- doc/external-editor-json-rpc.md | 7 +- indra/newview/llcompilequeue.cpp | 19 ++++- indra/newview/llscripteditorws.cpp | 164 +++++++++++++++++++++++++++++++++++++ indra/newview/llscripteditorws.h | 2 + 4 files changed, 188 insertions(+), 4 deletions(-) diff --git a/doc/external-editor-json-rpc.md b/doc/external-editor-json-rpc.md index 74ee34f528..a09bde2b22 100644 --- a/doc/external-editor-json-rpc.md +++ b/doc/external-editor-json-rpc.md @@ -1721,9 +1721,8 @@ interface CommandParamInfo { - `commands`: Array of commands the responder supports. Each entry describes one command. - `command`: The namespaced command identifier. - `description` (optional): Human-readable description of what the command does. - - `params` (optional): Map of parameter names to their type descriptors. **Not currently - populated** — the viewer returns only `command` and `description`, so parameter discovery - does not work. Implementation is tracked separately. + - `params` (optional): Map of parameter names to their type descriptors. The viewer populates + this field for its registered commands. **Known viewer commands:** @@ -1732,6 +1731,8 @@ interface CommandParamInfo { | `viewer.teleport` | `object_id: string` | Teleport agent to an in-world object. | | `viewer.camera.focus` | `object_id: string` | Zoom camera to an in-world object (same behavior as context menu Zoom In). | | `viewer.object.save_back_to_contents` | `object_id: string` | Save an in-world object back to source object contents. | +| `viewer.script.reset_all` | `object_id: string` | Open the viewer's reset queue and reset all scripts in an in-world object. | +| `viewer.script.recompile_all` | `object_id: string`, `target: "luau" \| "lsl2" \| "mono" \| "auto"` | Open the viewer's compile queue and recompile scripts in an in-world object using the selected target. `luau` automatically selects Luau for native Luau scripts and LSL-Luau for LSL scripts. `auto` uses each script's previously registered VM. | **Known extension commands:** diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index ba19135fce..98b9255f66 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -356,7 +356,8 @@ bool LLFloaterCompileQueue::processScript(LLHandle hfloat LLCheckedHandle floater(hfloater); // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. // which is caught in objectScriptProcessingQueueCoro - std::string compile_target = floater->mCompileTarget; + const std::string requested_target = floater->mCompileTarget; + std::string compile_target = requested_target; // Initial test to see if we can (or should) attempt to compile the script. LLInventoryItem *item = dynamic_cast(inventory); @@ -367,6 +368,22 @@ bool LLFloaterCompileQueue::processScript(LLHandle hfloat return true; } + if (requested_target == "auto-luau") + { + compile_target = + item->getInventorySubType() == SST_LUA ? "luau" : "lsl-luau"; + } + else if (requested_target == "auto") + { + compile_target = item->getRuntime(); + if (compile_target.empty()) + { + floater->addStringMessage( + "Skipping: " + item->getName() + " (no registered VM)"); + return true; + } + } + if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) { diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp index 56dc2bf15e..729f335db4 100644 --- a/indra/newview/llscripteditorws.cpp +++ b/indra/newview/llscripteditorws.cpp @@ -34,6 +34,7 @@ #include "llagentcamera.h" #include "llappviewer.h" #include "llchat.h" +#include "llcompilequeue.h" #include "lldate.h" #include "llerror.h" #include "lleventcoro.h" @@ -239,6 +240,29 @@ LLScriptEditorWSServer::LLScriptEditorWSServer(const std::string& name, U16 port { return this->handleSaveBackToObjectContents(connection_id, p); }); + + registerCommand({ "viewer.script.reset_all", + "Reset all scripts in an in-world object", + object_id_command_params() }, + [this](U32 connection_id, const LLSD& p) -> LLSD + { + return this->handleObjectScriptResetAll(connection_id, p); + }); + + registerCommand({ "viewer.script.recompile_all", + "Recompile all scripts in an in-world object", + LLSDMap("object_id", + LLSDMap("type", "string") + ("required", true)) + ("target", + LLSDMap("type", "string") + ("required", true) + ("description", + "Compilation target: luau, lsl2, mono, or auto")) }, + [this](U32 connection_id, const LLSD& p) -> LLSD + { + return this->handleObjectScriptRecompileAll(connection_id, p); + }); } LLScriptEditorWSServer::ptr_t LLScriptEditorWSServer::getServer() @@ -814,6 +838,146 @@ LLSD LLScriptEditorWSServer::handleObjectScriptReset(U32 connection_id, const LL return response; } +LLSD LLScriptEditorWSServer::handleObjectScriptResetAll(U32 connection_id, const LLSD& params) +{ + LLUUID prim_id = params["object_id"].asUUID(); + if (prim_id.isNull()) + { + throw LLJSONRPCConnection::InvalidParams("object_id is required"); + } + + LLViewerObject* prim = gObjectList.findObject(prim_id); + if (!prim) + { + throw LLJSONRPCConnection::InvalidParams("Object not found"); + } + + LLViewerObject* root = prim->getRootEdit(); + if (!root || !isObjectPublished(root->getID())) + { + throw LLJSONRPCConnection::ForbiddenError("Object is not published"); + } + + if (!prim->flagScripted()) + { + throw LLJSONRPCConnection::InvalidParams( + "Prim contains no scripts"); + } + + if (!prim->permModify()) + { + throw LLJSONRPCConnection::ForbiddenError( + "No modify permission on prim"); + } + + LLUUID queue_id; + queue_id.generate(); + + LLFloaterScriptQueue* queue = + LLFloaterReg::getTypedInstance( + "reset_queue", LLSD(queue_id)); + if (!queue) + { + throw LLJSONRPCConnection::InternalError( + "Unable to open reset queue"); + } + + queue->addObject(prim->getID(), prim->getID().asString()); + if (!queue->start()) + { + queue->closeFloater(); + throw LLJSONRPCConnection::InternalError( + "Unable to start reset queue"); + } + + queue->setTitle(LLTrans::getString("ResetQueueTitle")); + + LLSD response; + response["success"] = true; + response["object_id"] = prim->getID(); + response["queued"] = true; + return response; +} + +LLSD LLScriptEditorWSServer::handleObjectScriptRecompileAll( + U32 connection_id, const LLSD& params) +{ + LLUUID object_id = params["object_id"].asUUID(); + if (object_id.isNull()) + { + throw LLJSONRPCConnection::InvalidParams("object_id is required"); + } + + std::string target = params["target"].asString(); + if (target != "luau" && + target != "lsl2" && + target != "mono" && + target != "auto") + { + throw LLJSONRPCConnection::InvalidParams( + "target must be 'luau', 'lsl2', 'mono', or 'auto'"); + } + + LLViewerObject* object = gObjectList.findObject(object_id); + if (!object) + { + throw LLJSONRPCConnection::InvalidParams("Object not found"); + } + + LLViewerObject* root = object->getRootEdit(); + if (!root || root->getID() != object_id || !isObjectPublished(root->getID())) + { + throw LLJSONRPCConnection::ForbiddenError("Object is not published"); + } + + if (!root->flagScripted()) + { + throw LLJSONRPCConnection::InvalidParams( + "Object contains no scripts"); + } + + if (!root->permModify()) + { + throw LLJSONRPCConnection::ForbiddenError( + "No modify permission on object"); + } + + if (target == "luau") + { + target = "auto-luau"; + } + + LLUUID queue_id; + queue_id.generate(); + + LLFloaterCompileQueue* queue = + LLFloaterReg::getTypedInstance( + "compile_queue", LLSD(queue_id)); + if (!queue) + { + throw LLJSONRPCConnection::InternalError( + "Unable to open compile queue"); + } + + queue->setCompileTarget(target); + queue->addObject(root->getID(), root->getID().asString()); + if (!queue->start()) + { + queue->closeFloater(); + throw LLJSONRPCConnection::InternalError( + "Unable to start compile queue"); + } + + queue->setTitle(LLTrans::getString("CompileQueueTitle")); + + LLSD response; + response["success"] = true; + response["object_id"] = root->getID(); + response["target"] = target; + response["queued"] = true; + return response; +} + LLSD LLScriptEditorWSServer::handleObjectModify(U32 connection_id, const LLSD& params) { // Step 1: Parameter Validation diff --git a/indra/newview/llscripteditorws.h b/indra/newview/llscripteditorws.h index 868db9a9cf..e569d4ff7d 100644 --- a/indra/newview/llscripteditorws.h +++ b/indra/newview/llscripteditorws.h @@ -257,6 +257,8 @@ protected: LLSD handleObjectList() const; LLSD handleObjectScriptSetRunning(U32 connection_id, const LLSD& params); LLSD handleObjectScriptReset(U32 connection_id, const LLSD& params); + LLSD handleObjectScriptResetAll(U32 connection_id, const LLSD& params); + LLSD handleObjectScriptRecompileAll(U32 connection_id, const LLSD& params); LLSD handleObjectModify(U32 connection_id, const LLSD& params); LLSD handleObjectItemModify(U32 connection_id, const LLSD& params); LLSD handleCommandExecute(U32 connection_id, const LLSD& params); -- cgit v1.3