diff options
| author | Rider Linden <rider@lindenlab.com> | 2025-10-10 12:28:41 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-10 12:28:41 -0700 |
| commit | dd9ea09d50ef5a4992d2ae29d2970ce05998bad0 (patch) | |
| tree | d4500ea28b20dcb0aa274b3bbc0ad10585634193 /indra/newview/llpreviewscript.cpp | |
| parent | 0b7bc35131ce4a526c1e696904c4e8332e1b0fe9 (diff) | |
| parent | c21fda0b79c2e18c0dccf9e88524a716b57b94bf (diff) | |
Merge pull request #4599 from secondlife/rider/vscode_integration
Websockets Server implementation for script editor integration with VSCode or other external editors.
Full implementation of JSON-RPC server bound to external code editing.
Diffstat (limited to 'indra/newview/llpreviewscript.cpp')
| -rw-r--r-- | indra/newview/llpreviewscript.cpp | 148 |
1 files changed, 113 insertions, 35 deletions
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 69f5812853..b3e53df6d8 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -65,22 +65,12 @@ #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" -#include "llkeyboard.h" -#include "llscrollcontainer.h" -#include "llcheckboxctrl.h" #include "llscripteditor.h" -#include "llselectmgr.h" -#include "lltooldraganddrop.h" -#include "llscrolllistctrl.h" #include "lltextbox.h" -#include "llslider.h" -#include "lldir.h" -#include "llcombobox.h" #include "llviewerstats.h" #include "llviewerwindow.h" #include "lluictrlfactory.h" #include "llmediactrl.h" -#include "lluictrlfactory.h" #include "lltrans.h" #include "llviewercontrol.h" #include "llappviewer.h" @@ -91,6 +81,8 @@ #include "lltoggleablemenu.h" #include "llmenubutton.h" #include "llinventoryfunctions.h" +#include "llwebsocketmgr.h" +#include "llscripteditorws.h" #include <regex> const std::string HELP_LSL_PORTAL_TOPIC = "LSL_Portal"; @@ -1121,19 +1113,21 @@ void LLScriptEdCore::doSave( bool close_after_save ) void LLScriptEdCore::openInExternalEditor() { - if (mContainer->mLiveFile) - { - // If already open in an external editor, just return - return; - } + //if (mContainer->mLiveFile) + //{ + // // If already open in an external editor, just return + // return; + //} // Generate a suitable filename std::string script_name = mScriptName; - std::string forbidden_chars = "<>:\"\\/|?*"; - for (std::string::iterator c = forbidden_chars.begin(); c != forbidden_chars.end(); c++) - { - script_name.erase(std::remove(script_name.begin(), script_name.end(), *c), script_name.end()); - } + + static const std::set<char> forbidden_chars{ '<', '>', ':', '"', '\\', '/', '|', '?', '*' }; + script_name.erase( + std::remove_if(script_name.begin(), script_name.end(), [](char c) { + return forbidden_chars.contains(c); + }), script_name.end()); + std::string filename = mContainer->getTmpFileName(script_name); // Save the script to a temporary file. @@ -1147,9 +1141,18 @@ void LLScriptEdCore::openInExternalEditor() writeToFile(filename); } + if (mContainer->mLiveFile && mContainer->mLiveFile->filename() != filename) + { // The name may have changed if we changed the type of scipt being edited. + delete mContainer->mLiveFile; + mContainer->mLiveFile = NULL; + } // Start watching file changes. - mContainer->mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1)); - mContainer->mLiveFile->addToEventTimer(); + if (!mContainer->mLiveFile) + { + mContainer->mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1)); + mContainer->mLiveFile->addToEventTimer(); + } + mContainer->startWebsocketServer(); // Open it in external editor. { @@ -1505,8 +1508,6 @@ void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) mAssociatedExperience = experience_id; } - - void LLLiveLSLEditor::requestExperiences() { if (!getIsModifiable()) @@ -1558,20 +1559,16 @@ LLScriptEdContainer::~LLScriptEdContainer() delete mLiveLogFile; mLiveLogFile = nullptr; + + if (!mWebSocketServer.expired()) + { + unsubscribeScript(); + } } -std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) +std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) const { - // Take script inventory item id (within the object inventory) - // to consideration so that it's possible to edit multiple scripts - // in the same object inventory simultaneously (STORM-781). - std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString(); - - // Use MD5 sum to make the file name shorter and not exceed maximum path length. - char script_id_hash_str[33]; /* Flawfinder: ignore */ - LLMD5 script_id_hash((const U8 *)script_id.c_str()); - script_id_hash.hex_digest(script_id_hash_str); - + std::string script_id_hash_str(getUniqueHash()); std::string script_extension = mScriptEd->mEditor->getIsLuauLanguage() ? ".luau" : ".lsl"; if (script_name.empty()) @@ -1584,6 +1581,21 @@ std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) } } +std::string LLScriptEdContainer::getUniqueHash() const +{ + // Take script inventory item id (within the object inventory) + // to consideration so that it's possible to edit multiple scripts + // in the same object inventory simultaneously (STORM-781). + std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString(); + + // Use MD5 sum to make the file name shorter and not exceed maximum path length. + char script_id_hash_str[33]; /* Flawfinder: ignore */ + LLMD5 script_id_hash((const U8*)script_id.c_str()); + script_id_hash.hex_digest(script_id_hash_str); + + return std::string(script_id_hash_str); +} + std::string LLScriptEdContainer::getErrorLogFileName(const std::string& script_path) { if (script_path.empty()) @@ -1665,6 +1677,63 @@ bool LLScriptEdContainer::handleKeyHere(KEY key, MASK mask) return true; } +void LLScriptEdContainer::startWebsocketServer() +{ + if (gSavedSettings.getBOOL("ExternalWebsocketSyncEnable")) + { + // Attempt to find an existing server + LLWebsocketMgr& wsmgr = LLWebsocketMgr::instance(); + 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 + 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(LLScriptEditorWSServer::DEFAULT_SERVER_NAME); + } + + if (!is_running && !server->isRunning()) + { // Failed to start the server + LL_WARNS() << "Failed to start script editor websocket server" << LL_ENDL; + return; + } + + std::string script_id_hash_str(getUniqueHash()); + server->subscribeScriptEditor(mObjectUUID, mItemUUID, mScriptEd->mScriptName, getHandle(), script_id_hash_str); + mWebSocketServer = server; + } +} + +void LLScriptEdContainer::unsubscribeScript() +{ + auto server = mWebSocketServer.lock(); + if (server) + { + std::string script_id_hash_str(getUniqueHash()); + server->sendUnsubscribeScriptEditor(script_id_hash_str); + server->unsubscribeEditor(script_id_hash_str); + } +} + +void LLScriptEdContainer::sendCompileResults(LLSD& params) +{ + auto server = mWebSocketServer.lock(); + if (server) + { + std::string script_id_hash_str(getUniqueHash()); + server->sendCompileResults(script_id_hash_str, params); + } +} + /// --------------------------------------------------------------------------- /// LLPreviewLSL /// --------------------------------------------------------------------------- @@ -1902,6 +1971,7 @@ void LLPreviewLSL::finishedLSLUpload(LLUUID itemId, LLSD response) { preview->callbackLSLCompileFailed(response["errors"]); } + preview->sendCompileResults(response); } } @@ -1925,6 +1995,12 @@ bool LLPreviewLSL::failedLSLUpload(LLUUID itemId, LLUUID taskId, LLSD response, LLSD errors; errors.append(LLTrans::getString("UploadFailed") + reason); preview->callbackLSLCompileFailed(errors); + + LLSD message; + message["compiled"] = false; + message["errors"] = errors; + preview->sendCompileResults(message); + return true; } @@ -2459,6 +2535,8 @@ void LLLiveLSLEditor::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAs { preview->callbackLSLCompileFailed(response["errors"]); } + response["is_running"] = isRunning; + preview->sendCompileResults(response); } } |
