diff options
| author | Erik Kundiman <erik@megapahit.org> | 2026-09-04 22:45:38 +0800 |
|---|---|---|
| committer | Erik Kundiman <erik@megapahit.org> | 2026-09-04 22:45:38 +0800 |
| commit | f7beb28baafa6600c1e8c9b6a08deb3705ba2e78 (patch) | |
| tree | 6bd3af510b83613f2e9c9627d3bf4c33e30db394 /indra | |
| parent | f57d1d24e1a9947405587a6fe36d38ba42e097bd (diff) | |
| parent | 5ddd940bf4bd75659a86e8d0eb205a766be5d53e (diff) | |
Merge tag 'Second_Life_Release#5ddd940b-26.4' into 26.4
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llcorehttp/lljsonrpcws.cpp | 124 | ||||
| -rw-r--r-- | indra/llcorehttp/lljsonrpcws.h | 13 | ||||
| -rw-r--r-- | indra/llui/lltextbase.cpp | 8 | ||||
| -rw-r--r-- | indra/llui/lltextbase.h | 5 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/llcompilequeue.cpp | 19 | ||||
| -rw-r--r-- | indra/newview/llfloatermaturitydialog.cpp | 86 | ||||
| -rw-r--r-- | indra/newview/llfloatermaturitydialog.h | 47 | ||||
| -rw-r--r-- | indra/newview/llpublishedobjectmgr.cpp | 26 | ||||
| -rw-r--r-- | indra/newview/llscripteditorws.cpp | 227 | ||||
| -rw-r--r-- | indra/newview/llscripteditorws.h | 7 | ||||
| -rw-r--r-- | indra/newview/llviewerfloaterreg.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewermessage.cpp | 34 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/floater_maturity_dialog.xml | 100 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 11 |
15 files changed, 669 insertions, 42 deletions
diff --git a/indra/llcorehttp/lljsonrpcws.cpp b/indra/llcorehttp/lljsonrpcws.cpp index d6c5b0eb97..5b704e6322 100644 --- a/indra/llcorehttp/lljsonrpcws.cpp +++ b/indra/llcorehttp/lljsonrpcws.cpp @@ -207,16 +207,17 @@ void LLJSONRPCConnection::processRequest(const LLSD& request) // Async handler — launched as a coroutine, response sent by the lambda. if (is_notification) { - LL_WARNS("JSONRPC") << "Async method " << method - << " called as notification; ignoring" << LL_ENDL; - return; + LL_WARNS("JSONRPC") << "Method " << method + << " called as notification; rejecting request" << LL_ENDL; + throw InvalidRequest("Method " + method + " cannot be called as a notification"); } ptr_t conn = std::static_pointer_cast<LLJSONRPCConnection>(getSelfPtr()); if (!conn) { - LL_WARNS("JSONRPC") << "Connection expired before async method " << method - << " could be launched" << LL_ENDL; - return; + LL_WARNS("JSONRPC") << "Connection expired before method " << method + << " could be launched; failing request" << LL_ENDL; + throw InternalError("Connection expired before method " + method + + " could be launched"); } LLMainThreadTask::dispatch( [handler, method, id, params, conn]() @@ -515,6 +516,24 @@ void LLJSONRPCConnection::unregisterMethod(const std::string& method) LL_DEBUGS("JSONRPC") << "Unregistered method: " << method << LL_ENDL; } +std::set<std::string> LLJSONRPCConnection::getMethods() const +{ + LLMutexLock lock(&mMutex); + std::set<std::string> methods; + + for (const auto& [method, handler] : mMethodHandlers) + { + methods.insert(method); + } + + for (const auto& [method, handler] : mAsyncMethodHandlers) + { + methods.insert(method); + } + + return methods; +} + LLSD LLJSONRPCConnection::makeEnvelope(const LLSD& id, const std::string& method, const LLSD& params, @@ -669,23 +688,11 @@ LLJSONRPCServer::LLJSONRPCServer(const std::string& name, U16 port, bool local_o << " on port " << port << LL_ENDL; // Register standard JSON-RPC methods - registerGlobalMethod("system.listMethods", [this](const std::string& method, const LLSD& id, const LLSD& params) -> LLSD { - LL_DEBUGS("JSONRPC") << "System method " << method << " called" << LL_ENDL; - return getMethodList(); - }); - registerGlobalMethod("system.getStats", [this](const std::string& method, const LLSD& id, const LLSD& params) -> LLSD { LL_DEBUGS("JSONRPC") << "System method " << method << " called" << LL_ENDL; return getServerStats(); }); - registerGlobalMethod("system.ping", [](const std::string& method, const LLSD& id, const LLSD& params) -> LLSD { - LL_DEBUGS("JSONRPC") << "System method " << method << " called" << LL_ENDL; - LLSD result; - result["pong"] = LLDate::now().asString(); - result["params"] = params; - return result; - }); } LLWebsocketMgr::WSConnection::ptr_t LLJSONRPCServer::connectionFactory(LLWebsocketMgr::WSServer::ptr_t server, @@ -718,21 +725,80 @@ void LLJSONRPCServer::setupConnectionMethods(LLJSONRPCConnection::ptr_t connecti connection->registerMethod(method, handler); } - // Register session.ping handler for connection health monitoring - connection->registerMethod("session.ping", - [](const std::string&, const LLSD&, const LLSD& params) -> LLSD + std::weak_ptr<LLJSONRPCConnection> weak_connection = connection; + connection->registerMethod( + "system.listMethods", + [weak_connection]( + const std::string&, + const LLSD&, + const LLSD&) -> LLSD { - LLSD result; - // Echo back the original timestamp - if (params.has("timestamp")) + LLSD methods(LLSD::emptyArray()); + auto connection = weak_connection.lock(); + if (!connection) { - result["timestamp"] = params["timestamp"]; + return methods; } - // Add server's current time in milliseconds - result["server_time"] = static_cast<LLSD::Integer>( - LLDate::now().secondsSinceEpoch() * 1000.0); - return result; + + for (const std::string& method : connection->getMethods()) + { + methods.append(method); + } + + return methods; + }); + + connection->registerMethod( + "system.ping", + [this, weak_connection]( + const std::string& method, + const LLSD& id, + const LLSD& params) -> LLSD + { + LL_DEBUGS("JSONRPC") << "System method " << method + << " called" << LL_ENDL; + return handlePing(weak_connection.lock(), params); + }); + + connection->registerMethod( + "system.getVersion", + [this, weak_connection]( + const std::string& method, + const LLSD& id, + const LLSD& params) -> LLSD + { + LL_DEBUGS("JSONRPC") << "System method " << method + << " called" << LL_ENDL; + return handleGetVersion(weak_connection.lock(), params); }); + + connection->registerMethod( + "system.status", + [this, weak_connection]( + const std::string& method, + const LLSD& id, + const LLSD& params) -> LLSD + { + LL_DEBUGS("JSONRPC") << "System method " << method + << " called" << LL_ENDL; + return handleStatus(weak_connection.lock(), params); + }); +} + +LLSD LLJSONRPCServer::handlePing( + const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const +{ + return LLSD("pong"); +} + +LLSD LLJSONRPCServer::handleStatus( + const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const +{ + LLSD result; + result["status"] = "OK"; + return result; } void LLJSONRPCServer::registerGlobalMethod(const std::string& method, MethodHandler handler) diff --git a/indra/llcorehttp/lljsonrpcws.h b/indra/llcorehttp/lljsonrpcws.h index 7cb26b1fb2..aa4a96be4d 100644 --- a/indra/llcorehttp/lljsonrpcws.h +++ b/indra/llcorehttp/lljsonrpcws.h @@ -275,6 +275,12 @@ public: void unregisterMethod(const std::string& method); /** + * @brief Get the names of all registered methods + * @return Ordered set containing sync and async method names + */ + virtual std::set<std::string> getMethods() const; + + /** * @brief Make an asynchronous JSON-RPC call * @param method The method name to call * @param params The parameters to pass @@ -495,6 +501,13 @@ protected: LLWebsocketMgr::WSConnection::ptr_t connectionFactory(LLWebsocketMgr::WSServer::ptr_t server, LLWebsocketMgr::connection_h handle) override; + virtual LLSD handlePing(const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const; + virtual LLSD handleGetVersion(const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const = 0; + virtual LLSD handleStatus(const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const; + /** * @brief Apply global method handlers to a new connection * @param connection The connection to configure diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 802367720f..06645df203 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -158,6 +158,7 @@ LLTextBase::Params::Params() track_end("track_end", false), read_only("read_only", false), skip_link_underline("skip_link_underline", false), + link_color("link_color"), spellcheck("spellcheck", false), v_pad("v_pad", 0), h_pad("h_pad", 0), @@ -196,6 +197,8 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mReadOnly(p.read_only), mSkipTripleClick(false), mSkipLinkUnderline(p.skip_link_underline), + mHasLinkColor(p.link_color.isProvided()), + mLinkColor(p.link_color.isProvided() ? p.link_color() : LLUIColor()), mSpellCheck(p.spellcheck), mSpellCheckStart(-1), mSpellCheckEnd(-1), @@ -2426,6 +2429,11 @@ void LLTextBase::appendTextImpl(const std::string& new_text, const LLStyle::Para LLStyle::Params link_params(style_params); link_params.overwriteFrom(match.getStyle()); + if (mHasLinkColor) + { + link_params.color = mLinkColor; + link_params.readonly_color = mLinkColor; + } // output the text before the Url if (start > 0) diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 62f984b8fc..d3edd17dca 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -351,7 +351,8 @@ public: bg_writeable_color, bg_focus_color, text_selected_color, - bg_selected_color; + bg_selected_color, + link_color; Optional<bool> bg_visible, border_visible, @@ -777,6 +778,8 @@ protected: bool mAlwaysShowIcons; bool mSkipLinkUnderline; + bool mHasLinkColor; + LLUIColor mLinkColor; // support widgets LLHandle<LLContextMenu> mPopupMenuHandle; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ee77a4deaf..800826e9d1 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -266,6 +266,7 @@ set(viewer_SOURCE_FILES llfloaterloadprefpreset.cpp llfloatermarketplacelistings.cpp llfloatermap.cpp + llfloatermaturitydialog.cpp llfloatermediasettings.cpp llfloatermemleak.cpp llfloatermodelpreview.cpp @@ -962,6 +963,7 @@ set(viewer_HEADER_FILES llfloatermap.h llfloatermarketplace.h llfloatermarketplacelistings.h + llfloatermaturitydialog.h llfloatermediasettings.h llfloatermemleak.h llfloatermodelpreview.h 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/llfloatermaturitydialog.cpp b/indra/newview/llfloatermaturitydialog.cpp new file mode 100644 index 0000000000..afa9a70024 --- /dev/null +++ b/indra/newview/llfloatermaturitydialog.cpp @@ -0,0 +1,86 @@ +/** + * @file llfloatermaturitydialog.cpp + * @brief LLFloaterMaturityDialog class implementation + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2026, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatermaturitydialog.h" + +#include "llbutton.h" +#include "llagent.h" +#include "lltextbox.h" +#include "llviewercontrol.h" +#include "llviewerregion.h" + +LLFloaterMaturityDialog::LLFloaterMaturityDialog(const LLSD& key) + : LLModalDialog(key, true) +{ +} + +bool LLFloaterMaturityDialog::postBuild() +{ + setCanDrag(false); + getChild<LLButton>("continue_btn")->setCommitCallback( + [this](LLUICtrl*, const LLSD&) { onContinue(); }); + getChild<LLButton>("cancel_btn")->setCommitCallback( + [this](LLUICtrl*, const LLSD&) { onCancel(); }); + + return true; +} + +void LLFloaterMaturityDialog::onOpen(const LLSD& key) +{ + LLModalDialog::onOpen(key); + + mRegionAccess = static_cast<U8>(key.asInteger()); + + LLStringUtil::format_map_t args; + args["[MATURITY]"] = LLViewerRegion::accessToString(mRegionAccess); + getChild<LLTextBox>("location_rated_lbl")->setText(getString("location_rated_string", args)); + getChild<LLTextBox>("current_maturity_lbl")->setText(getString("update_maturity_string", args)); + getChild<LLButton>("continue_btn")->setLabel(getString("allow_maturity_string", args)); + + centerOnScreen(); +} + +void LLFloaterMaturityDialog::draw() +{ + // Skip floater shadow/background; icon child provides the visual background + LLView::draw(); +} + +void LLFloaterMaturityDialog::onContinue() +{ + gSavedSettings.setU32("PreferredMaturity", static_cast<U32>(mRegionAccess)); + gAgent.restartFailedTeleportRequest(); + closeFloater(); +} + +void LLFloaterMaturityDialog::onCancel() +{ + gAgent.clearTeleportRequest(); + closeFloater(); +} + diff --git a/indra/newview/llfloatermaturitydialog.h b/indra/newview/llfloatermaturitydialog.h new file mode 100644 index 0000000000..4ca1a53c2a --- /dev/null +++ b/indra/newview/llfloatermaturitydialog.h @@ -0,0 +1,47 @@ +/** + * @file llfloatermaturitydialog.h + * @brief LLFloaterMaturityDialog class definition + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2026, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "llmodaldialog.h" + +class LLFloaterMaturityDialog : public LLModalDialog +{ + friend class LLFloaterReg; +public: + bool postBuild() override; + void onOpen(const LLSD& key) override; + void draw() override; + +private: + LLFloaterMaturityDialog(const LLSD& key); + ~LLFloaterMaturityDialog() = default; + + void onContinue(); + void onCancel(); + + U8 mRegionAccess = SIM_ACCESS_ADULT; +}; diff --git a/indra/newview/llpublishedobjectmgr.cpp b/indra/newview/llpublishedobjectmgr.cpp index d92bb382de..85a9542dce 100644 --- a/indra/newview/llpublishedobjectmgr.cpp +++ b/indra/newview/llpublishedobjectmgr.cpp @@ -88,6 +88,23 @@ namespace // Never emit an empty prim/object name to downstream tooling. return obj->getID().asString(); } + + void add_object_permissions(LLSD& object_data, LLViewerObject* object) + { + LLPermissions* permissions = + LLSelectMgr::getInstance()->findObjectPermissions(object); + if (!permissions) + { + return; + } + + LLSD permission_entry; + permission_entry["owner"] = + static_cast<S32>(permissions->getMaskOwner()); + permission_entry["next_owner"] = + static_cast<S32>(permissions->getMaskNextOwner()); + object_data["permissions"] = permission_entry; + } } class LLPublishedPrimListener : public LLVOInventoryListener @@ -729,6 +746,7 @@ LLSD LLPublishedObjectMgr::buildPublishedObjectLLSD(LLViewerObject* root) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_SCRIPTDEV; LLSD pub; + add_object_permissions(pub, root); pub["object_id"] = root->getID(); pub["object_name"] = get_prim_name(root); pub["object_description"] = nv_string(root, "Desc"); @@ -737,6 +755,11 @@ LLSD LLPublishedObjectMgr::buildPublishedObjectLLSD(LLViewerObject* root) const { pub["region"] = root->getRegion()->getName(); } + const PublishedObjectInfo* published_info = getPublished(root->getID()); + if (published_info) + { + pub["can_save_back"] = published_info->mCanSaveBackToContents; + } pub["inventory"] = buildPrimInventoryLLSD(root); LLSD linked_objects = LLSD::emptyArray(); @@ -748,6 +771,7 @@ LLSD LLPublishedObjectMgr::buildPublishedObjectLLSD(LLViewerObject* root) const link["link_number"] = link_number++; link["link_name"] = get_prim_name(child); link["link_description"] = nv_string(child, "Desc"); + add_object_permissions(link, child); link["inventory"] = buildPrimInventoryLLSD(child); linked_objects.append(link); } @@ -773,6 +797,7 @@ LLSD LLPublishedObjectMgr::buildObjectListLLSD() const } LLSD pub; + add_object_permissions(pub, root); pub["object_id"] = info.mObjectID; pub["object_name"] = info.mObjectName; pub["object_description"] = info.mObjectDescription; @@ -803,6 +828,7 @@ LLSD LLPublishedObjectMgr::buildObjectListLLSD() const link["link_number"] = prim_info.mLinkNumber; link["link_name"] = prim_info.mPrimName; link["link_description"] = prim_info.mPrimDescription; + add_object_permissions(link, child); link["inventory"] = buildPrimInventoryLLSD(child); linked_objects.append(link); } diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp index f886be819f..72b5b66463 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" @@ -169,6 +170,14 @@ namespace return std::string(s); } + LLSD object_id_command_params() + { + LLSD params(LLSD::emptyMap()); + params["object_id"]["type"] = "string"; + params["object_id"]["required"] = true; + return params; + } + } //======================================================================== @@ -184,7 +193,8 @@ LLScriptEditorWSServer::LLScriptEditorWSServer(const std::string& name, U16 port LL_INFOS("ScriptEditorWS") << "Created JSON-RPC script editor server: " << name << " on port " << port << LL_ENDL; - registerCommand({ "viewer.teleport", "Teleport agent to an in-world object" }, + registerCommand({ "viewer.teleport", "Teleport agent to an in-world object", + object_id_command_params() }, [](U32, const LLSD& p) -> LLSD { LLUUID object_id = p["object_id"].asUUID(); @@ -203,7 +213,9 @@ LLScriptEditorWSServer::LLScriptEditorWSServer(const std::string& name, U16 port return response; }); - registerCommand({ "viewer.camera.focus", "Zoom camera to an in-world object (same behavior as context menu Zoom In)" }, + registerCommand({ "viewer.camera.focus", + "Zoom camera to an in-world object (same behavior as context menu Zoom In)", + object_id_command_params() }, [](U32, const LLSD& p) -> LLSD { LLUUID object_id = p["object_id"].asUUID(); @@ -221,11 +233,36 @@ LLScriptEditorWSServer::LLScriptEditorWSServer(const std::string& name, U16 port return response; }); - registerCommand({ "viewer.object.save_back_to_contents", "Save an in-world object back to source object contents" }, + registerCommand({ "viewer.object.save_back_to_contents", + "Save an in-world object back to source object contents", + object_id_command_params() }, [this](U32 connection_id, const LLSD& p) -> LLSD { 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() @@ -801,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 == "auto-luau") ? "luau" : target; + response["queued"] = true; + return response; +} + LLSD LLScriptEditorWSServer::handleObjectModify(U32 connection_id, const LLSD& params) { // Step 1: Parameter Validation @@ -1019,6 +1196,10 @@ LLSD LLScriptEditorWSServer::handleCommandList() LLSD info; info["command"] = entry.first.command; info["description"] = entry.first.description; + if (!entry.first.params.isUndefined()) + { + info["params"] = entry.first.params; + } commands.append(info); } LLSD response; @@ -1070,6 +1251,33 @@ void LLScriptEditorWSServer::broadcastLanguageChange() } } +LLSD LLScriptEditorWSServer::handlePing( + const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const +{ + LLSD result; + result["pong"] = "pong"; + + if (params.has("timestamp")) + { + result["timestamp"] = params["timestamp"]; + } + + result["server_time"] = static_cast<LLSD::Integer>( + LLDate::now().secondsSinceEpoch() * 1000.0); + return result; +} + +LLSD LLScriptEditorWSServer::handleGetVersion( + const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const +{ + LLSD result; + result["client_name"] = LLVersionInfo::instance().getChannel(); + result["client_version"] = LLVersionInfo::instance().getVersion(); + return result; +} + LLSD LLScriptEditorWSServer::handleLanguageIdRequest() const { LLSD response; @@ -1456,6 +1664,7 @@ LLSD LLScriptEditorWSServer::handleObjectContentGet(const std::string& method, c response["prim_id"] = prim_id; response["item_id"] = item_id; response["content"] = text_content; + response["encoding"] = "utf-8"; return response; } @@ -1523,7 +1732,13 @@ LLSD LLScriptEditorWSServer::saveScript(LLViewerObject* prim, LLInventoryItem* i [&, prim_id, item_id](const std::string& pump_name) { auto [on_success, on_failure] = make_asset_upload_callbacks(pump_name); - bool is_running = params.has("running") ? params["running"].asBoolean() : false; + const LLViewerInventoryItem* viewer_item = + dynamic_cast<const LLViewerInventoryItem*>(item); + bool is_running = viewer_item ? viewer_item->getIsRunning() : false; + if (params.has("running")) + { + is_running = params["running"].asBoolean(); + } LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>( prim_id, item_id, compile_target, is_running, LLUUID::null, content, @@ -2078,6 +2293,10 @@ void LLScriptEditorWSServer::sendRuntimeEvent( } LLSD message; + if (!script_id.empty()) + { + message["script_id"] = script_id; + } message["object_id"] = event.mRootID; message["prim_id"] = event.mPrimID; message["item_id"] = event.mItemID; diff --git a/indra/newview/llscripteditorws.h b/indra/newview/llscripteditorws.h index 1dc218853b..6eb8385210 100644 --- a/indra/newview/llscripteditorws.h +++ b/indra/newview/llscripteditorws.h @@ -236,6 +236,10 @@ protected: LLWebsocketMgr::connection_h handle) override; void setupConnectionMethods(LLJSONRPCConnection::ptr_t connection) override; + LLSD handlePing(const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const override; + LLSD handleGetVersion(const LLJSONRPCConnection::ptr_t& connection, + const LLSD& params) const override; void broadcastLanguageChange(); @@ -257,6 +261,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); @@ -333,6 +339,7 @@ private: { std::string command; std::string description; + LLSD params; }; enum WSCommandError { diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 52da6b1a37..12320ac86e 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -103,6 +103,7 @@ #include "llfloatermap.h" #include "llfloatermarketplace.h" #include "llfloatermarketplacelistings.h" +#include "llfloatermaturitydialog.h" #include "llfloatermediasettings.h" #include "llfloatermemleak.h" #include "llfloatermodelpreview.h" @@ -431,6 +432,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("marketplace", "floater_marketplace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMarketplace>); LLFloaterReg::add("marketplace_listings", "floater_marketplace_listings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMarketplaceListings>); LLFloaterReg::add("marketplace_validation", "floater_marketplace_validation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMarketplaceValidation>); + LLFloaterReg::add("maturity_dialog", "floater_maturity_dialog.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMaturityDialog>); LLFloaterReg::add("message_critical", "floater_critical.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>); LLFloaterReg::add("message_tos", "floater_tos.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>); LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMove>); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index dde4897ab7..c4bd9e73ec 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -4950,10 +4950,12 @@ bool handle_teleport_access_blocked(LLSD& llsdBlock, const std::string & notific { U8 regionAccess = static_cast<U8>(llsdBlock["_region_access"].asInteger()); std::string regionMaturity = LLViewerRegion::accessToString(regionAccess); + llsdBlock["REGIONMATURITY_CAP"] = regionMaturity; LLStringUtil::toLower(regionMaturity); llsdBlock["REGIONMATURITY"] = regionMaturity; LLNotificationPtr tp_failure_notification; + bool skip_notif = false; std::string notifySuffix; if (notificationID == std::string("TeleportEntryAccessBlocked")) @@ -5023,21 +5025,39 @@ bool handle_teleport_access_blocked(LLSD& llsdBlock, const std::string & notific } } // End of special handling for "TeleportEntryAccessBlocked" else - { // Normal case, no message munging - gAgent.clearTeleportRequest(); - if (LLNotifications::getInstance()->templateExists(notificationID)) + { + if (notificationID == "RegionTPAccessBlocked") { - tp_failure_notification = LLNotificationsUtil::add(notificationID, llsdBlock, llsdBlock); + bool can_change_maturity = (regionAccess == SIM_ACCESS_MATURE) ? gAgent.isMature() : gAgent.isAdult(); + if (can_change_maturity) + { + LLFloaterReg::showInstance("maturity_dialog", LLSD((S32)regionAccess)); + skip_notif = true; + } + else + { + gAgent.clearTeleportRequest(); + tp_failure_notification = LLNotificationsUtil::add("RegionTPAccessBlocked_NotifyAdultsOnly", llsdBlock); + } } else { - llsdBlock["MESSAGE"] = defaultMessage; - tp_failure_notification = LLNotificationsUtil::add("GenericAlertOK", llsdBlock); + // Normal case, no message munging + gAgent.clearTeleportRequest(); + if (LLNotifications::getInstance()->templateExists(notificationID)) + { + tp_failure_notification = LLNotificationsUtil::add(notificationID, llsdBlock, llsdBlock); + } + else + { + llsdBlock["MESSAGE"] = defaultMessage; + tp_failure_notification = LLNotificationsUtil::add("GenericAlertOK", llsdBlock); + } } returnValue = true; } - if ((tp_failure_notification == NULL) || tp_failure_notification->isIgnored()) + if (((tp_failure_notification == NULL) || tp_failure_notification->isIgnored()) && !skip_notif) { // Given a simple notification if no tp_failure_notification is set or it is ignore LLNotificationsUtil::add(notificationID + notifySuffix, llsdBlock); diff --git a/indra/newview/skins/default/xui/en/floater_maturity_dialog.xml b/indra/newview/skins/default/xui/en/floater_maturity_dialog.xml new file mode 100644 index 0000000000..35769534ac --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_maturity_dialog.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater + height="185" + width="540" + layout="topleft" + name="floater_maturity_dialog" + title="MATURITY" + show_title="false" + header_height="0" + bg_opaque_image="Window_NoTitle_Foreground" + bg_alpha_image="Window_NoTitle_Background" + can_resize="false" + can_drag_on_left="false" + can_minimize="false" + single_instance="true" + can_close="false" + background_visible="false"> + <floater.string + name="location_rated_string" + value="The location you've chosen is rated [MATURITY]."/> + <floater.string + name="update_maturity_string" + value="Your current maturity preference doesn't include [MATURITY] content. Allowing it will update your preference."/> + <floater.string + name="allow_maturity_string" + value="Allow [MATURITY] content"/> + <icon + color="FloaterFocusBackgroundColor" + follows="left|right|bottom|top" + image_name="Rounded_Square" + layout="topleft" + left="0" + top="0" + height="185" + width="540"/> + <text + type="string" + length="1" + follows="top|left|right" + font="SansSerifMediumBold" + text_color="White" + layout="topleft" + left="20" + height="18" + top="20" + right="-10" + name="location_rated_lbl"> + The location you've chosen is rated Adult. + </text> + <text + type="string" + length="1" + follows="top|left|right" + text_color="White" + layout="topleft" + left="20" + height="32" + top_pad="10" + right="-10" + word_wrap="true" + name="current_maturity_lbl"> + Your current maturity preference doesn't include Adult content. Allowing it will update your preference. + </text> + + <button + follows="top|left" + layout="topleft" + height="28" + label="Allow content" + left="20" + top_pad="18" + name="continue_btn" + width="245" /> + + <button + follows="top|left" + layout="topleft" + height="28" + label="Cancel" + left_pad="10" + name="cancel_btn" + width="245" /> + + <text + type="string" + length="1" + follows="top|left|right" + text_color="White" + link_color="White" + layout="topleft" + left="20" + height="16" + top_pad="25" + right="-10" + parse_urls="true" + name="learn_more_lbl"> +[https://lindenlab.freshdesk.com/support/solutions/articles/31000135220-i-can-t-access-adult-land-and-content- Learn more] about maturity ratings. + </text> + +</floater> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 68a8712ee1..90cda572ba 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -5703,6 +5703,17 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="alertmodal.tga" + name="RegionTPAccessBlocked_NotifyAdultsOnly" + type="alertmodal"> + <tag>fail</tag> + The region you are trying to visit is rated [REGIONMATURITY_CAP], which is not available on your account type. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" name="LandClaimAccessBlocked" type="alertmodal"> The land you're trying to claim has a maturity rating exceeding your current preferences. You can change your preferences using Me > Preferences > General. |
