summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRider Linden <rider@lindenlab.com>2026-08-26 15:17:40 -0700
committerRider Linden <rider@lindenlab.com>2026-08-26 15:17:40 -0700
commite86a0501bc655548aaadeb1d6475fa1652787098 (patch)
tree971b03588db325aba24874d1d2f0c7f8023335cd
parent20280de17db765f128d5e31109ce97baec62bc9c (diff)
Issue #6191: implemented both recompile_all and reset_all commands through command interface.
-rw-r--r--doc/external-editor-json-rpc.md7
-rw-r--r--indra/newview/llcompilequeue.cpp19
-rw-r--r--indra/newview/llscripteditorws.cpp164
-rw-r--r--indra/newview/llscripteditorws.h2
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<LLFloaterCompileQueue> hfloat
LLCheckedHandle<LLFloaterCompileQueue> 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<LLInventoryItem *>(inventory);
@@ -367,6 +368,22 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> 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<LLFloaterScriptQueue>(
+ "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<LLFloaterCompileQueue>(
+ "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);