summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRider Linden <rider@lindenlab.com>2025-10-01 15:30:40 -0700
committerRider Linden <rider@lindenlab.com>2025-10-07 09:19:42 -0700
commit339e7ccae99aab39a01aef052719ab9b3432919b (patch)
treef9298aae9ae88afa9afb885899c2bfa26af64b11
parent672bdc8915bc5f7f305b6b51751e2c0578ef6032 (diff)
Added scripting options floater for config, switched to using config values. Added "runtime.debug" and "runtime.error" notifications.
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/settings.xml44
-rw-r--r--indra/newview/llfloaterimnearbychathandler.cpp11
-rw-r--r--indra/newview/llfloaterpreference.cpp6
-rw-r--r--indra/newview/llfloaterpreference.h1
-rw-r--r--indra/newview/llfloaterscripting.cpp106
-rw-r--r--indra/newview/llfloaterscripting.h49
-rw-r--r--indra/newview/llpreviewscript.cpp19
-rw-r--r--indra/newview/llscripteditorws.cpp162
-rw-r--r--indra/newview/llscripteditorws.h26
-rw-r--r--indra/newview/llviewerfloaterreg.cpp2
-rw-r--r--indra/newview/skins/default/xui/en/floater_scripting_settings.xml143
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_advanced.xml51
13 files changed, 561 insertions, 61 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 6c7b4fef81..702a738e57 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -291,6 +291,7 @@ set(viewer_SOURCE_FILES
llfloatersceneloadstats.cpp
llfloaterscriptdebug.cpp
llfloaterscriptedprefs.cpp
+ llfloaterscripting.cpp
llfloaterscriptlimits.cpp
llfloatersearch.cpp
llfloatersellland.cpp
@@ -971,6 +972,7 @@ set(viewer_HEADER_FILES
llfloatersceneloadstats.h
llfloaterscriptdebug.h
llfloaterscriptedprefs.h
+ llfloaterscripting.h
llfloaterscriptlimits.h
llfloatersearch.h
llfloatersellland.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 8d2e077f0b..3f4800ac91 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -13915,6 +13915,50 @@
<key>Value</key>
<string />
</map>
+ <key>ExternalWebsocketSyncEnable</key>
+ <map>
+ <key>Comment</key>
+ <string>Enables the Websocket JSONRPC server when doing external script editing.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>ExternalWebsocketSyncPort</key>
+ <map>
+ <key>Comment</key>
+ <string>Port that the JSONRPC server listens on when editing</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>9020</integer>
+ </map>
+ <key>ExternalWebsocketSyncLocal</key>
+ <map>
+ <key>Comment</key>
+ <string>Should the JSONRPC server listen only for local connections.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>ExternalWebsocketForwardDebug</key>
+ <map>
+ <key>Comment</key>
+ <string>Forward messages and runtime errors for a script to JSONRPC client.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>YawFromMousePosition</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index c920a3c898..1e3bc2804e 100644
--- a/indra/newview/llfloaterimnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -44,6 +44,7 @@
#include "llfloaterimcontainer.h"
#include "llrootview.h"
#include "lllayoutstack.h"
+#include "llscripteditorws.h"
//add LLFloaterIMNearbyChatHandler to LLNotificationsUI namespace
using namespace LLNotificationsUI;
@@ -335,6 +336,7 @@ void LLFloaterIMNearbyChatScreenChannel::addChat(LLSD& chat)
{
if (!gSavedSettings.getBOOL("ShowScriptErrors"))
return;
+
if (gSavedSettings.getS32("ShowScriptErrorsLocation") == 1)
return;
}
@@ -526,6 +528,15 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
if (!gSavedSettings.getBOOL("ShowScriptErrors"))
return;
+ if (gSavedSettings.getBOOL("ExternalWebsocketSyncEnable") && gSavedSettings.getBOOL("ExternalWebsocketForwardDebug"))
+ {
+ LLScriptEditorWSServer::ptr_t server = LLScriptEditorWSServer::getServer();
+ if (server)
+ {
+ server->forwardChatToIDE(chat_msg);
+ }
+ }
+
// don't process debug messages from not owned objects, see EXT-7762
if (gAgentID != chat_msg.mOwnerID)
{
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 291f22d78f..1f13a2f933 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -346,6 +346,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
mCommitCallbackRegistrar.add("Pref.RememberedUsernames", boost::bind(&LLFloaterPreference::onClickRememberedUsernames, this));
mCommitCallbackRegistrar.add("Pref.SpellChecker", boost::bind(&LLFloaterPreference::onClickSpellChecker, this));
mCommitCallbackRegistrar.add("Pref.Advanced", boost::bind(&LLFloaterPreference::onClickAdvanced, this));
+ mCommitCallbackRegistrar.add("Pref.Scripting", boost::bind(&LLFloaterPreference::onClickScriptingPerfs, this));
sSkin = gSavedSettings.getString("SkinCurrent");
@@ -1825,6 +1826,11 @@ void LLFloaterPreference::onClickAdvanced()
}
}
+void LLFloaterPreference::onClickScriptingPerfs()
+{
+ LLFloaterReg::showInstance("scripting_settings");
+}
+
void LLFloaterPreference::onClickActionChange()
{
updateClickActionControls();
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index a784d502ef..73abe49d22 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -190,6 +190,7 @@ public:
void onClickRenderExceptions();
void onClickAutoAdjustments();
void onClickAdvanced();
+ void onClickScriptingPerfs();
void applyUIColor(LLUICtrl* ctrl, const LLSD& param);
void getUIColor(LLUICtrl* ctrl, const LLSD& param);
void onLogChatHistorySaved();
diff --git a/indra/newview/llfloaterscripting.cpp b/indra/newview/llfloaterscripting.cpp
new file mode 100644
index 0000000000..0719ced58d
--- /dev/null
+++ b/indra/newview/llfloaterscripting.cpp
@@ -0,0 +1,106 @@
+/**
+ * @file llfloaterscripting.cpp
+ * @brief Asset creation permission preferences.
+ * @author Jonathan Yap
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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 "llcheckboxctrl.h"
+#include "llfloaterscripting.h"
+#include "llviewercontrol.h"
+#include "llviewerwindow.h"
+#include "lluictrlfactory.h"
+#include "llpermissions.h"
+#include "llagent.h"
+#include "llviewerregion.h"
+#include "llnotificationsutil.h"
+#include "llsdserialize.h"
+#include "llvoavatar.h"
+#include "llcorehttputil.h"
+#include "lleventfilter.h"
+#include "lleventcoro.h"
+#include "llviewermenufile.h"
+#include "llappviewer.h"
+
+namespace
+{
+ class LLEditorPicker : public LLFilePickerThread
+ {
+ public:
+ LLEditorPicker(LLFloaterScripting *floater):
+ LLFilePickerThread(LLFilePicker::FFLOAD_EXE)
+ {
+ mHandle = floater->getDerivedHandle<LLFloaterScripting>();
+ }
+ void notify(const std::vector<std::string>& filenames) override
+ {
+ if (LLAppViewer::instance()->quitRequested())
+ {
+ return;
+ }
+
+ LLFloaterScripting* floater = mHandle.get();
+ if (floater && !filenames.empty())
+ {
+ floater->pickedEditor(filenames.front());
+ }
+ }
+
+ private:
+ LLHandle<LLFloaterScripting> mHandle;
+ };
+
+}
+
+LLFloaterScripting::LLFloaterScripting(const LLSD& seed)
+ : LLFloater(seed)
+{
+ mCommitCallbackRegistrar.add("ScriptingSettings.CLOSE", boost::bind(&LLFloaterScripting::onClickClose, this));
+ mCommitCallbackRegistrar.add("ScriptingSettings.BROWSE", boost::bind(&LLFloaterScripting::onClickBrowse, this));
+}
+
+bool LLFloaterScripting::postBuild()
+{
+ refresh();
+ return true;
+}
+
+void LLFloaterScripting::onClickClose()
+{
+ closeFloater();
+}
+
+void LLFloaterScripting::onClickBrowse()
+{
+ (new LLEditorPicker(this))->getFile();
+}
+
+void LLFloaterScripting::pickedEditor(std::string_view editor)
+{
+ assert_main_thread();
+ std::stringstream cmdline;
+ cmdline << "\"" << editor << "\" \"%s\"";
+
+ gSavedSettings.setString("ExternalEditor", cmdline.str());
+}
diff --git a/indra/newview/llfloaterscripting.h b/indra/newview/llfloaterscripting.h
new file mode 100644
index 0000000000..ca7bd3e091
--- /dev/null
+++ b/indra/newview/llfloaterscripting.h
@@ -0,0 +1,49 @@
+/**
+ * @file llfloaterscripting.h
+ * @brief Asset creation permission preferences.
+ * @author Jonathan Yap
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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 "llfloater.h"
+#include "lleventcoro.h"
+#include "llcoros.h"
+
+class LLFloaterScripting : public LLFloater
+{
+ friend class LLFloaterReg;
+
+public:
+ bool postBuild() override;
+ void onClickClose();
+ void onClickBrowse();
+
+ void pickedEditor(std::string_view editor);
+
+private:
+ LLFloaterScripting(const LLSD& seed);
+
+};
+
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index 9aac1742d5..bd1f68da33 100644
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -1671,27 +1671,26 @@ bool LLScriptEdContainer::handleKeyHere(KEY key, MASK mask)
void LLScriptEdContainer::startWebsocketServer()
{
- // if the user has enabled websockets, create the server to talk to the external editor
+ if (gSavedSettings.getBOOL("ExternalWebsocketSyncEnable"))
{
- // TODO: Get the name, port, and locality from settings
- std::string server_name(LLScriptEditorWSServer::DEFAULT_SERVER_NAME);
- U16 server_port(LLScriptEditorWSServer::DEFAULT_SERVER_PORT);
- bool server_localhost(true);
-
// Attempt to find an existing server
LLWebsocketMgr& wsmgr = LLWebsocketMgr::instance();
- LLScriptEditorWSServer::ptr_t server = std::static_pointer_cast<LLScriptEditorWSServer>(wsmgr.findServerByName(server_name));
+ LLScriptEditorWSServer::ptr_t server =
+ std::static_pointer_cast<LLScriptEditorWSServer>(
+ wsmgr.findServerByName(LLScriptEditorWSServer::DEFAULT_SERVER_NAME));
if (!server)
{ // We couldn't find one, so create it
- server = std::make_shared<LLScriptEditorWSServer>(server_name, server_port, server_localhost);
+ U16 server_port = static_cast<U16>(gSavedSettings.getS32("ExternalWebsocketSyncPort"));
+ bool server_localhost = gSavedSettings.getBOOL("ExternalWebsocketSyncLocal");
+ server = std::make_shared<LLScriptEditorWSServer>(LLScriptEditorWSServer::DEFAULT_SERVER_NAME, server_port, server_localhost);
wsmgr.addServer(server);
}
bool is_running = server->isRunning();
if (!is_running)
{ // Server isn't running, so start it
- is_running = wsmgr.startServer(server_name);
+ is_running = wsmgr.startServer(LLScriptEditorWSServer::DEFAULT_SERVER_NAME);
}
if (!is_running && !server->isRunning())
@@ -1701,7 +1700,7 @@ void LLScriptEdContainer::startWebsocketServer()
}
std::string script_id_hash_str(getUniqueHash());
- server->subscribeScriptEditor(getHandle(), script_id_hash_str);
+ server->subscribeScriptEditor(mObjectUUID, mItemUUID, mScriptEd->mScriptName, getHandle(), script_id_hash_str);
mWebSocketServer = server;
}
}
diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp
index 2df4c73cd9..72aaf919ea 100644
--- a/indra/newview/llscripteditorws.cpp
+++ b/indra/newview/llscripteditorws.cpp
@@ -35,6 +35,9 @@
#include "llversioninfo.h"
#include "llagent.h"
#include "llregex.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llchat.h"
//========================================================================
LLScriptEditorWSServer::LLScriptEditorWSServer(const std::string& name, U16 port, bool local_only)
@@ -44,6 +47,18 @@ LLScriptEditorWSServer::LLScriptEditorWSServer(const std::string& name, U16 port
<< " on port " << port << LL_ENDL;
}
+LLScriptEditorWSServer::ptr_t LLScriptEditorWSServer::getServer()
+{
+ if (!LLWebsocketMgr::instanceExists())
+ {
+ return nullptr;
+ }
+ LLWebsocketMgr& wsmgr = LLWebsocketMgr::instance();
+ return std::static_pointer_cast<LLScriptEditorWSServer>(
+ wsmgr.findServerByName(LLScriptEditorWSServer::DEFAULT_SERVER_NAME));
+}
+
+
LLWebsocketMgr::WSConnection::ptr_t LLScriptEditorWSServer::connectionFactory(LLWebsocketMgr::WSServer::ptr_t server,
LLWebsocketMgr::connection_h handle)
{
@@ -109,14 +124,16 @@ void LLScriptEditorWSServer::onConnectionClosed(const LLWebsocketMgr::WSConnecti
}
}
-bool LLScriptEditorWSServer::subscribeScriptEditor(const LLHandle<LLPanel>& editor_handle, const std::string &script_id)
+bool LLScriptEditorWSServer::subscribeScriptEditor(const LLUUID& object_id, const LLUUID& item_id, std::string_view script_name,
+ const LLHandle<LLPanel>& editor_handle, const std::string& script_id)
{
if (!editor_handle.isDead())
{
auto it = mSubscriptions.find(script_id);
if (it == mSubscriptions.end())
- { // Don't readd if already subscribed
- mSubscriptions.emplace(script_id, EditorSubscription{ editor_handle, LLScriptEditorWSConnection::wptr_t() });
+ { // Don't re-add if already subscribed
+ mSubscriptions.emplace(script_id,
+ LLScriptEditorWSServer::EditorSubscription(object_id, item_id, script_name, editor_handle));
return false;
}
else
@@ -369,8 +386,14 @@ LLSD LLScriptEditorWSServer::handleScriptSubscribe(U32 connection_id, const LLSD
if (result == SUBSCRIPTION_SUCCESS)
{
- //TODO: Build an info block for the subscribed script.
- //buildScriptSubscriptionInfo(result);
+ auto it = mSubscriptions.find(script_id);
+ if (it != mSubscriptions.end())
+ {
+ LLViewerObject* object = gObjectList.findObject((*it).second.mObjectID);
+ response["object_id"] = (*it).second.mObjectID;
+ //response["object_name"] = object ? object->getName() : "Unknown";
+ response["item_id"] = (*it).second.mItemID;
+ }
}
return response;
@@ -499,6 +522,133 @@ void LLScriptEditorWSServer::sendCompileResults(const std::string &script_id, co
notifyScript(script_id, "script.compiled", params);
}
+void LLScriptEditorWSServer::forwardChatToIDE(const LLChat& chat_msg) const
+{
+ auto it = std::find_if(mSubscriptions.begin(), mSubscriptions.end(),
+ [&chat_msg](const auto& pair) { return (pair.second.mObjectID == chat_msg.mFromID); });
+
+ if (it == mSubscriptions.end())
+ { // Not a script we are tracking
+ return;
+ }
+
+ bool is_error = false;
+ std::string error_message;
+ std::string object_name;
+ std::string script_name;
+ S32 line_number = 0;
+ // We have at least one script from this object, we will forward the message to the IDE
+ // but first we need to see if it is a runtime error
+ std::vector<std::string> lines = LLStringUtil::getTokens(chat_msg.mText, "\n");
+ // If this is a runtime error, the first line will look like: "<Object Name> [script:<Script Name>] Script run-time error"
+ static const std::string runtime_error_marker = "Script run-time error";
+ if (!lines.empty() && std::equal(runtime_error_marker.rbegin(), runtime_error_marker.rend(), lines.front().rbegin()))
+ {
+ is_error = true;
+ std::string first_line = lines.front();
+
+ // Extract the object and script name from the first line
+ static const boost::regex RUNTIME_ERR_REGEX_FLEX(R"(^(.+?)\s+\[script:([^\]]+)\]\s+Script run-time error)");
+ boost::smatch m;
+
+ S32 remove_count = 0;
+ if (boost::regex_match(first_line, m, RUNTIME_ERR_REGEX_FLEX))
+ {
+ object_name = m[1].str();
+ script_name = m[2].str();
+ remove_count++;
+ }
+
+ // TODO: Build an actual error message to forward to the external editor
+ // Explaination:
+ // Well! Heck!
+ // As it turns out, the complete error message arrives as either two or three
+ // separate chat messages from the server.
+ // 2 if the script is LSL or if it is Lua but not owned by the editing agent
+ // 3 if the script is Lua and owned by the editing agent.
+ //
+ // Message 1: <Object Name> [script:<Script Name>] Script run-time error
+ // Message 2: <runtime error>
+ // Message 3: <script>:<line>: <actual error message>\n
+ // <call stack>
+ //
+ // These need to be compositited into a single error message to send to the IDE.
+ //
+ //if (lines.size() > 1)
+ //{ // The second line is the actual error message
+ // error_message = lines[1];
+ // remove_count++;
+ // if ((error_message == "runtime error") && (lines.size() > 2))
+ // { // If the error message is just "runtime error", the next line might actually be the real message:
+ // // "lua_script:7: attempt to perform arithmetic (sub) on nil"
+ // static const boost::regex LUA_ERROR_REGEX(R"(^(.+?):(\d+):\s*(.+)$)");
+ //
+ // if (boost::regex_match(first_line, m, RUNTIME_ERR_REGEX_FLEX))
+ // {
+ // line_number = std::stoi(m[2].str());
+ // error_message = m[3].str();
+ // remove_count++;
+ // }
+ // }
+ // else
+ // {
+ // error_message = "Unknown script runtime error";
+ // }
+ //}
+ //else
+ //{
+ // error_message = "Unknown script runtime error";
+ //}
+ if (lines.size() > remove_count)
+ { // The rest of the lines may contain a stack trace
+ lines.erase(lines.begin(), lines.begin() + remove_count);
+ }
+ else
+ {
+ lines.clear();
+ }
+
+ // We should also check that the script name matches one of our subscriptions
+ if (!script_name.empty() && (it->second.mScriptName != script_name))
+ { // right object, wrong script
+ auto sit = std::find_if(mSubscriptions.begin(), mSubscriptions.end(),
+ [&chat_msg, &script_name](const auto& pair)
+ {
+ return (pair.second.mScriptName == script_name) && (pair.second.mObjectID == chat_msg.mFromID);
+ });
+ if (sit != mSubscriptions.end())
+ { // We have a better match
+ it = sit;
+ }
+ }
+ }
+ std::string script_id = it->first;
+ LLSD message;
+ message["scriptId"] = script_id;
+ message["objectId"] = chat_msg.mFromID;
+ message["objectName"] = chat_msg.mFromName;
+ message["message"] = chat_msg.mText;
+
+ if (is_error)
+ {
+ message["error"] = error_message;
+ message["line"] = line_number;
+ if (!lines.empty())
+ {
+ message["stack"] = LLSD::emptyArray();
+ for (const auto& line : lines)
+ {
+ message["stack"].append(line);
+ }
+ }
+ }
+
+ if (!it->second.mConnection.expired())
+ {
+ it->second.mConnection.lock()->notify(is_error ? "runtime.error" : "runtime.debug", message);
+ }
+}
+
//========================================================================
U32 LLScriptEditorWSConnection::sNextConnectionID = 1;
@@ -522,7 +672,7 @@ void LLScriptEditorWSConnection::onOpen()
handshake["viewer_version"] = LLVersionInfo::instance().getVersion();
handshake["agent_id"] = gAgent.getID();
- handshake["agent_name"] = "todo";
+ handshake["agent_name"] = gAgentUsername;
std::string challenge_file = generateChallenge();
if (!challenge_file.empty())
diff --git a/indra/newview/llscripteditorws.h b/indra/newview/llscripteditorws.h
index 2c3ae31e8d..3ed23a0b7e 100644
--- a/indra/newview/llscripteditorws.h
+++ b/indra/newview/llscripteditorws.h
@@ -41,6 +41,8 @@
class LLLiveLSLEditor;
class LLScriptEdContainer;
class LLScriptEditorWSServer;
+class LLChat;
+class LLPanel;
class LLScriptEditorWSConnection : public LLJSONRPCConnection, public std::enable_shared_from_this<LLScriptEditorWSConnection>
{
@@ -159,12 +161,15 @@ public:
virtual ~LLScriptEditorWSServer() = default;
+ static LLScriptEditorWSServer::ptr_t getServer();
+
void onStarted() override;
void onStopped() override;
void onConnectionOpened(const LLWebsocketMgr::WSConnection::ptr_t& connection) override;
void onConnectionClosed(const LLWebsocketMgr::WSConnection::ptr_t& connection) override;
- bool subscribeScriptEditor(const LLHandle<LLPanel>& editor_handle, const std::string &script_id);
+ bool subscribeScriptEditor(const LLUUID& object_id, const LLUUID& item_id, std::string_view script_name,
+ const LLHandle<LLPanel>& editor_handle, const std::string &script_id);
void unsubscribeEditor(const std::string &script_id);
void notifyScript(const std::string& script_id, const std::string& method, const LLSD& message) const;
@@ -173,10 +178,8 @@ public:
LLHandle<LLPanel> findEditorForScript(const std::string& script_id) const;
- /**
- * @brief Get list of active script editing sessions
- * @return Set of script IDs currently being edited
- */
+ void forwardChatToIDE(const LLChat& chat_msg) const;
+
std::set<std::string> getActiveScripts() const;
protected:
@@ -195,9 +198,18 @@ protected:
private:
struct EditorSubscription
{
- LLHandle<LLPanel> mEditorHandle;
- LLScriptEditorWSConnection::wptr_t mConnection;
+ EditorSubscription(const LLUUID &object_id, const LLUUID &item_id, std::string_view script_name, LLHandle<LLPanel> editor_handle):
+ mObjectID(object_id),
+ mItemID(item_id),
+ mScriptName(script_name),
+ mEditorHandle(editor_handle)
+ {}
U32 mConnectionID{ 0 };
+ LLUUID mObjectID;
+ LLUUID mItemID;
+ std::string mScriptName;
+ LLScriptEditorWSConnection::wptr_t mConnection;
+ LLHandle<LLPanel> mEditorHandle;
};
using subscriptions_t = std::unordered_map<std::string, EditorSubscription>;
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 3b35ca8db1..107b3df831 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -132,6 +132,7 @@
#include "llfloatersavecamerapreset.h"
#include "llfloatersaveprefpreset.h"
#include "llfloatersceneloadstats.h"
+#include "llfloaterscripting.h"
#include "llfloaterscriptdebug.h"
#include "llfloaterscriptedprefs.h"
#include "llfloaterscriptlimits.h"
@@ -492,6 +493,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("script_debug_output", "floater_script_debug_panel.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebugOutput>);
LLFloaterReg::add("script_floater", "floater_script.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLScriptFloater>);
LLFloaterReg::add("script_limits", "floater_script_limits.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptLimits>);
+ LLFloaterReg::add("scripting_settings", "floater_scripting_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScripting>);
LLFloaterReg::add("my_scripts", "floater_my_scripts.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMyScripts>);
LLFloaterReg::add("sell_land", "floater_sell_land.xml", &LLFloaterSellLand::buildFloater);
LLFloaterReg::add("settings_color", "floater_settings_color.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSettingsColor>);
diff --git a/indra/newview/skins/default/xui/en/floater_scripting_settings.xml b/indra/newview/skins/default/xui/en/floater_scripting_settings.xml
new file mode 100644
index 0000000000..fb782a20d1
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_scripting_settings.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater
+ legacy_header_height="18"
+ height="265"
+ layout="topleft"
+ name="floater_scripting_settings"
+ help_topic="scripting_settings"
+ save_rect="true"
+ title="SCRIPT DEVELOPMENT SETTINGS"
+ width="485">
+ <check_box
+ control_name="ShowScriptErrors"
+ follows="left|top"
+ height="20"
+ label="Show script errors in:"
+ layout="topleft"
+ left="30"
+ top_pad="20"
+ name="show_script_errors"
+ width="256" />
+ <radio_group
+ enabled_control="ShowScriptErrors"
+ control_name="ShowScriptErrorsLocation"
+ follows="top|left"
+ height="16"
+ layout="topleft"
+ left_delta="50"
+ name="show_location"
+ top_pad="5"
+ width="364">
+ <radio_item
+ height="16"
+ label="Nearby chat"
+ layout="topleft"
+ left="3"
+ name="0"
+ top="0"
+ width="315" />
+ <radio_item
+ height="16"
+ label="Separate window"
+ layout="topleft"
+ left_delta="175"
+ name="1"
+ top_delta="0"
+ width="315" />
+ </radio_group>
+
+ <text
+ follows="top|left|right"
+ height="15"
+ layout="topleft"
+ left="30"
+ name="tip"
+ top_pad="20"
+ width="330"
+ wrap="true">
+ External Script Editor:
+ </text>
+ <line_editor
+ control_name="ExternalEditor"
+ follows="top|left"
+ height="20"
+ initial_value=""
+ layout="left"
+ left_pad="5"
+ name="external_editor"
+ top_delta="25"
+ value=""
+ width="360" />
+ <button
+ follows="left|top"
+ height="20"
+ label="Browse..."
+ layout="topleft"
+ left_pad="5"
+ name="browse_btn"
+ top_delta="0"
+ width="70">
+ <button.commit_callback
+ function="ScriptingSettings.BROWSE" />
+ </button>
+
+ <check_box
+ control_name="ExternalWebsocketSyncEnable"
+ follows="top|left"
+ height="20"
+ label="Enable WebScocket Sync"
+ layout="topleft"
+ left="30"
+ name="websocket_sync_enable"
+ top_pad="20"
+ width="200" />
+
+ <text
+ enabled_control="ExternalWebsocketSyncEnable"
+ follows="top|left"
+ height="15"
+ layout="topleft"
+ left="50"
+ name="websocket_tip"
+ top_pad="10"
+ width="140"
+ wrap="false">
+ WebSocket Listen Port:
+ </text>
+ <line_editor
+ enabled_control="ExternalWebsocketSyncEnable"
+ control_name="ExternalWebsocketSyncPort"
+ follows="top|left"
+ height="20"
+ initial_value="9020"
+ layout="topleft"
+ left_pad="5"
+ name="websocket_port"
+ top_delta="-2"
+ value="9020"
+ width="120" />
+ <check_box
+ control_name="ExternalWebsocketForwardDebug"
+ enabled_control="ExternalWebsocketSyncEnable"
+ follows="top|left"
+ height="20"
+ label="Forward script errors and debug to WebSocket clients"
+ layout="topleft"
+ left="50"
+ name="websocket_forward_debug"
+ top_pad="5"
+ width="400" />
+
+ <button
+ follows="left|top"
+ height="23"
+ label="Close"
+ layout="topleft"
+ right="-10"
+ name="close_btn"
+ top="-30"
+ width="100">
+ <button.commit_callback
+ function="ScriptingSettings.CLOSE" />
+ </button>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
index 86999b1afb..434ee1db35 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
@@ -169,43 +169,6 @@
name="hud_scale_slider"
top_pad="-14"
width="250" />
- <check_box
- control_name="ShowScriptErrors"
- follows="left|top"
- height="20"
- label="Show script errors in:"
- layout="topleft"
- left="30"
- top_pad="10"
- name="show_script_errors"
- width="256" />
- <radio_group
- enabled_control="ShowScriptErrors"
- control_name="ShowScriptErrorsLocation"
- follows="top|left"
- height="16"
- layout="topleft"
- left_delta="50"
- name="show_location"
- top_pad="5"
- width="364">
- <radio_item
- height="16"
- label="Nearby chat"
- layout="topleft"
- left="3"
- name="0"
- top="0"
- width="315" />
- <radio_item
- height="16"
- label="Separate window"
- layout="topleft"
- left_delta="175"
- name="1"
- top_delta="0"
- width="315" />
- </radio_group>
<check_box
control_name="AllowMultipleViewers"
@@ -266,9 +229,21 @@
name="default_creation_permissions"
height="20"
left="30"
- top_pad="16"
+ top_pad="10"
width="200">
<button.commit_callback
function="Pref.PermsDefault" />
</button>
+ <button
+ follows="top|left"
+ layout="topleft"
+ label="Script Development"
+ name="scripting_development"
+ height="20"
+ left="30"
+ top_pad="10"
+ width="200">
+ <button.commit_callback
+ function="Pref.Scripting" />
+ </button>
</panel>