From 0cc0db81afec26842ea9887aa50afb072d13914c Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 25 Feb 2025 06:43:52 +0200 Subject: #3422 Better handling of Lua syntax --- indra/newview/llsyntaxid.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 5114ee3672..1f8766eea2 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -40,7 +40,8 @@ //----------------------------------------------------------------------------- const std::string SYNTAX_ID_CAPABILITY_NAME = "LSLSyntax"; const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; -const std::string FILENAME_DEFAULT = "keywords_lsl_default.xml"; +const std::string FILENAME_DEFAULT_LSL = "keywords_lsl_default.xml"; +const std::string FILENAME_DEFAULT_LUA = "keywords_lua_default.xml"; /** * @brief LLSyntaxIdLSL constructor @@ -60,7 +61,7 @@ LLSyntaxIdLSL::LLSyntaxIdLSL() void LLSyntaxIdLSL::buildFullFileSpec() { ELLPath path = mSyntaxId.isNull() ? LL_PATH_APP_SETTINGS : LL_PATH_CACHE; - const std::string filename = mSyntaxId.isNull() ? FILENAME_DEFAULT : "keywords_lsl_" + mSyntaxId.asString() + ".llsd.xml"; + const std::string filename = mSyntaxId.isNull() ? FILENAME_DEFAULT_LSL : "keywords_lsl_" + mSyntaxId.asString() + ".llsd.xml"; mFullFileSpec = gDirUtilp->getExpandedFilename(path, filename); } @@ -325,3 +326,38 @@ boost::signals2::connection LLSyntaxIdLSL::addSyntaxIDCallback(const syntax_id_c { return mSyntaxIDChangedSignal.connect(cb); } + + + +//----------------------------------------------------------------------------- +// LLSyntaxLua +//----------------------------------------------------------------------------- +LLSyntaxLua::LLSyntaxLua() + : mKeywordsXml(LLSD()) + , mInitialized(false) +{ +} + +void LLSyntaxLua::initialize() +{ + if (mInitialized) return; + + loadDefaultKeywordsIntoLLSD(); + + mInitialized = true; +} + +void LLSyntaxLua::loadDefaultKeywordsIntoLLSD() +{ + std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, FILENAME_DEFAULT_LUA); + llifstream file(fullFileSpec.c_str()); + + if (file.good()) + { + LLSD content; + if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) + { + mKeywordsXml = content; + } + } +} -- cgit v1.3 From 684af8426b36f9ad4c2324ae0c5b4dc62f840079 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 17 Sep 2025 13:14:23 -0700 Subject: initial Lua types files, and switch websocket server to use a single connection for all scripts. --- indra/llcommon/llstl.h | 204 +++++++++++++++++ indra/llcorehttp/llwebsocketmgr.cpp | 31 +++ indra/llcorehttp/llwebsocketmgr.h | 62 ++---- indra/newview/CMakeLists.txt | 2 + indra/newview/llpreviewscript.cpp | 48 ++-- indra/newview/llpreviewscript.h | 11 +- indra/newview/llscripteditorws.cpp | 423 +++++++++++++++++++++++------------- indra/newview/llscripteditorws.h | 141 ++++++------ indra/newview/llsyntaxid.cpp | 17 +- indra/newview/llsyntaxid.h | 22 +- 10 files changed, 650 insertions(+), 311 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 7d41c42ba7..0088eeec67 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -35,6 +35,7 @@ #include #include #include +#include #ifdef LL_LINUX // For strcmp @@ -709,5 +710,208 @@ struct ll_template_cast_impl \ } \ } +//----------------------------------------------- +namespace LL +{ + /** + * @brief A range adapter that provides filtered iteration over a container. + * + * filter_range creates a filtered view of an iterator range using a predicate function. + * Only elements that satisfy the predicate will be accessible when iterating through + * the range. This is useful for processing subsets of containers without copying data. + * + * The class uses boost::filter_iterator internally to provide the filtering functionality. + * + * @tparam Predicate A callable object (function, functor, lambda) that takes an element + * from the iterator range and returns true if the element should be + * included in the filtered range. + * @tparam Iterator The iterator type for the underlying container/range. + * + * Example usage: + * @code + * std::vector numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + * + * // Create a predicate to filter even numbers + * auto is_even = [](int n) { return n % 2 == 0; }; + * + * // Create filtered range using make_filter helper + * auto even_range = LL::make_filter(is_even, numbers.begin(), numbers.end()); + * + * // Iterate through only even numbers + * for (auto value : even_range) { + * std::cout << value << " "; // Prints: 2 4 6 8 10 + * } + * + * // Or manually construct the filter_range + * LL::filter_range::iterator> + * manual_range(is_even, numbers.begin(), numbers.end()); + * @endcode + * + * @note This class provides a lightweight view over the original data. + * No copying of elements occurs, making it efficient for large containers. + * @note The predicate is applied during iteration, so complex predicates may + * impact performance for frequently-accessed ranges. + * @note The underlying container must remain valid for the lifetime of the filter_range. + * + * @see make_filter() for a convenient factory function + * @see boost::filter_iterator for the underlying implementation details + */ + template + class filter_range + { + public: + /// The filtered iterator type - combines predicate with base iterator + using filter_iter = boost::filter_iterator; + + /// Value type of the filtered elements + using value_type = typename std::iterator_traits::value_type; + + /// Iterator type for range-based for loops and STL algorithms + using iterator = filter_iter; + using const_iterator = filter_iter; + + /** + * @brief Constructs a filter_range with the given predicate and iterator range. + * + * @param pred The predicate function/functor to filter elements. + * Must be callable with signature: bool(const value_type&) + * @param begin Iterator to the beginning of the range to filter + * @param end Iterator to the end of the range to filter + * + * @pre begin and end must form a valid iterator range + * @pre pred must be a valid callable that can be invoked with elements from [begin, end) + */ + filter_range(Predicate pred, Iterator begin, Iterator end) + : begin_(pred, begin, end), end_(pred, end, end) {} + + /** + * @brief Returns an iterator to the first element that satisfies the predicate. + * + * @return filter_iter Iterator pointing to the first filtered element, + * or equal to end() if no elements satisfy the predicate. + */ + filter_iter begin() const { return begin_; } + + /** + * @brief Returns an iterator representing the end of the filtered range. + * + * @return filter_iter Past-the-end iterator for the filtered range. + */ + filter_iter end() const { return end_; } + + /** + * @brief Checks if the filtered range is empty. + * + * @return true if no elements in the range satisfy the predicate, false otherwise. + * + * @note This operation has O(1) complexity as it only compares iterators. + */ + bool empty() const { return begin_ == end_; } + + private: + filter_iter begin_; ///< Iterator to first element satisfying predicate + filter_iter end_; ///< Past-the-end iterator for the filtered range + }; + + /** + * @brief Factory function to create a filter_range with automatic template deduction. + * + * This convenience function eliminates the need to explicitly specify template parameters + * when creating a filter_range. The template parameters are automatically deduced from + * the function arguments. + * + * @tparam Predicate Automatically deduced predicate type + * @tparam Iterator Automatically deduced iterator type + * + * @param pred Predicate function/functor for filtering elements + * @param begin Iterator to the beginning of the range + * @param end Iterator to the end of the range + * + * @return filter_range A filter_range object configured with + * the provided predicate and range + * + * Example usage: + * @code + * std::vector words = {"hello", "world", "test", "example"}; + * + * // Filter strings longer than 4 characters + * auto long_words = LL::make_filter( + * [](const std::string& s) { return s.length() > 4; }, + * words.begin(), + * words.end() + * ); + * + * // Use with range-based for loop + * for (const auto& word : long_words) { + * std::cout << word << std::endl; // Prints: hello, world, example + * } + * + * // Use with STL algorithms + * auto count = std::distance(long_words.begin(), long_words.end()); + * std::cout << "Found " << count << " long words." << std::endl; + * @endcode + * + * @note This function is preferred over direct construction of filter_range + * for most use cases due to automatic template parameter deduction. + */ + template + filter_range make_filter(Predicate pred, Iterator begin, Iterator end) + { + return filter_range(pred, begin, end); + } + + /** + * @brief Create a filter_range over an entire container with automatic template deduction. + * + * This convenience function creates a filtered view over an entire container without + * requiring explicit begin() and end() calls. It automatically handles both const and + * non-const containers, preserving constness in the resulting iterator types. + * + * @tparam Predicate Automatically deduced predicate type + * @tparam Container Automatically deduced container type (const or non-const) + * + * @param pred Predicate function/functor for filtering elements + * @param container The container to filter (can be const or non-const) + * + * @return filter_range with appropriate iterator type for the container + * + * Example usage: + * @code + * // Non-const container + * std::vector numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + * auto evens = LL::make_filter([](int n) { return n % 2 == 0; }, numbers); + * + * // Const container + * const std::list words = {"cat", "elephant", "dog", "hippopotamus"}; + * auto long_words = LL::make_filter([](const std::string& s) { return s.size() > 3; }, words); + * + * // Works with any container that supports begin()/end() + * std::set values = {1.1, 2.2, 3.3, 4.4, 5.5}; + * auto large_values = LL::make_filter([](double d) { return d > 3.0; }, values); + * + * // Use with range-based for loops + * for (const auto& word : long_words) { + * std::cout << word << " "; // Prints: elephant hippopotamus + * } + * + * // Chain with STL algorithms + * auto even_count = std::distance(evens.begin(), evens.end()); + * std::cout << "Found " << even_count << " even numbers." << std::endl; + * @endcode + * + * @note This overload automatically calls begin() and end() on the container, + * making it more convenient than the iterator-based version. + * @note The container must remain valid for the lifetime of the returned filter_range. + * @note Constness of the container is preserved in the iterator type. + */ + template + filter_range()))> + make_filter(Predicate pred, Container&& container) + { + return filter_range()))>( + pred, std::begin(container), std::end(container)); + } + +} // namespace LL #endif // LL_LLSTL_H diff --git a/indra/llcorehttp/llwebsocketmgr.cpp b/indra/llcorehttp/llwebsocketmgr.cpp index d44d5d877d..09eb728500 100644 --- a/indra/llcorehttp/llwebsocketmgr.cpp +++ b/indra/llcorehttp/llwebsocketmgr.cpp @@ -485,6 +485,7 @@ bool LLWebsocketMgr::WSServer::start() LL_INFOS("WebSocket") << "WebSocket server thread exiting for: " << mServerName << LL_ENDL; }); + onStarted(); LL_INFOS("WebSocket") << "Started WebSocket server thread: " << mServerName << LL_ENDL; return true; } @@ -517,6 +518,7 @@ void LLWebsocketMgr::WSServer::stop() mServerThread.join(); LL_INFOS("WebSocket") << "WebSocket server thread joined for: " << mServerName << LL_ENDL; } + onStopped(); } bool LLWebsocketMgr::WSServer::isRunning() const @@ -596,6 +598,20 @@ LLWebsocketMgr::WSConnection::ptr_t LLWebsocketMgr::WSServer::getConnection(cons return nullptr; } +LLWebsocketMgr::connection_state_t LLWebsocketMgr::WSServer::getConnectionState(const connection_h& handle) const +{ + websocketpp::lib::error_code ec; + auto con = mImpl->mServer.get_con_from_hdl(handle, ec); + if (ec) + { + LL_WARNS("WebSocket") << mServerName << " failed to get connection state: " << ec.message() << LL_ENDL; + websocketpp::session::state::value state = websocketpp::session::state::closed; + return connection_closed; + } + return static_cast(con->get_state()); +} + + void LLWebsocketMgr::WSServer::handleOpenConnection(const connection_h& handle) { WSConnection::ptr_t connection; @@ -709,3 +725,18 @@ void LLWebsocketMgr::WSConnection::closeConnection(U16 code, const std::string& LL_WARNS("WebSocket") << "Failed to close connection through server" << LL_ENDL; } } + +bool LLWebsocketMgr::WSConnection::isConnected() const +{ + if (mOwningServer.expired()) + { + return false; + } + + LLWebsocketMgr::WSServer::ptr_t server = mOwningServer.lock(); + if (!server) + { + return false; + } + return server->getConnectionState(mConnectionHandle) == connection_open; +} diff --git a/indra/llcorehttp/llwebsocketmgr.h b/indra/llcorehttp/llwebsocketmgr.h index 570c8ad6cd..4165b3cecc 100644 --- a/indra/llcorehttp/llwebsocketmgr.h +++ b/indra/llcorehttp/llwebsocketmgr.h @@ -64,6 +64,14 @@ public: using connection_h = websocketpp::connection_hdl; class WSServer; + enum connection_state_t + { // must map to websocketpp::session::state + connection_connecting = 0, + connection_open = 1, + connection_closing = 2, + connection_closed = 3 + }; + class WSConnection { friend class LLWebsocketMgr; @@ -84,8 +92,6 @@ public: virtual ~WSConnection() = default; /** - * @brief Called when the connection is opened - * * Override this method in derived classes to handle connection establishment. * This is called after the WebSocket handshake is complete and the connection * is ready to send/receive messages. @@ -93,8 +99,6 @@ public: virtual void onOpen() {} /** - * @brief Called when the connection is closed - * * Override this method in derived classes to handle connection closure. * This is called when the connection has been terminated, either normally * or due to an error condition. @@ -107,27 +111,6 @@ public: * * Override this method in derived classes to handle incoming messages. * Currently only text messages are supported. - * - * @code - * class MyConnection : public LLWebsocketMgr::WSConnection - * { - * public: - * void onMessage(const std::string& message) override - * { - * // Parse and handle the message - * if (message == "ping") { - * sendMessage("pong"); - * } - * // Process JSON messages - * try { - * LLSD data = LLSDSerialize::fromJSON(message); - * handleStructuredMessage(data); - * } catch (...) { - * LL_WARNS("MyConnection") << "Invalid JSON received" << LL_ENDL; - * } - * } - * }; - * @endcode */ virtual void onMessage(const std::string& message) {} @@ -138,17 +121,6 @@ public: * * Sends a text message to the remote endpoint. The message is queued * asynchronously and may not be sent immediately. - * - * @code - * // Send a simple text message - * connection->sendMessage("Hello, client!"); - * - * // Send JSON data - * LLSD response; - * response["status"] = "ok"; - * response["data"] = "some data"; - * connection->sendMessage(LLSDSerialize::toJSON(response)); - * @endcode */ bool sendMessage(const std::string& message) const; bool sendMessage(const boost::json::value& json) const; @@ -172,22 +144,13 @@ public: * - 1008: Policy violation * - 1009: Message too big * - * @code - * // Normal closure - * connection->closeConnection(); - * - * // Close with specific reason - * connection->closeConnection(1000, "Session ended"); - * - * // Close due to policy violation - * connection->closeConnection(1008, "Authentication failed"); - * @endcode - * * @note After calling this method, no further messages should be sent * @note The onClose() callback will be invoked when the close handshake completes */ void closeConnection(U16 code = 1000, const std::string& reason = std::string()); + bool isConnected() const; + protected: connection_h mConnectionHandle; std::weak_ptr mOwningServer; // Back-reference to the server this connection belongs to @@ -260,6 +223,9 @@ public: WSServer(std::string_view name, U16 port, bool local_only = true); virtual ~WSServer(); + virtual void onStarted() {} + virtual void onStopped() {} + virtual void onConnectionOpened(const WSConnection::ptr_t& connection) { } virtual void onConnectionClosed(const WSConnection::ptr_t& connection) { } @@ -273,6 +239,8 @@ public: void broadcastMessage(const std::string& message); virtual bool update() { return true; } + connection_state_t getConnectionState(const connection_h& handle) const; + protected: virtual WSConnection::ptr_t connectionFactory(WSServer::ptr_t server, connection_h handle); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d1527ef578..6c7b4fef81 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1663,6 +1663,7 @@ set(viewer_APPSETTINGS_FILES app_settings/ignorable_dialogs.xml app_settings/key_bindings.xml app_settings/keywords_lsl_default.xml + app_settings/keywords_lua_default.xml app_settings/logcontrol.xml app_settings/settings.xml app_settings/settings_crash_behavior.xml @@ -1671,6 +1672,7 @@ set(viewer_APPSETTINGS_FILES app_settings/std_bump.ini app_settings/toolbars.xml app_settings/trees.xml + app_settings/types_lua_default.llsd app_settings/viewerart.xml app_settings/message.xml ${CMAKE_SOURCE_DIR}/../scripts/messages/message_template.msg diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 3ab2e747ea..f17b2aeed1 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1553,6 +1553,11 @@ LLScriptEdContainer::~LLScriptEdContainer() delete mLiveLogFile; mLiveLogFile = nullptr; + + if (!mWebSocketServer.expired()) + { + unsubscribeScript(); + } } std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) const @@ -1675,44 +1680,45 @@ void LLScriptEdContainer::startWebsocketServer() 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(wsmgr.findServerByName(server_name)); if (!server) - { + { // We couldn't find one, so create it server = std::make_shared(server_name, server_port, server_localhost); wsmgr.addServer(server); - wsmgr.startServer(server_name); + } + + bool is_running = server->isRunning(); + if (!is_running) + { // Server isn't running, so start it + is_running = wsmgr.startServer(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->associateEditor(getHandle(), script_id_hash_str); + server->subscribeScriptEditor(getHandle(), script_id_hash_str); + mWebSocketServer = server; } } -void LLScriptEdContainer::attachToWebSocket(const std::shared_ptr& connection) +void LLScriptEdContainer::unsubscribeScript() { - mWebSocket = connection; -} - -void LLScriptEdContainer::detachFromWebSocket(bool send_disconnect) -{ - if (mWebSocket) + auto server = mWebSocketServer.lock(); + if (server) { - if (send_disconnect) - { - // TODO: - mWebSocket->sendDisconnect(LLScriptEditorWSConnection::REASON_EDITOR_CLOSED); - mWebSocket->closeConnection(); - } - mWebSocket.reset(); + std::string script_id_hash_str(getUniqueHash()); + server->sendUnsubscribeScriptEditor(script_id_hash_str); + server->unsubscribeEditor(script_id_hash_str); } } -void LLScriptEdContainer::cleanupWebSocket() -{ - mWebSocket.reset(); -} /// --------------------------------------------------------------------------- /// LLPreviewLSL diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 72d52a115e..8d53c8899c 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -54,7 +54,7 @@ class LLScriptEdContainer; class LLFloaterGotoLine; class LLFloaterExperienceProfile; class LLScriptMovedObserver; -class LLScriptEditorWSConnection; +class LLScriptEditorWSServer; class LLLiveLSLFile : public LLLiveFile { @@ -105,6 +105,7 @@ public: void initMenu(); void processKeywords(); void processKeywords(bool luau_language); + LLKeywords& getKeywords() { return mEditor->getKeywords(); } void draw() override; bool postBuild() override; @@ -221,9 +222,9 @@ public: bool handleKeyHere(KEY key, MASK mask); void startWebsocketServer(); - void attachToWebSocket(const std::shared_ptr& connection); - void detachFromWebSocket(bool send_disconnect); - void cleanupWebSocket(); + void unsubscribeScript(); + + LLScriptEdCore* getScriptEdCore() const { return mScriptEd; } protected: std::string getTmpFileName(const std::string& script_name) const; @@ -238,7 +239,7 @@ protected: LLLiveLSLFile* mLiveFile = nullptr; LLLiveLSLFile* mLiveLogFile = nullptr; - std::shared_ptr mWebSocket; + std::weak_ptr mWebSocketServer; }; // Used to view and edit an LSL script from your inventory. diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp index 9863130aea..cb67880455 100644 --- a/indra/newview/llscripteditorws.cpp +++ b/indra/newview/llscripteditorws.cpp @@ -24,24 +24,6 @@ * $/LicenseInfo$ */ -/** - * This implementation provides JSON-RPC 2.0 WebSocket communication between - * the Second Life viewer and external script editors. It uses the standard - * JSON-RPC 2.0 protocol without pre-defined script-specific methods, - * allowing for flexible integration approaches. - * - * ## JSON-RPC Integration - * - * The connection provides a clean JSON-RPC 2.0 interface that can be - * extended with script-specific functionality as needed: - * - * ### Server-to-Client (Viewer to Editor): - * - `session.handshake`: Welcome message on connection - * - `session.disconnect`: Notify editor of disconnection - * - * ### Notifications (no response expected): - */ - #include "llviewerprecompiledheaders.h" #include "llscripteditorws.h" #include "llpreviewscript.h" @@ -51,6 +33,7 @@ #include "llerror.h" #include "lluuid.h" #include "llversioninfo.h" +#include "llagent.h" //======================================================================== LLScriptEditorWSServer::LLScriptEditorWSServer(const std::string& name, U16 port, bool local_only) @@ -64,7 +47,7 @@ LLWebsocketMgr::WSConnection::ptr_t LLScriptEditorWSServer::connectionFactory(LL LLWebsocketMgr::connection_h handle) { auto connection = std::make_shared(server, handle); - mActiveConnections.insert(connection); + mActiveConnections[connection->getConnectionID()] = connection; // Call setupConnectionMethods to register any global methods setupConnectionMethods(connection); @@ -72,6 +55,29 @@ LLWebsocketMgr::WSConnection::ptr_t LLScriptEditorWSServer::connectionFactory(LL return connection; } +void LLScriptEditorWSServer::onStarted() +{ + LLSyntaxIdLSL& syntax_id_mgr = LLSyntaxIdLSL::instance(); + wptr_t that(std::static_pointer_cast(shared_from_this())); + + mLastSyntaxId = syntax_id_mgr.getSyntaxID(); + mLanguageChangeSignal = syntax_id_mgr.addSyntaxIDCallback( + [that]() + { + auto server = that.lock(); + if (server && server->isRunning()) + { + server->broadcastLangugeChange(); + } + }); +} + +void LLScriptEditorWSServer::onStopped() +{ + mLanguageChangeSignal.disconnect(); + mLastSyntaxId.setNull(); +} + void LLScriptEditorWSServer::onConnectionOpened(const LLWebsocketMgr::WSConnection::ptr_t& connection) { // Call parent class to handle JSON-RPC setup and standard methods @@ -92,35 +98,102 @@ void LLScriptEditorWSServer::onConnectionClosed(const LLWebsocketMgr::WSConnecti auto script_connection = std::dynamic_pointer_cast(connection); if (script_connection) { - mActiveConnections.erase(script_connection); + U32 connection_id = script_connection->getConnectionID(); + unsubscribeConnection(connection_id); + mActiveConnections.erase(connection_id); - LL_INFOS("ScriptEditorWS") << "Removed connection from active connections. Total: " + LL_DEBUGS("ScriptEditorWS") << "Removed connection from active connections. Total: " << mActiveConnections.size() << LL_ENDL; // TODO: When connections reach 0, stop the server aftera a timeout. } } -bool LLScriptEditorWSServer::associateEditor(const LLHandle& editor_handle, const std::string& script_id) +bool LLScriptEditorWSServer::subscribeScriptEditor(const LLHandle& editor_handle, const std::string &script_id) { if (!editor_handle.isDead()) { - mScriptEditors[script_id] = editor_handle; + 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() }); + return false; + } + else + { // Update existing subscription with new editor handle + it->second.mEditorHandle = editor_handle; + } return true; } return false; } -void LLScriptEditorWSServer::dissociateEditor(const std::string& script_id) +void LLScriptEditorWSServer::unsubscribeEditor(const std::string &script_id) +{ + auto it = mSubscriptions.find(script_id); + if (it != mSubscriptions.end()) + { + mSubscriptions.erase(it); + } +} + +void LLScriptEditorWSServer::unsubscribeConnection(U32 connection_id) +{ + for (auto it = mSubscriptions.begin(); it != mSubscriptions.end(); ) + { + if (it->second.mConnectionID == connection_id) + { + LL_DEBUGS("ScriptEditorWS") << "Unsubscribing script " << it->first + << " from connection ID " << connection_id << LL_ENDL; + it = mSubscriptions.erase(it); + } + else + { + ++it; + } + } +} + +LLScriptEditorWSServer::SubscriptionError_t LLScriptEditorWSServer::updateScriptSubscription(const std::string &script_id, U32 connection_id) { - mScriptEditors.erase(script_id); + auto it = mSubscriptions.find(script_id); + if (it != mSubscriptions.end()) + { + if (it->second.mEditorHandle.isDead()) + { + unsubscribeEditor(script_id); + return SUBSCRIPTION_INVALID_EDITOR; + } + + auto con_it = mActiveConnections.find(connection_id); + if (con_it == mActiveConnections.end()) + { + return SUBSCRIPTION_INTERNAL_ERROR; + } + + if ((it->second.mConnectionID != 0) && !it->second.mConnection.expired() + && it->second.mConnection.lock()->isConnected()) + { + LL_WARNS("ScriptEditorWS") << "Script " << script_id << " is already subscribed on connection ID " << it->second.mConnectionID + << ", cannot subscribe again on connection ID " << connection_id << LL_ENDL; + // In the future we may want to support multiple connections per script. + // That would imply it was open in multiple editors. + return SUBSCRIPTION_ALREADY_SUBSCRIBED; + } + + it->second.mConnectionID = connection_id; + it->second.mConnection = con_it->second; + return SUBSCRIPTION_SUCCESS; + } + return SUBSCRIPTION_INVALID_SUBSCRIPTION; } + LLHandle LLScriptEditorWSServer::findEditorForScript(const std::string& script_id) const { - auto it = mScriptEditors.find(script_id); - if (it != mScriptEditors.end()) + auto it = mSubscriptions.find(script_id); + if (it != mSubscriptions.end()) { - return it->second; + return it->second.mEditorHandle; } return LLHandle(); } @@ -135,9 +208,9 @@ std::shared_ptr LLScriptEditorWSServer::findConnecti std::set LLScriptEditorWSServer::getActiveScripts() const { std::set active_scripts; - for (const auto& [script_id, editor_handle] : mScriptEditors) + for (const auto& [script_id, subinfo] : mSubscriptions) { - if (!editor_handle.isDead()) + if (!subinfo.mEditorHandle.isDead()) { active_scripts.insert(script_id); } @@ -145,69 +218,182 @@ std::set LLScriptEditorWSServer::getActiveScripts() const return active_scripts; } -void LLScriptEditorWSServer::broadcastScriptUpdate(const std::string& script_id, const std::string& content, const LLSD& metadata) +void LLScriptEditorWSServer::setupConnectionMethods(LLJSONRPCConnection::ptr_t connection) { - LL_DEBUGS("ScriptEditorWS") << "Broadcasting script update for script: " << script_id << LL_ENDL; + // Call parent class to register global JSON-RPC methods + LLJSONRPCServer::setupConnectionMethods(connection); - LLSD params; - params["script_id"] = script_id; - params["content"] = content; - params["timestamp"] = LLDate::now().asString(); + // Cast to our specific connection type to access script editor functionality + auto script_connection = std::dynamic_pointer_cast(connection); + if (script_connection) + { + LL_DEBUGS("ScriptEditorWS") << "Setting up script editor connection methods" << LL_ENDL; + wptr_t that(std::static_pointer_cast(shared_from_this())); + + U32 connection_id = script_connection->getConnectionID(); + + script_connection->registerMethod("language.syntax.id", + [that](const std::string&, const LLSD&, const LLSD&) -> LLSD + { + auto server = that.lock(); + if (server) + { + return server->handleLanguageIdRequest(); + } + return LLSD(); + }); + script_connection->registerMethod("language.syntax", + [that](const std::string&, const LLSD&, const LLSD& params) + { + auto server = that.lock(); + if (server) + { + return server->handleSyntaxRequest(params); + } + return LLSD(); + }); + script_connection->registerMethod("script.subscribe", + [that, connection_id](const std::string&, const LLSD&, const LLSD& params) -> LLSD + { + auto server = that.lock(); + if (server) + { + return server->handleScriptSubscribe(connection_id, params); + } + return LLSD(); + }); + script_connection->registerMethod("script.unsubscribe", [](const std::string&, const LLSD&, const LLSD& params) -> LLSD + { // this is a notification, no response expected + return LLSD(); + }); + // script_connection->registerMethod("language.syntax", ) + } +} + +void LLScriptEditorWSServer::broadcastLangugeChange() +{ + LLUUID syntax_id = LLSyntaxIdLSL::instance().getSyntaxID(); - if (!metadata.isUndefined()) + if (syntax_id != mLastSyntaxId) { - params["metadata"] = metadata; + mLastSyntaxId = syntax_id; + LLSD params; + params["id"] = syntax_id; + + if (isRunning()) + { + broadcastNotification("language.syntax.change", params); + } } +} - // Send to all connected editors as a notification - broadcastNotification("script.update", params); +LLSD LLScriptEditorWSServer::handleLanguageIdRequest() const +{ + LLSD response; + + response["id"] = mLastSyntaxId; + return response; } -void LLScriptEditorWSServer::broadcastCompilationResult(const std::string& script_id, bool success, const LLSD& errors) +LLSD LLScriptEditorWSServer::handleSyntaxRequest(const LLSD& params) const { - LL_DEBUGS("ScriptEditorWS") << "Broadcasting compilation result for script: " << script_id - << " (success: " << success << ")" << LL_ENDL; + LLSD response(LLSD::emptyMap()); + std::string category = params["kind"].asString(); - LLSD params; - params["script_id"] = script_id; - params["success"] = success; - params["timestamp"] = LLDate::now().asString(); + response["id"] = mLastSyntaxId; - if (!errors.isUndefined() && errors.isArray()) + if (category == "types.luau") { - params["errors"] = errors; + response["types"] = LLSyntaxLua::instance().getTypesXML(); } + else + { + LLSD syntax = LLSyntaxIdLSL::instance().getKeywordsXML(); + + // TODO: support language definitions and additional modules. - // Send to all connected editors as a notification - broadcastNotification("compilation.result", params); + if (syntax.has(category)) + { + response[category] = syntax[category]; + } + } + return response; } -void LLScriptEditorWSServer::setupConnectionMethods(LLJSONRPCConnection::ptr_t connection) +LLSD LLScriptEditorWSServer::handleScriptSubscribe(U32 connection_id, const LLSD& params) { - // Call parent class to register global JSON-RPC methods - LLJSONRPCServer::setupConnectionMethods(connection); + LLSD response(LLSD::emptyMap()); - // Cast to our specific connection type to access script editor functionality - auto script_connection = std::dynamic_pointer_cast(connection); - if (script_connection) + std::string script_id = params["script_id"].asString(); + std::string script_name = params["script_name"].asString(); + std::string language = params["script_language"].asString(); + + SubscriptionError_t result = updateScriptSubscription(script_id, connection_id); + + response["script_id"] = script_id; + response["success"] = (result == SUBSCRIPTION_SUCCESS); + response["status"] = result; + + LL_WARNS_IF(result != SUBSCRIPTION_SUCCESS, "ScriptEditorWS") + << "Script connect request for script " << script_id << " failed with status " << result << LL_ENDL; + switch (result) { - LL_INFOS("ScriptEditorWS") << "Setting up script editor connection methods" << LL_ENDL; + case SUBSCRIPTION_SUCCESS: + response["message"] = "OK"; + break; + case SUBSCRIPTION_INVALID_EDITOR: + response["message"] = "Invalid editor handle"; + break; + case SUBSCRIPTION_INVALID_SUBSCRIPTION: + response["message"] = "No subscription found for script"; + break; + case SUBSCRIPTION_ALREADY_SUBSCRIBED: + response["message"] = "Script already subscribed"; + break; + case SUBSCRIPTION_INTERNAL_ERROR: + response["message"] = "Internal server error"; + break; + } + + if (result == SUBSCRIPTION_SUCCESS) + { + //TODO: Build an info block for the subscribed script. + //buildScriptSubscriptionInfo(result); + } - // Here derived classes could add script-specific method registrations - // For now, the base LLScriptEditorWSConnection doesn't register any specific methods - // but this provides a hook for future customization + return response; +} + +LLSD LLScriptEditorWSServer::handleScriptUnsubscribe(U32 connection_id, const LLSD& params) +{ + std::string script_id = params["script_id"].asString(); - // Example of how custom methods could be registered: - // script_connection->registerMethod("script.custom", handler); + auto it = mSubscriptions.find(script_id); + if (it != mSubscriptions.end() && (it->second.mConnectionID == connection_id)) + { + unsubscribeEditor(script_id); } + return LLSD(); } -//======================================================================== -LLScriptEdContainer* LLScriptEditorWSConnection::getEditor() const +void LLScriptEditorWSServer::sendUnsubscribeScriptEditor(const std::string& script_id) { - return mEditorPanel.isDead() ? nullptr : dynamic_cast(mEditorPanel.get()); + auto it = mSubscriptions.find(script_id); + if (it != mSubscriptions.end()) + { + auto connection = it->second.mConnection.lock(); + if (connection) + { + LLSD params; + params["script_id"] = script_id; + connection->notify("script.unsubscribe", params); + } + } } +//======================================================================== +U32 LLScriptEditorWSConnection::sNextConnectionID = 1; + std::shared_ptr LLScriptEditorWSConnection::getServer() const { return std::static_pointer_cast(mOwningServer.lock()); @@ -220,20 +406,17 @@ void LLScriptEditorWSConnection::onOpen() LL_INFOS("ScriptEditorWS") << "Script editor JSON-RPC connection opened" << LL_ENDL; - // Generate unique editor session ID - mEditorId = LLUUID::generateNewID().asString(); - mEditorReady = false; - - LL_INFOS("ScriptEditorWS") << "Initialized editor session: " << mEditorId << LL_ENDL; - - // Build hello data according to the protocol specification + // Build hello data LLSD handshake; handshake["server_version"] = "1.0.0"; handshake["protocol_version"] = "1.0"; handshake["viewer_name"] = LLVersionInfo::instance().getChannel(); handshake["viewer_version"] = LLVersionInfo::instance().getVersion(); - // Supported languages array + handshake["agent_id"] = gAgent.getID(); + + // handshake["challenge"] = ... TODO: simple challenge, write to a file and have the client echo it back? + LLSD languages = LLSD::emptyArray(); languages.append("lsl"); languages.append("luau"); @@ -243,14 +426,19 @@ void LLScriptEditorWSConnection::onOpen() LLSD features; features["live_sync"] = true; features["compilation"] = true; - features["syntax_highlight"] = true; handshake["features"] = features; - // Send editor.handshake method call to the client and handle response - call("session.handshake", handshake, [this](const LLSD& result, const LLSD& error) { + wptr_t that = shared_from_this(); + + // Send session.handshake method call and the response + call("session.handshake", handshake, [that](const LLSD& result, const LLSD& error) { if (error.isUndefined()) { - handleHandshakeResponse(result); + auto self = that.lock(); + if (self) + { + self->handleHandshakeResponse(result); + } } else { @@ -266,17 +454,7 @@ void LLScriptEditorWSConnection::onClose() { // Call parent class to clean up JSON-RPC infrastructure LLJSONRPCConnection::onClose(); - - LL_INFOS("ScriptEditorWS") << "Script editor JSON-RPC connection closed for session: " - << mEditorId << LL_ENDL; - - cleanupConnection(); - - // Clean up editor-specific state - mEditorId.clear(); - mEditorCapabilities.clear(); - mScriptId.clear(); - mEditorReady = false; + mOwningServer.reset(); // Clean up handshake response data mClientName.clear(); @@ -297,6 +475,8 @@ void LLScriptEditorWSConnection::handleHandshakeResponse(const LLSD& result) mClientVersion = result["client_version"].asString(); mProtocolVersion = result["protocol_version"].asString(); + // TODO: Validate challenge_response if implemented + // Validate protocol compatibility if (mProtocolVersion != "1.0") { @@ -307,7 +487,6 @@ void LLScriptEditorWSConnection::handleHandshakeResponse(const LLSD& result) // Store script information if provided mScriptName = result["script_name"].asString(); mScriptLanguage = result["script_language"].asString(); - mScriptId = result["script_id"].asString(); // Store supported languages for (const auto& lang : llsd::inArray( result["languages"])) @@ -326,67 +505,7 @@ void LLScriptEditorWSConnection::handleHandshakeResponse(const LLSD& result) } } - connectToEditor(mScriptId); - // Mark editor as ready - mEditorReady = true; - - LL_INFOS("ScriptEditorWS") << "Handshake completed successfully for session: " << mEditorId << LL_ENDL; -} - -bool LLScriptEditorWSConnection::connectToEditor(const std::string& script_id) -{ - LLScriptEditorWSServer::ptr_t server = std::dynamic_pointer_cast(mOwningServer.lock()); - if (!server) - { - LL_WARNS("ScriptEditorWS") << "Cannot connect to editor - server reference lost" << LL_ENDL; - return false; - } - - mEditorPanel = server->findEditorForScript(script_id); - - LLScriptEdContainer* editor_core = getEditor(); - if (!editor_core) - { - LL_INFOS("ScriptEditorWS") << "Could not find editor: " << script_id << LL_ENDL; - // TODO: Disconnect the client if no editor found - return false; - } - - return true; -} - -void LLScriptEditorWSConnection::cleanupConnection() -{ - LL_INFOS("ScriptEditorWS") << "Cleaning up connection for editor session: " << mEditorId << LL_ENDL; - - LLScriptEditorWSServer::ptr_t server = getServer(); - if (server) - { - server->dissociateEditor(mScriptId); - } - - LLScriptEdContainer* editor_core = getEditor(); - - if (editor_core) - { - editor_core->cleanupWebSocket(); - - // Notify the editor panel of disconnection - //editor_core->onExternalEditorDisconnected(); - } - - mEditorPanel = LLHandle(); -} - - -void LLScriptEditorWSConnection::sendDisconnect(S32 reason, const std::string& message) -{ - LL_INFOS("ScriptEditorWS") << "Sending disconnect message to editor (reason: " - << reason << ", message: " << message << ")" << LL_ENDL; - - LLSD params; - params["reason"] = reason; - params["message"] = message; + notify("session.ok"); - notify("session.disconnect", params); + LL_INFOS("ScriptEditorWS") << "Handshake completed successfully." << LL_ENDL; } diff --git a/indra/newview/llscripteditorws.h b/indra/newview/llscripteditorws.h index 78c675a1f4..f6e40fad6e 100644 --- a/indra/newview/llscripteditorws.h +++ b/indra/newview/llscripteditorws.h @@ -42,44 +42,30 @@ class LLLiveLSLEditor; class LLScriptEdContainer; class LLScriptEditorWSServer; -/** - * @class LLScriptEditorWSConnection - * @brief JSON-RPC WebSocket connection specialized for external script editor communication - * - * This class handles JSON-RPC 2.0 communication between the Second Life viewer - * and external script editors. It provides a clean base for implementing - * script editor integration using the standard JSON-RPC 2.0 protocol. - * - * ## Usage - * - * @code - * // Create server and let base JSON-RPC handle method registration - * auto server = std::make_shared("script_editor_server", 9020); - * - * // Register custom methods as needed - * connection->registerMethod("custom.method", handler); - * @endcode - */ -class LLScriptEditorWSConnection : public LLJSONRPCConnection, - public std::enable_shared_from_this +class LLScriptEditorWSConnection : public LLJSONRPCConnection, public std::enable_shared_from_this { public: + using ptr_t = std::shared_ptr; + using wptr_t = std::weak_ptr; + enum DisconnectReason { - REASON_NORMAL = 0, - REASON_EDITOR_CLOSED = 1, + REASON_NORMAL = 0, + REASON_EDITOR_CLOSED = 1, REASON_PROTOCOL_ERROR = 2, - REASON_TIMEOUT = 3, + REASON_TIMEOUT = 3, REASON_INTERNAL_ERROR = 4 }; - LLScriptEditorWSConnection(const LLWebsocketMgr::WSServer::ptr_t server, - const LLWebsocketMgr::connection_h& handle) - : LLJSONRPCConnection(server, handle) - { } + LLScriptEditorWSConnection(const LLWebsocketMgr::WSServer::ptr_t server, const LLWebsocketMgr::connection_h& handle) : + LLJSONRPCConnection(server, handle) + { + mConnectionID = sNextConnectionID++; + } ~LLScriptEditorWSConnection() override = default; + U32 getConnectionID() const { return mConnectionID; } // Connection lifecycle overrides void onOpen() override; @@ -100,26 +86,21 @@ private: */ void handleHandshakeResponse(const LLSD& result); - bool connectToEditor(const std::string& script_id); - void cleanupConnection(); - - LLScriptEdContainer* getEditor() const; + LLScriptEdContainer* getEditor() const; std::shared_ptr getServer() const; - std::string mEditorId; ///< Unique identifier for this editor session - LLSD mEditorCapabilities; ///< Editor capabilities metadata - std::string mScriptId; ///< Unique identifier for the script being edited - bool mEditorReady; ///< Whether editor has completed initialization - LLHandle mEditorPanel; ///< Handle to the associated LSL editor panel + U32 mConnectionID{ 0 }; ///< Unique identifier for this connection // Client handshake response data - std::string mClientName; ///< Name of the external editor client - std::string mClientVersion; ///< Version of the external editor client - std::string mProtocolVersion; ///< JSON-RPC protocol version supported by client - std::string mScriptName; ///< Name of the script being edited - std::string mScriptLanguage; ///< Programming language of the script (lsl, luau, etc.) - string_set_t mLanguages; ///< Set of supported scripting languages - string_set_t mFeatures; ///< Active client features (live_sync, compilation, etc.) + std::string mClientName; ///< Name of the external editor client + std::string mClientVersion; ///< Version of the external editor client + std::string mProtocolVersion; ///< JSON-RPC protocol version supported by client + std::string mScriptName; ///< Name of the script being edited + std::string mScriptLanguage; ///< Programming language of the script (lsl, luau, etc.) + string_set_t mLanguages; ///< Set of supported scripting languages + string_set_t mFeatures; ///< Active client features (live_sync, compilation, etc.) + + static U32 sNextConnectionID; }; /** @@ -161,21 +142,34 @@ private: class LLScriptEditorWSServer : public LLJSONRPCServer { public: + enum SubscriptionError_t + { + SUBSCRIPTION_SUCCESS = 0, + SUBSCRIPTION_INVALID_EDITOR, + SUBSCRIPTION_INVALID_SUBSCRIPTION, + SUBSCRIPTION_ALREADY_SUBSCRIBED, + SUBSCRIPTION_INTERNAL_ERROR + }; + static constexpr char const* DEFAULT_SERVER_NAME = "script_editor_server"; static constexpr U16 DEFAULT_SERVER_PORT = 9020; using ptr_t = std::shared_ptr; + using wptr_t = std::weak_ptr; LLScriptEditorWSServer(const std::string& name, U16 port, bool local_only = true); virtual ~LLScriptEditorWSServer() = default; - // Server lifecycle callbacks + 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 associateEditor(const LLHandle& editor_handle, const std::string& script_id); - void dissociateEditor(const std::string& script_id); + bool subscribeScriptEditor(const LLHandle& editor_handle, const std::string &script_id); + void unsubscribeEditor(const std::string &script_id); + + void sendUnsubscribeScriptEditor(const std::string& script_id); LLHandle findEditorForScript(const std::string& script_id) const; std::shared_ptr findConnectionForScript(const std::string& script_id); @@ -186,45 +180,38 @@ public: */ std::set getActiveScripts() const; - /** - * @brief Send script content to all connected editors for a specific script - * @param script_id The script identifier - * @param content The script content - * @param metadata Optional metadata about the script - */ - void broadcastScriptUpdate(const std::string& script_id, const std::string& content, const LLSD& metadata = LLSD()); - - /** - * @brief Send compilation results to all connected editors for a specific script - * @param script_id The script identifier - * @param success Whether compilation succeeded - * @param errors Array of compilation errors/warnings - */ - void broadcastCompilationResult(const std::string& script_id, bool success, const LLSD& errors = LLSD()); - protected: LLWebsocketMgr::WSConnection::ptr_t connectionFactory(LLWebsocketMgr::WSServer::ptr_t server, LLWebsocketMgr::connection_h handle) override; - /** - * @brief Apply global method handlers to a new connection - * @param connection The connection to configure - * - * Override this method to customize which methods are registered on - * new connections. The base implementation registers all global methods, - * but derived classes can add additional script-specific methods. - */ - virtual void setupConnectionMethods(LLJSONRPCConnection::ptr_t connection) override; + void setupConnectionMethods(LLJSONRPCConnection::ptr_t connection) override; + + void broadcastLangugeChange(); + + LLSD handleLanguageIdRequest() const; + LLSD handleSyntaxRequest(const LLSD ¶ms) const; + LLSD handleScriptSubscribe(U32 connection_id, const LLSD& params); + LLSD handleScriptUnsubscribe(U32 connection_id, const LLSD& params); private: - using map_id_to_editor_t = std::unordered_map>; + struct EditorSubscription + { + LLHandle mEditorHandle; + LLScriptEditorWSConnection::wptr_t mConnection; + U32 mConnectionID{ 0 }; + }; + using subscriptions_t = std::unordered_map; + + SubscriptionError_t updateScriptSubscription(const std::string &script_id, U32 connection_id); + void unsubscribeConnection(U32 connection_id); + + subscriptions_t mSubscriptions; + std::map mActiveConnections; + + boost::signals2::connection mLanguageChangeSignal; + LLUUID mLastSyntaxId; - map_id_to_editor_t mScriptEditors; - std::set> mActiveConnections; - /** - * @brief Connection timeout management - */ LLTimer mCleanupTimer; static constexpr F32 CLEANUP_INTERVAL = 60.0f; // seconds static constexpr F32 CONNECTION_TIMEOUT = 300.0f; // 5 minutes diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 1f8766eea2..a891bf945e 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -343,7 +343,7 @@ void LLSyntaxLua::initialize() if (mInitialized) return; loadDefaultKeywordsIntoLLSD(); - + loadLuaTypesIntoLLSD(); mInitialized = true; } @@ -361,3 +361,18 @@ void LLSyntaxLua::loadDefaultKeywordsIntoLLSD() } } } + +void LLSyntaxLua::loadLuaTypesIntoLLSD() +{ + std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "types_lua_default.llsd"); + llifstream file(fullFileSpec.c_str()); + + if (file.good()) + { + LLSD content; + if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) + { + mTypesXml = content; + } + } +} diff --git a/indra/newview/llsyntaxid.h b/indra/newview/llsyntaxid.h index c24cea1776..51103e3396 100644 --- a/indra/newview/llsyntaxid.h +++ b/indra/newview/llsyntaxid.h @@ -41,11 +41,20 @@ class LLSyntaxIdLSL : public LLSingleton LLSINGLETON(LLSyntaxIdLSL); friend class fetchKeywordsFileResponder; +public: + using syntax_id_changed_signal_t = boost::signals2::signal; + using syntax_id_changed_h = boost::signals2::connection; + + void initialize(); + bool keywordFetchInProgress(); + LLSD getKeywordsXML() const { return mKeywordsXml; }; + LLUUID getSyntaxID() const { return mSyntaxId; } + syntax_id_changed_h addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb); + private: std::set mInflightFetches; - typedef boost::signals2::signal syntax_id_changed_signal_t; syntax_id_changed_signal_t mSyntaxIDChangedSignal; - boost::signals2::connection mRegionChangedCallback; + syntax_id_changed_h mRegionChangedCallback; bool syntaxIdChanged(); bool isSupportedVersion(const LLSD& content); @@ -67,11 +76,6 @@ private: LLSD mKeywordsXml; bool mInitialized; -public: - void initialize(); - bool keywordFetchInProgress(); - LLSD getKeywordsXML() const { return mKeywordsXml; }; - boost::signals2::connection addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb); }; @@ -82,11 +86,13 @@ class LLSyntaxLua : public LLSingleton public: void initialize(); LLSD getKeywordsXML() const { return mKeywordsXml; } + LLSD getTypesXML() const { return mTypesXml; } private: void loadDefaultKeywordsIntoLLSD(); - + void loadLuaTypesIntoLLSD(); LLSD mKeywordsXml; + LLSD mTypesXml; bool mInitialized; }; -- cgit v1.3 From 5306ad767bc1cc768145074aac25944c6059b3c2 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 8 Oct 2025 17:42:32 -0700 Subject: Renamed types_lua_default.llsd to types_lua_default.xml, returning an empty lang def is not a success. Hopefully this will package correctly. --- indra/newview/CMakeLists.txt | 2 +- indra/newview/app_settings/types_lua_default.llsd | 2488 --------------------- indra/newview/app_settings/types_lua_default.xml | 2488 +++++++++++++++++++++ indra/newview/llscripteditorws.cpp | 4 +- indra/newview/llsyntaxid.cpp | 2 +- 5 files changed, 2492 insertions(+), 2492 deletions(-) delete mode 100644 indra/newview/app_settings/types_lua_default.llsd create mode 100644 indra/newview/app_settings/types_lua_default.xml (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 702a738e57..87e350a69d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1674,7 +1674,7 @@ set(viewer_APPSETTINGS_FILES app_settings/std_bump.ini app_settings/toolbars.xml app_settings/trees.xml - app_settings/types_lua_default.llsd + app_settings/types_lua_default.xml app_settings/viewerart.xml app_settings/message.xml ${CMAKE_SOURCE_DIR}/../scripts/messages/message_template.msg diff --git a/indra/newview/app_settings/types_lua_default.llsd b/indra/newview/app_settings/types_lua_default.llsd deleted file mode 100644 index 5881df5ba5..0000000000 --- a/indra/newview/app_settings/types_lua_default.llsd +++ /dev/null @@ -1,2488 +0,0 @@ - - - - $schema - ./sl-lua-defs.schema.json - aliases - - list - - aliasTypes - - string - number - integer - vector - uuid - quaternion - boolean - - aliasIndex - number - tooltip - A table with numeric indices containing various value types - - table - - aliasTypes - - any - - aliasIndex - any - tooltip - A table with any type of indices and values - - numeric - - aliasTypes - - number - boolean - integer - - tooltip - A union type representing numeric values - - lljson_constant - - aliasTypes - - number - - tooltip - A constant value used in lljson module - - uuid_like - - aliasTypes - - string - uuid - - tooltip - A union type representing uuid values - - EventHandler - - aliasTypes - - (...any) -> () - - tooltip - A function type for event handlers that accepts variable arguments - - - functions - - toquaternion - - parameters - - - name - str - type - string - - - returnType - quaternion | nil - tooltip - Creates a quaternion from a string argument in format `<1,1,1,1>`. Invalid strings will return `nil`. Due to an old error from LSL, strings that match up to the closing `>` are interpreted as valid. So `<1,1,1,1` and `<1,1,1,1spoon` are both cast to `<1,1,1,1>`. When testing if a string is a quaternion or a vector, you should test with `toquaternion` first. - - tovector - - parameters - - - name - str - type - string - - - returnType - vector | nil - tooltip - Creates a vector from a string argument in format `<1,1,1>`. Invalid strings will return `nil`. Due to an old error from LSL, strings that match up to the closing `>` are interpreted as valid. So `<1,1,1`, `<1,1,1,1` and `<1,1,1spoon` are all cast to `<1,1,1>`. When testing if a string is a quaternion or a vector, you should test with `toquaternion` first. - - integer - - parameters - - - name - value - type - string | number - - - returnType - integer - tooltip - Creates an integer value from argument - - uuid - - parameters - - - name - str - type - string - - - returnType - uuid - tooltip - Creates a uuid from a string argument - - vector - - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - returnType - vector - tooltip - Creates a vector. - - quaternion - - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - name - s - type - number - - - returnType - quaternion - tooltip - Creates a quaternion rotation. - - - classes - - integer - - methods - - __add - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - integer - tooltip - Addition operator for integers - - - parameters - - - name - self - type - integer - - - name - other - type - number - - - returnType - number - tooltip - Addition operator for integer and number - - - __sub - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - integer - tooltip - Subtraction operator for integers - - - parameters - - - name - self - type - integer - - - name - other - type - number - - - returnType - number - tooltip - Subtraction operator for integer and number - - - __mul - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - integer - tooltip - Multiplication operator for integers - - - parameters - - - name - self - type - integer - - - name - other - type - number - - - returnType - number - tooltip - Multiplication operator for integer and number - - - __div - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - integer - tooltip - Division operator for integers - - - parameters - - - name - self - type - integer - - - name - other - type - number - - - returnType - number - tooltip - Division operator for integer and number - - - __unm - - - parameters - - - name - self - type - integer - - - returnType - integer - tooltip - Unary minus operator - - - __mod - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - integer - tooltip - Modulo operator for integers - - - parameters - - - name - self - type - integer - - - name - other - type - number - - - returnType - number - tooltip - Modulo operator for integer and number - - - __pow - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - integer - tooltip - Power operator for integers - - - parameters - - - name - self - type - integer - - - name - other - type - number - - - returnType - number - tooltip - Power operator for integer and number - - - __idiv - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - integer - tooltip - Integer division operator for integers - - - parameters - - - name - self - type - integer - - - name - other - type - number - - - returnType - integer - tooltip - Integer division operator for integer and number - - - __eq - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - boolean - tooltip - Equality comparison operator - - - __lt - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - boolean - tooltip - Less than comparison operator - - - __le - - - parameters - - - name - self - type - integer - - - name - other - type - integer - - - returnType - boolean - tooltip - Less than or equal comparison operator - - - - tooltip - Integer class with arithmetic and comparison operators - - uuid - - methods - - __tostring - - - parameters - - - name - self - type - uuid - - - returnType - string - tooltip - Convert UUID to string representation - - - - properties - - istruthy - boolean - - tooltip - UUID class with string conversion and truthiness property - - quaternion - - properties - - x - number - y - number - z - number - s - number - - methods - - __mul - - - parameters - - - name - self - type - quaternion - - - name - other - type - vector - - - returnType - vector - tooltip - Multiply vector by quaternion - - - parameters - - - name - self - type - quaternion - - - name - other - type - quaternion - - - returnType - quaternion - tooltip - Multiply quaternion by quaternion - - - - tooltip - Quaternion class representing a rotation with x, y, z, s components - - vector - - properties - - x - number - y - number - z - number - - methods - - __mul - - - parameters - - - name - self - type - vector - - - name - other - type - vector - - - returnType - vector - tooltip - Multiply vector by vector - - - parameters - - - name - self - type - vector - - - name - other - type - quaternion - - - returnType - vector - tooltip - Multiply vector by quaternion - - - parameters - - - name - self - type - vector - - - name - other - type - number - - - returnType - vector - tooltip - Multiply vector by number - - - __div - - - parameters - - - name - self - type - vector - - - name - other - type - vector - - - returnType - vector - tooltip - Divide vector by vector - - - parameters - - - name - self - type - vector - - - name - other - type - quaternion - - - returnType - vector - tooltip - Divide vector by quaternion - - - parameters - - - name - self - type - vector - - - name - other - type - number - - - returnType - vector - tooltip - Divide vector by number - - - __idiv - - - parameters - - - name - self - type - vector - - - name - other - type - vector - - - returnType - vector - tooltip - Floor divide vector by vector - - - parameters - - - name - self - type - vector - - - name - other - type - quaternion - - - returnType - vector - tooltip - Floor divide vector by quaternion - - - parameters - - - name - self - type - vector - - - name - other - type - number - - - returnType - vector - tooltip - Floor divide vector by number - - - __add - - - parameters - - - name - self - type - vector - - - name - other - type - vector - - - returnType - vector - tooltip - Add two vectors - - - __sub - - - parameters - - - name - self - type - vector - - - name - other - type - vector - - - returnType - vector - tooltip - Subtract vector from vector - - - __unm - - - parameters - - - name - self - type - vector - - - returnType - vector - tooltip - Negate a vector - - - - tooltip - Vector class representing a 3D vector with x, y, z components - - DetectedEvent - - properties - - index - number - valid - boolean - canAdjustDamage - boolean - - methods - - adjustDamage - - - parameters - - - name - self - type - DetectedEvent - - - name - damage - type - number - - - returnType - () - tooltip - Adjusts the damage amount for this detected event - - - getOwner - - - parameters - - - name - self - type - DetectedEvent - - - returnType - any - tooltip - Gets the owner of the detected object - - - getName - - - parameters - - - name - self - type - DetectedEvent - - - returnType - any - tooltip - Gets the name of the detected object - - - getFace - - - parameters - - - name - self - type - DetectedEvent - - - returnType - any - tooltip - Gets the face number of the detected collision/touch - - - getLinkNumber - - - parameters - - - name - self - type - DetectedEvent - - - returnType - any - tooltip - Gets the link number of the detected object - - - getKey - - - parameters - - - name - self - type - DetectedEvent - - - returnType - any - tooltip - Gets the key/UUID of the detected object - - - new - - - parameters - - - name - self - type - DetectedEvent - - - name - index - type - number - - - name - canAdjustDamage - type - boolean - - - returnType - DetectedEvent - tooltip - Creates a new DetectedEvent instance - - - - tooltip - DetectedEvent class for handling multi-event handlers like touch, collision, sensor events - - LLEvents - - properties - - _handlers - { [string]: { EventHandler } } - - methods - - on - - - parameters - - - name - self - type - LLEvents - - - name - event_name - type - string - - - name - handler - type - EventHandler - - - returnType - EventHandler - tooltip - Registers a persistent event handler for the specified event - - - once - - - parameters - - - name - self - type - LLEvents - - - name - event_name - type - string - - - name - handler - type - EventHandler - - - returnType - EventHandler - tooltip - Registers a one-time event handler that will be automatically removed after first execution - - - off - - - parameters - - - name - self - type - LLEvents - - - name - event_name - type - string - - - name - handler - type - EventHandler - optional - 1 - - - returnType - boolean - tooltip - Removes an event handler and returns true if successful - - - listeners - - - parameters - - - name - self - type - LLEvents - - - name - event_name - type - string - - - returnType - { EventHandler } - tooltip - Returns a copy of all handlers registered for the specified event - - - eventNames - - - parameters - - - name - self - type - LLEvents - - - returnType - { string } - tooltip - Returns an array of all event names that have registered handlers - - - - tooltip - LLEvents interface providing dynamic event handler management for Second Life scripts - - LLTimers - - properties - - _timers - { _LLTimerData } - - methods - - on - - - parameters - - - name - self - type - LLTimers - - - name - seconds - type - number - - - name - handler - type - EventHandler - - - returnType - EventHandler - tooltip - Registers a repeating timer that will call the handler every specified number of seconds - - - once - - - parameters - - - name - self - type - LLTimers - - - name - seconds - type - number - - - name - handler - type - EventHandler - - - returnType - EventHandler - tooltip - Registers a one-shot timer that will call the handler once after the specified delay - - - off - - - parameters - - - name - self - type - LLTimers - - - name - handler - type - EventHandler - optional - 1 - - - returnType - boolean - tooltip - Removes a timer handler and returns true if successful - - - - tooltip - LLTimers interface providing timer management for Second Life scripts - - _LLTimerData - - properties - - handler - EventHandler - nextRun - number - interval - number | nil - - tooltip - Internal timer data structure containing handler function and timing information - - - modules - - bit32 - - methods - - arshift - - - parameters - - - name - n - type - integer - - - name - i - type - integer - - - returnType - integer - tooltip - Arithmetic right shift for integers - - - parameters - - - name - n - type - integer | number - - - name - i - type - integer | number - - - returnType - number - tooltip - Arithmetic right shift for mixed types - - - band - - - parameters - - - name - args - type - integer - variadic - 1 - - - returnType - integer - tooltip - Bitwise AND for integers - - - parameters - - - name - args - type - integer | number - variadic - 1 - - - returnType - number - tooltip - Bitwise AND for mixed types - - - bnot - - - parameters - - - name - n - type - integer - - - returnType - integer - tooltip - Bitwise NOT for integers - - - parameters - - - name - n - type - integer | number - - - returnType - number - tooltip - Bitwise NOT for mixed types - - - bor - - - parameters - - - name - args - type - integer - variadic - 1 - - - returnType - integer - tooltip - Bitwise OR for integers - - - parameters - - - name - args - type - integer | number - variadic - 1 - - - returnType - number - tooltip - Bitwise OR for mixed types - - - bxor - - - parameters - - - name - args - type - integer - variadic - 1 - - - returnType - integer - tooltip - Bitwise XOR for integers - - - parameters - - - name - args - type - integer | number - variadic - 1 - - - returnType - number - tooltip - Bitwise XOR for mixed types - - - btest - - - parameters - - - name - args - type - integer | number - variadic - 1 - - - returnType - boolean - tooltip - Test if bitwise AND is non-zero - - - extract - - - parameters - - - name - n - type - integer - - - name - f - type - integer - - - name - w - type - integer - - - returnType - integer - tooltip - Extract bits from integer - - - parameters - - - name - n - type - integer | number - - - name - f - type - integer | number - - - name - w - type - integer | number - - - returnType - number - tooltip - Extract bits from mixed types - - - lrotate - - - parameters - - - name - n - type - integer - - - name - i - type - integer - - - returnType - integer - tooltip - Left rotate for integers - - - parameters - - - name - n - type - integer | number - - - name - i - type - integer | number - - - returnType - number - tooltip - Left rotate for mixed types - - - lshift - - - parameters - - - name - n - type - integer - - - name - i - type - integer - - - returnType - integer - tooltip - Left shift for integers - - - parameters - - - name - n - type - integer | number - - - name - i - type - integer | number - - - returnType - number - tooltip - Left shift for mixed types - - - replace - - - parameters - - - name - n - type - integer - - - name - r - type - integer - - - name - f - type - integer - - - name - w - type - integer - optional - 1 - - - returnType - integer - tooltip - Replace bits in integer - - - parameters - - - name - n - type - integer | number - - - name - r - type - integer | number - - - name - f - type - integer | number - - - name - w - type - integer | number - optional - 1 - - - returnType - number - tooltip - Replace bits in mixed types - - - rrotate - - - parameters - - - name - n - type - integer - - - name - i - type - integer - - - returnType - integer - tooltip - Right rotate for integers - - - parameters - - - name - n - type - integer | number - - - name - i - type - integer | number - - - returnType - number - tooltip - Right rotate for mixed types - - - rshift - - - parameters - - - name - n - type - integer - - - name - i - type - integer - - - returnType - integer - tooltip - Right shift for integers - - - parameters - - - name - n - type - integer | number - - - name - i - type - integer | number - - - returnType - number - tooltip - Right shift for mixed types - - - countlz - - - parameters - - - name - n - type - integer - - - returnType - integer - tooltip - Count leading zeros for integers - - - parameters - - - name - n - type - number - - - returnType - integer - tooltip - Count leading zeros for numbers - - - countrz - - - parameters - - - name - n - type - integer - - - returnType - integer - tooltip - Count trailing zeros for integers - - - parameters - - - name - n - type - number - - - returnType - integer - tooltip - Count trailing zeros for numbers - - - byteswap - - - parameters - - - name - n - type - integer - - - returnType - integer - tooltip - Byte swap for integers - - - parameters - - - name - n - type - number - - - returnType - integer - tooltip - Byte swap for numbers - - - - tooltip - Bitwise operations library providing bit manipulation functions - - vector - - properties - - one - vector - zero - vector - - methods - - abs - - - parameters - - - name - vec - type - vector - - - returnType - vector - tooltip - Applies math.abs to every component of the input vector - - - angle - - - parameters - - - name - vec1 - type - vector - - - name - vec2 - type - vector - - - name - axis - type - vector - optional - 1 - - - returnType - number - tooltip - Computes the angle between two vectors in radians. The axis, if specified, is used to determine the sign of the angle - - - ceil - - - parameters - - - name - vec - type - vector - - - returnType - vector - tooltip - Applies math.ceil to every component of the input vector - - - clamp - - - parameters - - - name - vec - type - vector - - - name - min - type - vector - - - name - max - type - vector - - - returnType - vector - tooltip - Applies math.clamp to every component of the input vector - - - create - - - parameters - - - name - x - type - number | integer - - - name - y - type - number | integer - - - name - z - type - number | integer - - - returnType - vector - tooltip - Creates a new vector with the given component values - - - cross - - - parameters - - - name - vec1 - type - vector - - - name - vec2 - type - vector - - - returnType - vector - tooltip - Computes the cross product of two vectors - - - dot - - - parameters - - - name - vec1 - type - vector - - - name - vec2 - type - vector - - - returnType - number - tooltip - Computes the dot product of two vectors - - - floor - - - parameters - - - name - vec - type - vector - - - returnType - vector - tooltip - Applies math.floor to every component of the input vector - - - magnitude - - - parameters - - - name - vec - type - vector - - - returnType - number - tooltip - Calculates the magnitude of a given vector - - - max - - - parameters - - - name - args - type - vector - variadic - 1 - - - returnType - vector - tooltip - Applies math.max to the corresponding components of the input vectors - - - min - - - parameters - - - name - args - type - vector - variadic - 1 - - - returnType - vector - tooltip - Applies math.min to the corresponding components of the input vectors - - - normalize - - - parameters - - - name - vec - type - vector - - - returnType - vector - tooltip - Computes the normalized version (unit vector) of a given vector - - - sign - - - parameters - - - name - vec - type - vector - - - returnType - vector - tooltip - Applies math.sign to every component of the input vector - - - - tooltip - Vector utility functions for operations on vectors - - lljson - - properties - - empty_array - lljson_constant - null - lljson_constant - - methods - - decode - - - parameters - - - name - json - type - string - - - returnType - string | number | integer | vector | uuid | quaternion | boolean | table | nil - tooltip - Decode JSON string to Lua value - - - encode - - - parameters - - - name - value - type - string | number | integer | vector | uuid | quaternion | boolean | table | nil - - - returnType - string - tooltip - Encode Lua value as JSON - - - - tooltip - JSON encoding and decoding module with constants for null and empty arrays - - llbase64 - - methods - - decode - - - parameters - - - name - base64 - type - string - - - returnType - string - tooltip - Decode a base64 string to a string - - - parameters - - - name - base64 - type - string - - - name - asBuffer - type - true - - - returnType - buffer - tooltip - Decode a base64 string to a buffer - - - parameters - - - name - base64 - type - string - - - name - asBuffer - type - false - - - returnType - string - tooltip - Decode a base64 string to a string - - - encode - - - parameters - - - name - value - type - string | buffer - - - returnType - string - tooltip - Encode a string or buffer to base64 - - - - tooltip - Base64 encoding and decoding module for strings and buffers - - - - diff --git a/indra/newview/app_settings/types_lua_default.xml b/indra/newview/app_settings/types_lua_default.xml new file mode 100644 index 0000000000..5881df5ba5 --- /dev/null +++ b/indra/newview/app_settings/types_lua_default.xml @@ -0,0 +1,2488 @@ + + + + $schema + ./sl-lua-defs.schema.json + aliases + + list + + aliasTypes + + string + number + integer + vector + uuid + quaternion + boolean + + aliasIndex + number + tooltip + A table with numeric indices containing various value types + + table + + aliasTypes + + any + + aliasIndex + any + tooltip + A table with any type of indices and values + + numeric + + aliasTypes + + number + boolean + integer + + tooltip + A union type representing numeric values + + lljson_constant + + aliasTypes + + number + + tooltip + A constant value used in lljson module + + uuid_like + + aliasTypes + + string + uuid + + tooltip + A union type representing uuid values + + EventHandler + + aliasTypes + + (...any) -> () + + tooltip + A function type for event handlers that accepts variable arguments + + + functions + + toquaternion + + parameters + + + name + str + type + string + + + returnType + quaternion | nil + tooltip + Creates a quaternion from a string argument in format `<1,1,1,1>`. Invalid strings will return `nil`. Due to an old error from LSL, strings that match up to the closing `>` are interpreted as valid. So `<1,1,1,1` and `<1,1,1,1spoon` are both cast to `<1,1,1,1>`. When testing if a string is a quaternion or a vector, you should test with `toquaternion` first. + + tovector + + parameters + + + name + str + type + string + + + returnType + vector | nil + tooltip + Creates a vector from a string argument in format `<1,1,1>`. Invalid strings will return `nil`. Due to an old error from LSL, strings that match up to the closing `>` are interpreted as valid. So `<1,1,1`, `<1,1,1,1` and `<1,1,1spoon` are all cast to `<1,1,1>`. When testing if a string is a quaternion or a vector, you should test with `toquaternion` first. + + integer + + parameters + + + name + value + type + string | number + + + returnType + integer + tooltip + Creates an integer value from argument + + uuid + + parameters + + + name + str + type + string + + + returnType + uuid + tooltip + Creates a uuid from a string argument + + vector + + parameters + + + name + x + type + number + + + name + y + type + number + + + name + z + type + number + + + returnType + vector + tooltip + Creates a vector. + + quaternion + + parameters + + + name + x + type + number + + + name + y + type + number + + + name + z + type + number + + + name + s + type + number + + + returnType + quaternion + tooltip + Creates a quaternion rotation. + + + classes + + integer + + methods + + __add + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + integer + tooltip + Addition operator for integers + + + parameters + + + name + self + type + integer + + + name + other + type + number + + + returnType + number + tooltip + Addition operator for integer and number + + + __sub + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + integer + tooltip + Subtraction operator for integers + + + parameters + + + name + self + type + integer + + + name + other + type + number + + + returnType + number + tooltip + Subtraction operator for integer and number + + + __mul + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + integer + tooltip + Multiplication operator for integers + + + parameters + + + name + self + type + integer + + + name + other + type + number + + + returnType + number + tooltip + Multiplication operator for integer and number + + + __div + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + integer + tooltip + Division operator for integers + + + parameters + + + name + self + type + integer + + + name + other + type + number + + + returnType + number + tooltip + Division operator for integer and number + + + __unm + + + parameters + + + name + self + type + integer + + + returnType + integer + tooltip + Unary minus operator + + + __mod + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + integer + tooltip + Modulo operator for integers + + + parameters + + + name + self + type + integer + + + name + other + type + number + + + returnType + number + tooltip + Modulo operator for integer and number + + + __pow + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + integer + tooltip + Power operator for integers + + + parameters + + + name + self + type + integer + + + name + other + type + number + + + returnType + number + tooltip + Power operator for integer and number + + + __idiv + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + integer + tooltip + Integer division operator for integers + + + parameters + + + name + self + type + integer + + + name + other + type + number + + + returnType + integer + tooltip + Integer division operator for integer and number + + + __eq + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + boolean + tooltip + Equality comparison operator + + + __lt + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + boolean + tooltip + Less than comparison operator + + + __le + + + parameters + + + name + self + type + integer + + + name + other + type + integer + + + returnType + boolean + tooltip + Less than or equal comparison operator + + + + tooltip + Integer class with arithmetic and comparison operators + + uuid + + methods + + __tostring + + + parameters + + + name + self + type + uuid + + + returnType + string + tooltip + Convert UUID to string representation + + + + properties + + istruthy + boolean + + tooltip + UUID class with string conversion and truthiness property + + quaternion + + properties + + x + number + y + number + z + number + s + number + + methods + + __mul + + + parameters + + + name + self + type + quaternion + + + name + other + type + vector + + + returnType + vector + tooltip + Multiply vector by quaternion + + + parameters + + + name + self + type + quaternion + + + name + other + type + quaternion + + + returnType + quaternion + tooltip + Multiply quaternion by quaternion + + + + tooltip + Quaternion class representing a rotation with x, y, z, s components + + vector + + properties + + x + number + y + number + z + number + + methods + + __mul + + + parameters + + + name + self + type + vector + + + name + other + type + vector + + + returnType + vector + tooltip + Multiply vector by vector + + + parameters + + + name + self + type + vector + + + name + other + type + quaternion + + + returnType + vector + tooltip + Multiply vector by quaternion + + + parameters + + + name + self + type + vector + + + name + other + type + number + + + returnType + vector + tooltip + Multiply vector by number + + + __div + + + parameters + + + name + self + type + vector + + + name + other + type + vector + + + returnType + vector + tooltip + Divide vector by vector + + + parameters + + + name + self + type + vector + + + name + other + type + quaternion + + + returnType + vector + tooltip + Divide vector by quaternion + + + parameters + + + name + self + type + vector + + + name + other + type + number + + + returnType + vector + tooltip + Divide vector by number + + + __idiv + + + parameters + + + name + self + type + vector + + + name + other + type + vector + + + returnType + vector + tooltip + Floor divide vector by vector + + + parameters + + + name + self + type + vector + + + name + other + type + quaternion + + + returnType + vector + tooltip + Floor divide vector by quaternion + + + parameters + + + name + self + type + vector + + + name + other + type + number + + + returnType + vector + tooltip + Floor divide vector by number + + + __add + + + parameters + + + name + self + type + vector + + + name + other + type + vector + + + returnType + vector + tooltip + Add two vectors + + + __sub + + + parameters + + + name + self + type + vector + + + name + other + type + vector + + + returnType + vector + tooltip + Subtract vector from vector + + + __unm + + + parameters + + + name + self + type + vector + + + returnType + vector + tooltip + Negate a vector + + + + tooltip + Vector class representing a 3D vector with x, y, z components + + DetectedEvent + + properties + + index + number + valid + boolean + canAdjustDamage + boolean + + methods + + adjustDamage + + + parameters + + + name + self + type + DetectedEvent + + + name + damage + type + number + + + returnType + () + tooltip + Adjusts the damage amount for this detected event + + + getOwner + + + parameters + + + name + self + type + DetectedEvent + + + returnType + any + tooltip + Gets the owner of the detected object + + + getName + + + parameters + + + name + self + type + DetectedEvent + + + returnType + any + tooltip + Gets the name of the detected object + + + getFace + + + parameters + + + name + self + type + DetectedEvent + + + returnType + any + tooltip + Gets the face number of the detected collision/touch + + + getLinkNumber + + + parameters + + + name + self + type + DetectedEvent + + + returnType + any + tooltip + Gets the link number of the detected object + + + getKey + + + parameters + + + name + self + type + DetectedEvent + + + returnType + any + tooltip + Gets the key/UUID of the detected object + + + new + + + parameters + + + name + self + type + DetectedEvent + + + name + index + type + number + + + name + canAdjustDamage + type + boolean + + + returnType + DetectedEvent + tooltip + Creates a new DetectedEvent instance + + + + tooltip + DetectedEvent class for handling multi-event handlers like touch, collision, sensor events + + LLEvents + + properties + + _handlers + { [string]: { EventHandler } } + + methods + + on + + + parameters + + + name + self + type + LLEvents + + + name + event_name + type + string + + + name + handler + type + EventHandler + + + returnType + EventHandler + tooltip + Registers a persistent event handler for the specified event + + + once + + + parameters + + + name + self + type + LLEvents + + + name + event_name + type + string + + + name + handler + type + EventHandler + + + returnType + EventHandler + tooltip + Registers a one-time event handler that will be automatically removed after first execution + + + off + + + parameters + + + name + self + type + LLEvents + + + name + event_name + type + string + + + name + handler + type + EventHandler + optional + 1 + + + returnType + boolean + tooltip + Removes an event handler and returns true if successful + + + listeners + + + parameters + + + name + self + type + LLEvents + + + name + event_name + type + string + + + returnType + { EventHandler } + tooltip + Returns a copy of all handlers registered for the specified event + + + eventNames + + + parameters + + + name + self + type + LLEvents + + + returnType + { string } + tooltip + Returns an array of all event names that have registered handlers + + + + tooltip + LLEvents interface providing dynamic event handler management for Second Life scripts + + LLTimers + + properties + + _timers + { _LLTimerData } + + methods + + on + + + parameters + + + name + self + type + LLTimers + + + name + seconds + type + number + + + name + handler + type + EventHandler + + + returnType + EventHandler + tooltip + Registers a repeating timer that will call the handler every specified number of seconds + + + once + + + parameters + + + name + self + type + LLTimers + + + name + seconds + type + number + + + name + handler + type + EventHandler + + + returnType + EventHandler + tooltip + Registers a one-shot timer that will call the handler once after the specified delay + + + off + + + parameters + + + name + self + type + LLTimers + + + name + handler + type + EventHandler + optional + 1 + + + returnType + boolean + tooltip + Removes a timer handler and returns true if successful + + + + tooltip + LLTimers interface providing timer management for Second Life scripts + + _LLTimerData + + properties + + handler + EventHandler + nextRun + number + interval + number | nil + + tooltip + Internal timer data structure containing handler function and timing information + + + modules + + bit32 + + methods + + arshift + + + parameters + + + name + n + type + integer + + + name + i + type + integer + + + returnType + integer + tooltip + Arithmetic right shift for integers + + + parameters + + + name + n + type + integer | number + + + name + i + type + integer | number + + + returnType + number + tooltip + Arithmetic right shift for mixed types + + + band + + + parameters + + + name + args + type + integer + variadic + 1 + + + returnType + integer + tooltip + Bitwise AND for integers + + + parameters + + + name + args + type + integer | number + variadic + 1 + + + returnType + number + tooltip + Bitwise AND for mixed types + + + bnot + + + parameters + + + name + n + type + integer + + + returnType + integer + tooltip + Bitwise NOT for integers + + + parameters + + + name + n + type + integer | number + + + returnType + number + tooltip + Bitwise NOT for mixed types + + + bor + + + parameters + + + name + args + type + integer + variadic + 1 + + + returnType + integer + tooltip + Bitwise OR for integers + + + parameters + + + name + args + type + integer | number + variadic + 1 + + + returnType + number + tooltip + Bitwise OR for mixed types + + + bxor + + + parameters + + + name + args + type + integer + variadic + 1 + + + returnType + integer + tooltip + Bitwise XOR for integers + + + parameters + + + name + args + type + integer | number + variadic + 1 + + + returnType + number + tooltip + Bitwise XOR for mixed types + + + btest + + + parameters + + + name + args + type + integer | number + variadic + 1 + + + returnType + boolean + tooltip + Test if bitwise AND is non-zero + + + extract + + + parameters + + + name + n + type + integer + + + name + f + type + integer + + + name + w + type + integer + + + returnType + integer + tooltip + Extract bits from integer + + + parameters + + + name + n + type + integer | number + + + name + f + type + integer | number + + + name + w + type + integer | number + + + returnType + number + tooltip + Extract bits from mixed types + + + lrotate + + + parameters + + + name + n + type + integer + + + name + i + type + integer + + + returnType + integer + tooltip + Left rotate for integers + + + parameters + + + name + n + type + integer | number + + + name + i + type + integer | number + + + returnType + number + tooltip + Left rotate for mixed types + + + lshift + + + parameters + + + name + n + type + integer + + + name + i + type + integer + + + returnType + integer + tooltip + Left shift for integers + + + parameters + + + name + n + type + integer | number + + + name + i + type + integer | number + + + returnType + number + tooltip + Left shift for mixed types + + + replace + + + parameters + + + name + n + type + integer + + + name + r + type + integer + + + name + f + type + integer + + + name + w + type + integer + optional + 1 + + + returnType + integer + tooltip + Replace bits in integer + + + parameters + + + name + n + type + integer | number + + + name + r + type + integer | number + + + name + f + type + integer | number + + + name + w + type + integer | number + optional + 1 + + + returnType + number + tooltip + Replace bits in mixed types + + + rrotate + + + parameters + + + name + n + type + integer + + + name + i + type + integer + + + returnType + integer + tooltip + Right rotate for integers + + + parameters + + + name + n + type + integer | number + + + name + i + type + integer | number + + + returnType + number + tooltip + Right rotate for mixed types + + + rshift + + + parameters + + + name + n + type + integer + + + name + i + type + integer + + + returnType + integer + tooltip + Right shift for integers + + + parameters + + + name + n + type + integer | number + + + name + i + type + integer | number + + + returnType + number + tooltip + Right shift for mixed types + + + countlz + + + parameters + + + name + n + type + integer + + + returnType + integer + tooltip + Count leading zeros for integers + + + parameters + + + name + n + type + number + + + returnType + integer + tooltip + Count leading zeros for numbers + + + countrz + + + parameters + + + name + n + type + integer + + + returnType + integer + tooltip + Count trailing zeros for integers + + + parameters + + + name + n + type + number + + + returnType + integer + tooltip + Count trailing zeros for numbers + + + byteswap + + + parameters + + + name + n + type + integer + + + returnType + integer + tooltip + Byte swap for integers + + + parameters + + + name + n + type + number + + + returnType + integer + tooltip + Byte swap for numbers + + + + tooltip + Bitwise operations library providing bit manipulation functions + + vector + + properties + + one + vector + zero + vector + + methods + + abs + + + parameters + + + name + vec + type + vector + + + returnType + vector + tooltip + Applies math.abs to every component of the input vector + + + angle + + + parameters + + + name + vec1 + type + vector + + + name + vec2 + type + vector + + + name + axis + type + vector + optional + 1 + + + returnType + number + tooltip + Computes the angle between two vectors in radians. The axis, if specified, is used to determine the sign of the angle + + + ceil + + + parameters + + + name + vec + type + vector + + + returnType + vector + tooltip + Applies math.ceil to every component of the input vector + + + clamp + + + parameters + + + name + vec + type + vector + + + name + min + type + vector + + + name + max + type + vector + + + returnType + vector + tooltip + Applies math.clamp to every component of the input vector + + + create + + + parameters + + + name + x + type + number | integer + + + name + y + type + number | integer + + + name + z + type + number | integer + + + returnType + vector + tooltip + Creates a new vector with the given component values + + + cross + + + parameters + + + name + vec1 + type + vector + + + name + vec2 + type + vector + + + returnType + vector + tooltip + Computes the cross product of two vectors + + + dot + + + parameters + + + name + vec1 + type + vector + + + name + vec2 + type + vector + + + returnType + number + tooltip + Computes the dot product of two vectors + + + floor + + + parameters + + + name + vec + type + vector + + + returnType + vector + tooltip + Applies math.floor to every component of the input vector + + + magnitude + + + parameters + + + name + vec + type + vector + + + returnType + number + tooltip + Calculates the magnitude of a given vector + + + max + + + parameters + + + name + args + type + vector + variadic + 1 + + + returnType + vector + tooltip + Applies math.max to the corresponding components of the input vectors + + + min + + + parameters + + + name + args + type + vector + variadic + 1 + + + returnType + vector + tooltip + Applies math.min to the corresponding components of the input vectors + + + normalize + + + parameters + + + name + vec + type + vector + + + returnType + vector + tooltip + Computes the normalized version (unit vector) of a given vector + + + sign + + + parameters + + + name + vec + type + vector + + + returnType + vector + tooltip + Applies math.sign to every component of the input vector + + + + tooltip + Vector utility functions for operations on vectors + + lljson + + properties + + empty_array + lljson_constant + null + lljson_constant + + methods + + decode + + + parameters + + + name + json + type + string + + + returnType + string | number | integer | vector | uuid | quaternion | boolean | table | nil + tooltip + Decode JSON string to Lua value + + + encode + + + parameters + + + name + value + type + string | number | integer | vector | uuid | quaternion | boolean | table | nil + + + returnType + string + tooltip + Encode Lua value as JSON + + + + tooltip + JSON encoding and decoding module with constants for null and empty arrays + + llbase64 + + methods + + decode + + + parameters + + + name + base64 + type + string + + + returnType + string + tooltip + Decode a base64 string to a string + + + parameters + + + name + base64 + type + string + + + name + asBuffer + type + true + + + returnType + buffer + tooltip + Decode a base64 string to a buffer + + + parameters + + + name + base64 + type + string + + + name + asBuffer + type + false + + + returnType + string + tooltip + Decode a base64 string to a string + + + encode + + + parameters + + + name + value + type + string | buffer + + + returnType + string + tooltip + Encode a string or buffer to base64 + + + + tooltip + Base64 encoding and decoding module for strings and buffers + + + + diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp index cecac23383..814157e86c 100644 --- a/indra/newview/llscripteditorws.cpp +++ b/indra/newview/llscripteditorws.cpp @@ -334,12 +334,12 @@ LLSD LLScriptEditorWSServer::handleSyntaxRequest(const LLSD& params) const if (category == "defs.lua") { response["defs"] = LLSyntaxLua::instance().getTypesXML(); - response["success"] = true; + response["success"] = response["defs"].isDefined(); } else if (category == "defs.lsl") { response["defs"] = LLSyntaxIdLSL::instance().getKeywordsXML(); - response["success"] = true; + response["success"] = response["defs"].isDefined(); } else { diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index a891bf945e..bca3f0abc8 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -364,7 +364,7 @@ void LLSyntaxLua::loadDefaultKeywordsIntoLLSD() void LLSyntaxLua::loadLuaTypesIntoLLSD() { - std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "types_lua_default.llsd"); + std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "types_lua_default.xml"); llifstream file(fullFileSpec.c_str()); if (file.good()) -- cgit v1.3 From a0a59882c19f48d62459bf03ad28dfb7975e0dd0 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 6 Feb 2026 14:22:13 -0800 Subject: fetchKeywordsFileCoro would invoke loadKeywordsIntoLLSD as part of the coroutine. This would trigger an attempt to get a mutex which will fail in a coro. Instead post the cleanup back to the mainthread. --- indra/newview/llsyntaxid.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index d9b2726992..16274f59bc 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -148,9 +148,14 @@ void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) if (isSupportedVersion(result)) { - setKeywordsXml(result); - cacheFile(fileSpec, result); - loadKeywordsIntoLLSD(); + // Shuttle this task to the main coro/worker. + // loadKeywordsIntoLLSD will attempt to get a mutex which is not coro aware. + LLAppViewer::instance()->postToMainCoro([this, result, fileSpec]() + { + setKeywordsXml(result); + cacheFile(fileSpec, result); + loadKeywordsIntoLLSD(); + }); } else { -- cgit v1.3 From 364ff6e6d45087653a302b96ccd8c9e02db2b9d1 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 28 Apr 2026 10:23:44 -0700 Subject: Refactor the LLSyntaxIDLSL/Lua into single LLSyntaxDefCache, use new cap when available to retrieve all language definition files and cache them locally. Retrieve the default definition files from the LSL definitions precompiled library. --- indra/llmessage/llcorehttputil.cpp | 33 +- indra/llmessage/llcorehttputil.h | 1 - indra/newview/app_settings/types_lua_default.xml | 5262 ---------------------- indra/newview/llpreviewscript.cpp | 5 +- indra/newview/llscripteditor.cpp | 4 +- indra/newview/llscripteditorws.cpp | 10 +- indra/newview/llsyntaxid.cpp | 517 ++- indra/newview/llsyntaxid.h | 127 +- indra/newview/llviewerregion.cpp | 1 + indra/newview/viewer_manifest.py | 12 +- 10 files changed, 438 insertions(+), 5534 deletions(-) delete mode 100644 indra/newview/app_settings/types_lua_default.xml (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index b24e5e4fcc..f18af1f18e 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -39,7 +39,7 @@ #include "llfilesystem.h" #include "message.h" // for getting the port - +#include "llstl.h" using namespace LLCore; @@ -112,15 +112,17 @@ bool responseToLLSD(HttpResponse * response, bool log, LLSD & out_llsd) { // Convert response to LLSD BufferArray * body(response->getBody()); - if (!body || !body->size()) + size_t body_size(body ? body->size() : 0); + if (!body || !body_size) { return false; } LLCore::BufferArrayStream bas(body); LLSD body_llsd; - S32 parse_status(LLSDSerialize::fromXML(body_llsd, bas, log)); - if (LLSDParser::PARSE_FAILURE == parse_status){ + + if (!LLSDSerialize::deserialize(body_llsd, bas, body_size)) + { return false; } out_llsd = body_llsd; @@ -409,28 +411,6 @@ LLSD HttpCoroLLSDHandler::handleSuccess(LLCore::HttpResponse * response, LLCore: result = parseBody(response, success); -#if 0 - bool parsed = !((response->getBodySize() == 0) || - !LLCoreHttpUtil::responseToLLSD(response, emit_parse_errors, result)); - - if (!parsed) - { - // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' - LLCore::HttpHeaders::ptr_t headers(response->getHeaders()); - const std::string *contentType = (headers) ? headers->find(HTTP_IN_HEADER_CONTENT_TYPE) : NULL; - - if (contentType && (HTTP_CONTENT_LLSD_XML == *contentType)) - { - std::string thebody = LLCoreHttpUtil::responseToString(response); - LL_WARNS("CoreHTTP") << "Failed to deserialize . " << response->getRequestURL() << " [status:" << response->getStatus().toString() << "] " - << " body: " << thebody << LL_ENDL; - - // Replace the status with a new one indicating the failure. - status = LLCore::HttpStatus(499, "Failed to deserialize LLSD."); - } - } -#endif - if (!success) { #if 1 @@ -955,7 +935,6 @@ LLSD HttpCoroutineAdapter::getJsonAndSuspend(LLCore::HttpRequest::ptr_t request, return getAndSuspend_(request, url, options, headers, httpHandler); } - LLSD HttpCoroutineAdapter::getAndSuspend_(LLCore::HttpRequest::ptr_t &request, const std::string & url, LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h index 3dbfd6f00d..8fa638fd6e 100644 --- a/indra/llmessage/llcorehttputil.h +++ b/indra/llmessage/llcorehttputil.h @@ -483,7 +483,6 @@ public: headers); } - /// Execute a DELETE transaction on the supplied URL and yield execution of /// the coroutine until a result is available. /// diff --git a/indra/newview/app_settings/types_lua_default.xml b/indra/newview/app_settings/types_lua_default.xml deleted file mode 100644 index cb40ec3d06..0000000000 --- a/indra/newview/app_settings/types_lua_default.xml +++ /dev/null @@ -1,5262 +0,0 @@ - - - - version - 1.0.0 - typeAliases - - - name - rotation - definition - - kind - reference - name - quaternion - - comment - 'rotation' is an alias for 'quaternion' - - - name - numeric - definition - - kind - union - types - - boolean - number - - - - - name - list - definition - - kind - array - elementType - - kind - union - types - - string - number - vector - uuid - quaternion - boolean - - - - - - name - LLDetectedEventName - definition - - kind - literal-union - values - - - - - name - LLNonDetectedEventName - definition - - kind - literal-union - values - - - - - name - LLEventName - definition - - kind - union - types - - LLDetectedEventName - LLNonDetectedEventName - - - - - name - LLEventHandler - definition - - kind - function - parameters - - - type - any - variadic - true - - - returnType - () - - - - name - LLDetectedEventHandler - definition - - kind - function - parameters - - - name - detected - type - - kind - array - elementType - LLDetectedEvent - - - - returnType - () - - - - name - LLTimerEveryCallback - definition - - kind - function - parameters - - - name - scheduled - type - number - - - name - interval - type - number - - - returnType - () - - comment - Callback type for LLTimers.every() - receives scheduled time and interval - - - name - LLTimerOnceCallback - definition - - kind - function - parameters - - - name - scheduled - type - number - - - returnType - () - - comment - Callback type for LLTimers.once() - receives scheduled time - - - name - LLTimerCallback - definition - - kind - union - types - - LLTimerEveryCallback - LLTimerOnceCallback - (...any) -> ...any - - - comment - Union of timer callback types - - - name - OsDateTime - definition - - kind - table - properties - - - name - year - type - number - - - name - month - type - number - - - name - day - type - number - - - name - hour - type - number - optional - true - - - name - min - type - number - optional - true - - - name - sec - type - number - optional - true - - - name - wday - type - number - optional - true - - - name - yday - type - number - optional - true - - - name - isdst - type - boolean - optional - true - - - - comment - Date/time table structure used by os.date and os.time - - - classes - - - name - quaternion - comment - Luau Type Definition for LSL UserData Type: quaternion - properties - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - name - s - type - number - - - methods - - - name - __add - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __sub - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __mul - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __div - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __unm - parameters - - - name - self - - - returnType - quaternion - - - name - __eq - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - boolean - - - name - __tostring - parameters - - - name - self - - - returnType - string - - - - - name - uuid - comment - Luau Type Definition for LSL UserData Type: uuid - properties - - - name - istruthy - type - boolean - comment - Returns true if the UUID is not the null UUID (all zeros) - - - name - bytes - type - string? - comment - Returns the raw 16-byte binary string of the UUID, or nil if the UUID is not in a compressed state - - - methods - - - name - __tostring - parameters - - - name - self - - - returnType - string - - - - - name - vector - comment - Luau Type Definition for LSL UserData Type: vector - properties - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - methods - - - name - __add - parameters - - - name - self - - - name - other - type - vector - - - returnType - vector - comment - Native component-wise addition - - - name - __sub - parameters - - - name - self - - - name - other - type - vector - - - returnType - vector - comment - Native component-wise subtraction - - - name - __unm - parameters - - - name - self - - - returnType - vector - comment - Unary negation - - - name - __mul - parameters - - - name - self - - - name - other - type - number | vector | quaternion - - - returnType - vector - comment - Multiplication: vector * number or vector * vector -> vector, vector * quaternion -> vector (Rotation) - - - name - __div - parameters - - - name - self - - - name - other - type - number | vector | quaternion - - - returnType - vector - comment - Division: vector / number or vector / vector -> vector (Scale), vector / quaternion -> vector (Rotation by inverse) - - - name - __mod - parameters - - - name - self - - - name - other - type - vector - - - returnType - vector - comment - LSL-style modulo: vector % vector -> vector (Cross Product) - - - name - __tostring - parameters - - - name - self - - - returnType - string - - - - - name - LLDetectedEvent - comment - Event detection class providing access to detected object/avatar information - properties - - - name - index - type - number - - - name - valid - type - boolean - - - name - canAdjustDamage - type - boolean - - - methods - - - - name - LLEvents - comment - Event registration and management class for Second Life events - methods - - - name - on - parameters - - - name - self - - - name - event - type - LLEventName - - - name - callback - type - LLEventHandler - - - returnType - LLEventHandler - comment - Registers a callback for an event. Returns the callback. - - - name - off - parameters - - - name - self - - - name - event - type - LLEventName - - - name - callback - type - LLEventHandler - - - returnType - boolean - comment - Unregisters a callback. Returns true if found and removed. - - - name - once - parameters - - - name - self - - - name - event - type - LLEventName - - - name - callback - type - LLEventHandler - - - returnType - LLEventHandler - comment - Registers a one-time callback. Returns the wrapper function. - - - name - listeners - parameters - - - name - self - - - name - event - type - LLEventName - - - returnType - {LLEventHandler} - comment - Returns a list of all listeners for a specific event. - - - name - eventNames - parameters - - - name - self - - - returnType - {string} - comment - Returns a list of all event names that have listeners. - - - - - name - LLTimers - comment - Timer management class for scheduling periodic and one-time callbacks - methods - - - name - every - parameters - - - name - self - - - name - seconds - type - number - - - name - callback - type - LLTimerEveryCallback - - - returnType - LLTimerCallback - comment - Registers a callback to be called every N seconds. Returns the callback. - - - name - once - parameters - - - name - self - - - name - seconds - type - number - - - name - callback - type - LLTimerOnceCallback - - - returnType - LLTimerCallback - comment - Registers a callback to be called once after N seconds. Returns the callback. - - - name - off - parameters - - - name - self - - - name - callback - type - LLTimerCallback - - - returnType - boolean - comment - Unregisters a timer callback. Returns true if found and removed. - - - - - globalVariables - - - name - quaternion - type - - kind - callable-table - tableType - - properties - - - name - create - type - - kind - function - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - name - s - type - number - - - returnType - quaternion - - comment - Constructor - - - name - identity - type - quaternion - comment - Identity quaternion constant - - - name - normalize - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - quaternion - - comment - Returns normalized quaternion - - - name - magnitude - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - number - - comment - Returns magnitude of quaternion - - - name - dot - type - - kind - function - parameters - - - name - a - type - quaternion - - - name - b - type - quaternion - - - returnType - number - - comment - Dot product of two quaternions - - - name - slerp - type - - kind - function - parameters - - - name - a - type - quaternion - - - name - b - type - quaternion - - - name - t - type - number - - - returnType - quaternion - - comment - Spherical linear interpolation - - - name - conjugate - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - quaternion - - comment - Returns conjugate of quaternion - - - name - tofwd - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - vector - - comment - Returns forward vector from quaternion rotation - - - name - toleft - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - vector - - comment - Returns left vector from quaternion rotation - - - name - toup - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - vector - - comment - Returns up vector from quaternion rotation - - - - callSignature - - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - name - s - type - number - - - returnType - quaternion - - - comment - Global 'quaternion' library table with callable metatable - - - name - rotation - type - - kind - typeof - target - quaternion - - comment - 'rotation' global is an alias to the 'quaternion' library - - - name - uuid - type - - kind - callable-table - tableType - - properties - - - name - create - type - - kind - function - parameters - - - name - value - type - - kind - union - types - - string - buffer - uuid - - - - - returnType - uuid? - - comment - Creates a new uuid from a string, buffer, or existing uuid - - - - callSignature - - parameters - - - name - value - type - - kind - union - types - - string - buffer - uuid - - - - - returnType - uuid? - - - comment - Global 'uuid' library table - - - name - vector - type - - kind - callable-table - tableType - - properties - - - name - create - type - - kind - function - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - optional - true - - - returnType - vector - - comment - Creates a new vector with the given component values - - - name - magnitude - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - number - - comment - Calculates the magnitude of a given vector - - - name - normalize - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Computes the normalized version (unit vector) of a given vector - - - name - cross - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - returnType - vector - - comment - Computes the cross product of two vectors - - - name - dot - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - returnType - number - - comment - Computes the dot product of two vectors - - - name - angle - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - name - axis - type - vector - optional - true - - - returnType - number - - comment - Computes the angle between two vectors in radians - - - name - floor - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.floor to every component of the input vector - - - name - ceil - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.ceil to every component of the input vector - - - name - abs - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.abs to every component of the input vector - - - name - sign - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.sign to every component of the input vector - - - name - clamp - type - - kind - function - parameters - - - name - v - type - vector - - - name - min - type - vector - - - name - max - type - vector - - - returnType - vector - - comment - Applies math.clamp to every component of the input vector - - - name - max - type - - kind - function - parameters - - - name - v - type - vector - - - type - vector - variadic - true - - - returnType - vector - - comment - Applies math.max to the corresponding components of the input vectors - - - name - min - type - - kind - function - parameters - - - name - v - type - vector - - - type - vector - variadic - true - - - returnType - vector - - comment - Applies math.min to the corresponding components of the input vectors - - - name - lerp - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - name - t - type - number - - - returnType - vector - - comment - Linear interpolation between two vectors - - - name - zero - type - vector - comment - A zero vector <0,0,0> - - - name - one - type - vector - comment - A one vector <1,1,1> - - - - callSignature - - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - optional - true - - - returnType - vector - - - comment - Global 'vector' library table - - - name - LLEvents - type - LLEvents - comment - Second Life event management and registration - - - name - LLTimers - type - LLTimers - comment - Second Life timer management and scheduling - - - name - loadstring - type - nil - comment - loadstring is removed in SLua - - - name - getfenv - type - nil - comment - getfenv is removed in SLua - - - name - setfenv - type - nil - comment - setfenv is removed in SLua - - - modules - - - name - bit32 - comment - Bitwise operations library - functions - - - name - arshift - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Arithmetic right shift - - - name - band - parameters - - - type - number - variadic - true - - - returnType - number - comment - Bitwise AND of all arguments - - - name - bnot - parameters - - - name - x - type - number - - - returnType - number - comment - Bitwise NOT - - - name - bor - parameters - - - type - number - variadic - true - - - returnType - number - comment - Bitwise OR of all arguments - - - name - bxor - parameters - - - type - number - variadic - true - - - returnType - number - comment - Bitwise XOR of all arguments - - - name - btest - parameters - - - type - number - variadic - true - - - returnType - boolean - comment - Returns true if bitwise AND of all arguments is not zero - - - name - extract - parameters - - - name - n - type - number - - - name - field - type - number - - - name - width - type - number - optional - true - - - returnType - number - comment - Extracts bits from n at position field with width - - - name - lrotate - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Left rotate - - - name - lshift - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Left shift - - - name - replace - parameters - - - name - n - type - number - - - name - v - type - number - - - name - field - type - number - - - name - width - type - number - optional - true - - - returnType - number - comment - Replaces bits in n at position field with width using value v - - - name - rrotate - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Right rotate - - - name - rshift - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Right shift - - - name - countlz - parameters - - - name - n - type - number - - - returnType - number - comment - Count leading zeros - - - name - countrz - parameters - - - name - n - type - number - - - returnType - number - comment - Count trailing zeros - - - name - byteswap - parameters - - - name - n - type - number - - - returnType - number - comment - Swap byte order - - - - - name - buffer - comment - Buffer manipulation library for binary data - functions - - - name - create - parameters - - - name - size - type - number - - - returnType - buffer - comment - Creates a new buffer of the specified size - - - name - fromstring - parameters - - - name - str - type - string - - - returnType - buffer - comment - Creates a buffer from a string - - - name - tostring - parameters - - - name - b - type - buffer - - - returnType - string - comment - Converts buffer to string - - - name - readi8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read signed 8-bit integer - - - name - readu8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read unsigned 8-bit integer - - - name - readi16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read signed 16-bit integer - - - name - readu16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read unsigned 16-bit integer - - - name - readi32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read signed 32-bit integer - - - name - readu32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read unsigned 32-bit integer - - - name - readf32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read 32-bit float - - - name - readf64 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read 64-bit float - - - name - writei8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write signed 8-bit integer - - - name - writeu8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write unsigned 8-bit integer - - - name - writei16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write signed 16-bit integer - - - name - writeu16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write unsigned 16-bit integer - - - name - writei32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write signed 32-bit integer - - - name - writeu32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write unsigned 32-bit integer - - - name - writef32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write 32-bit float - - - name - writef64 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write 64-bit float - - - name - readstring - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - count - type - number - - - returnType - string - comment - Read string from buffer - - - name - writestring - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - string - - - name - count - type - number - optional - true - - - returnType - () - comment - Write string to buffer - - - name - len - parameters - - - name - b - type - buffer - - - returnType - number - comment - Returns the length of the buffer - - - name - copy - parameters - - - name - target - type - buffer - - - name - targetOffset - type - number - - - name - source - type - buffer - - - name - sourceOffset - type - number - optional - true - - - name - count - type - number - optional - true - - - returnType - () - comment - Copy data from source buffer to target buffer - - - name - fill - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - name - count - type - number - optional - true - - - returnType - () - comment - Fill buffer with a value - - - name - readbits - parameters - - - name - b - type - buffer - - - name - bitOffset - type - number - - - name - bitCount - type - number - - - returnType - number - comment - Read bits from buffer - - - name - writebits - parameters - - - name - b - type - buffer - - - name - bitOffset - type - number - - - name - bitCount - type - number - - - name - value - type - number - - - returnType - () - comment - Write bits to buffer - - - - - name - coroutine - comment - Coroutine manipulation library - functions - - - name - create - parameters - - - name - f - type - (...any) -> ...any - - - returnType - thread - comment - Creates a new coroutine from a function - - - name - resume - parameters - - - name - co - type - thread - - - type - any - variadic - true - - - returnType - (boolean, ...any) - comment - Resumes a coroutine, returns success and results - - - name - running - parameters - - returnType - thread? - comment - Returns the running coroutine, or nil if called from main thread - - - name - status - parameters - - - name - co - type - thread - - - returnType - "running" | "suspended" | "normal" | "dead" - comment - Returns the status of a coroutine - - - name - wrap - parameters - - - name - f - type - (...any) -> ...any - - - returnType - (...any) -> ...any - comment - Creates a coroutine and returns a function that resumes it - - - name - yield - parameters - - - type - any - variadic - true - - - returnType - ...any - comment - Suspends the coroutine and returns values to resume - - - name - isyieldable - parameters - - returnType - boolean - comment - Returns true if the coroutine can yield - - - name - close - parameters - - - name - co - type - thread - - - returnType - (boolean, string?) - comment - Closes a coroutine, returns success and optional error message - - - - - name - debug - comment - Debug library for introspection - functions - - - name - info - parameters - - - name - thread - type - thread - - - name - level - type - number - - - name - options - type - string - - - returnType - ...any - comment - Returns information about a function or stack level - overloads - - - parameters - - - name - level - type - number - - - name - options - type - string - - - returnType - ...any - comment - Returns information about a stack level - - - parameters - - - name - func - type - (...any) -> ...any - - - name - options - type - string - - - returnType - ...any - comment - Returns information about a function - - - - - name - traceback - parameters - - - name - thread - type - thread - - - name - message - type - string - optional - true - - - name - level - type - number - optional - true - - - returnType - string - comment - Returns a string with a traceback of the call stack - overloads - - - parameters - - - name - message - type - string - optional - true - - - name - level - type - number - optional - true - - - returnType - string - comment - Returns a string with a traceback of the current call stack - - - - - - - name - llbase64 - comment - Base64 encoding/decoding library - functions - - - name - encode - parameters - - - name - data - type - string | buffer - - - returnType - string - comment - Encodes a string or buffer to base64 - - - name - decode - parameters - - - name - data - type - string - - - returnType - string - comment - Decodes a base64 string to a string - overloads - - - parameters - - - name - data - type - string - - - name - asBuffer - type - boolean - optional - true - - - returnType - string | buffer - comment - Decodes a base64 string to a string or buffer - - - - - - - name - lljson - comment - JSON encoding/decoding library for Second Life - properties - - - name - null - type - any - comment - A constant to pass for null to json encode - - - name - empty_array_mt - type - { [any]: any } - comment - Metatable for declaring table as an empty array for json encode - - - name - array_mt - type - { [any]: any } - comment - Metatable for declaring table as an array for json encode - - - name - empty_array - type - any - comment - A constant to pass for an empty array to json encode - - - name - _NAME - type - string - comment - Name of the lljson library - - - name - _VERSION - type - string - comment - Version of the lljson library - - - functions - - - name - encode - parameters - - - name - value - type - any - - - returnType - string - comment - Encodes a Lua value as JSON - - - name - decode - parameters - - - name - json - type - string - - - returnType - any - comment - Decodes a JSON string to a Lua value - - - name - slencode - parameters - - - name - value - type - any - - - name - tight - type - boolean - optional - true - - - returnType - string - comment - Encodes a Lua value as JSON preserving SL types. Use tight to encode more compactly. - - - name - sldecode - parameters - - - name - json - type - string - - - returnType - any - comment - Decodes a JSON string to a Lua value preserving SL types - - - - - name - math - comment - Mathematical functions library - properties - - - name - pi - type - number - comment - The value of pi - - - name - huge - type - number - comment - A value larger than any other numeric value (infinity) - - - functions - - - name - abs - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the absolute value of x - - - name - acos - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the arc cosine of x (in radians) - - - name - asin - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the arc sine of x (in radians) - - - name - atan - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the arc tangent of x (in radians) - - - name - atan2 - parameters - - - name - y - type - number - - - name - x - type - number - - - returnType - number - comment - Returns the arc tangent of y/x (in radians), using the signs to determine the quadrant - - - name - ceil - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the smallest integer larger than or equal to x - - - name - clamp - parameters - - - name - n - type - number - - - name - min - type - number - - - name - max - type - number - - - returnType - number - comment - Returns n clamped between min and max - - - name - cos - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the cosine of x (x in radians) - - - name - cosh - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the hyperbolic cosine of x - - - name - deg - parameters - - - name - x - type - number - - - returnType - number - comment - Converts x from radians to degrees - - - name - exp - parameters - - - name - x - type - number - - - returnType - number - comment - Returns e^x - - - name - floor - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the largest integer smaller than or equal to x - - - name - fmod - parameters - - - name - x - type - number - - - name - y - type - number - - - returnType - number - comment - Returns the remainder of x/y that rounds towards zero - - - name - frexp - parameters - - - name - x - type - number - - - returnType - (number, number) - comment - Returns m and e such that x = m * 2^e - - - name - ldexp - parameters - - - name - m - type - number - - - name - e - type - number - - - returnType - number - comment - Returns m * 2^e - - - name - lerp - parameters - - - name - a - type - number - - - name - b - type - number - - - name - t - type - number - - - returnType - number - comment - Linear interpolation between a and b by t - - - name - log - parameters - - - name - x - type - number - - - name - base - type - number - optional - true - - - returnType - number - comment - Returns the logarithm of x in the given base (default e) - - - name - log10 - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the base-10 logarithm of x - - - name - map - parameters - - - name - x - type - number - - - name - inMin - type - number - - - name - inMax - type - number - - - name - outMin - type - number - - - name - outMax - type - number - - - returnType - number - comment - Maps x from input range to output range - - - name - max - parameters - - - name - x - type - number - - - type - number - variadic - true - - - returnType - number - comment - Returns the maximum value among the arguments - - - name - min - parameters - - - name - x - type - number - - - type - number - variadic - true - - - returnType - number - comment - Returns the minimum value among the arguments - - - name - modf - parameters - - - name - x - type - number - - - returnType - (number, number) - comment - Returns the integer and fractional parts of x - - - name - noise - parameters - - - name - x - type - number - - - name - y - type - number - optional - true - - - name - z - type - number - optional - true - - - returnType - number - comment - Returns Perlin noise value for the given coordinates - - - name - pow - parameters - - - name - base - type - number - - - name - exponent - type - number - - - returnType - number - comment - Returns base^exponent - - - name - rad - parameters - - - name - x - type - number - - - returnType - number - comment - Converts x from degrees to radians - - - name - random - parameters - - - name - m - type - number - optional - true - - - name - n - type - number - optional - true - - - returnType - number - comment - Returns a pseudo-random number - - - name - randomseed - parameters - - - name - seed - type - number - - - returnType - () - comment - Sets the seed for the pseudo-random generator - - - name - round - parameters - - - name - x - type - number - - - returnType - number - comment - Returns x rounded to the nearest integer - - - name - sign - parameters - - - name - x - type - number - - - returnType - number - comment - Returns -1, 0, or 1 depending on the sign of x - - - name - sin - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the sine of x (x in radians) - - - name - sinh - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the hyperbolic sine of x - - - name - sqrt - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the square root of x - - - name - tan - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the tangent of x (x in radians) - - - name - tanh - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the hyperbolic tangent of x - - - name - isnan - parameters - - - name - x - type - number - - - returnType - boolean - comment - Returns true if x is NaN - - - name - isinf - parameters - - - name - x - type - number - - - returnType - boolean - comment - Returns true if x is infinite - - - name - isfinite - parameters - - - name - x - type - number - - - returnType - boolean - comment - Returns true if x is finite - - - - - name - os - comment - Operating system facilities library - functions - - - name - clock - parameters - - returnType - number - comment - Returns CPU time used by the program in seconds - - - name - date - parameters - - - name - format - type - string - optional - true - - - name - time - type - number - optional - true - - - returnType - string | OsDateTime | nil - comment - Returns a string or table containing date and time - - - name - difftime - parameters - - - name - t2 - type - number - - - name - t1 - type - number - optional - true - - - returnType - number - comment - Returns the difference in seconds between two times - - - name - time - parameters - - - name - time - type - OsDateTime - optional - true - - - returnType - number? - comment - Returns the current time or converts a table to time - - - - - name - string - comment - String manipulation library - functions - - - name - byte - parameters - - - name - s - type - string - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - ...number - comment - Returns the internal numeric codes of the characters - - - name - char - parameters - - - type - number - variadic - true - - - returnType - string - comment - Returns a string from character codes - - - name - find - parameters - - - name - s - type - string - - - name - pattern - type - string - - - name - init - type - number - optional - true - - - name - plain - type - boolean - optional - true - - - returnType - (number?, number?, ...string) - comment - Finds first match of pattern in string - - - name - format - parameters - - - name - formatstring - type - string - - - type - any - variadic - true - - - returnType - string - comment - Returns a formatted string - - - name - gmatch - parameters - - - name - s - type - string - - - name - pattern - type - string - - - returnType - () -> ...string - comment - Returns an iterator function for pattern matches - - - name - gsub - parameters - - - name - s - type - string - - - name - pattern - type - string - - - name - repl - type - string | { [string]: string } | (...string) -> string - - - name - n - type - number - optional - true - - - returnType - (string, number) - comment - Global substitution of pattern matches - - - name - len - parameters - - - name - s - type - string - - - returnType - number - comment - Returns the length of the string - - - name - lower - parameters - - - name - s - type - string - - - returnType - string - comment - Converts string to lowercase - - - name - match - parameters - - - name - s - type - string - - - name - pattern - type - string - - - name - init - type - number - optional - true - - - returnType - ...string - comment - Returns captures from pattern match - - - name - pack - parameters - - - name - fmt - type - string - - - type - any - variadic - true - - - returnType - string - comment - Packs values into a binary string - - - name - packsize - parameters - - - name - fmt - type - string - - - returnType - number - comment - Returns the size of a packed string for the given format - - - name - rep - parameters - - - name - s - type - string - - - name - n - type - number - - - returnType - string - comment - Returns a string repeated n times - - - name - reverse - parameters - - - name - s - type - string - - - returnType - string - comment - Reverses a string - - - name - split - parameters - - - name - s - type - string - - - name - separator - type - string - optional - true - - - returnType - {string} - comment - Splits a string by separator - - - name - sub - parameters - - - name - s - type - string - - - name - i - type - number - - - name - j - type - number - optional - true - - - returnType - string - comment - Returns a substring - - - name - unpack - parameters - - - name - fmt - type - string - - - name - s - type - string - - - name - pos - type - number - optional - true - - - returnType - ...any - comment - Unpacks values from a binary string - - - name - upper - parameters - - - name - s - type - string - - - returnType - string - comment - Converts string to uppercase - - - - - name - table - comment - Table manipulation library - functions - - - name - concat - parameters - - - name - list - type - {string} - - - name - sep - type - string - optional - true - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - string - comment - Concatenates table elements into a string - - - name - foreach - parameters - - - name - t - type - {[any]: any} - - - name - f - type - (key: any, value: any) -> any - - - returnType - any? - comment - Iterates over table key-value pairs (deprecated) - - - name - foreachi - parameters - - - name - t - type - {any} - - - name - f - type - (index: number, value: any) -> any - - - returnType - any? - comment - Iterates over array indices (deprecated) - - - name - getn - parameters - - - name - t - type - {any} - - - returnType - number - comment - Returns the length of a table (deprecated, use # operator) - - - name - maxn - parameters - - - name - t - type - {any} - - - returnType - number - comment - Returns the largest positive numeric index - - - name - insert - parameters - - - name - list - type - {any} - - - name - value - type - any - - - returnType - () - comment - Inserts an element at the end of a list - overloads - - - parameters - - - name - list - type - {any} - - - name - pos - type - number - - - name - value - type - any - - - returnType - () - comment - Inserts an element at a specific position - - - - - name - remove - parameters - - - name - list - type - {any} - - - name - pos - type - number - optional - true - - - returnType - any? - comment - Removes and returns an element from a list - - - name - sort - parameters - - - name - list - type - {any} - - - name - comp - type - (a: any, b: any) -> boolean - optional - true - - - returnType - () - comment - Sorts list elements in place - - - name - pack - parameters - - - type - any - variadic - true - - - returnType - { n: number, [number]: any } - comment - Packs arguments into a table with length field n - - - name - unpack - parameters - - - name - list - type - {any} - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - ...any - comment - Unpacks table elements as multiple return values - - - name - move - parameters - - - name - a1 - type - {any} - - - name - f - type - number - - - name - e - type - number - - - name - t - type - number - - - name - a2 - type - {any} - optional - true - - - returnType - {any} - comment - Moves elements from one table to another - - - name - create - parameters - - - name - count - type - number - - - name - value - type - any - optional - true - - - returnType - {any} - comment - Creates a new table with pre-allocated array slots - - - name - find - parameters - - - name - t - type - {any} - - - name - value - type - any - - - name - init - type - number - optional - true - - - returnType - number? - comment - Finds the index of a value in an array - - - name - clear - parameters - - - name - t - type - {[any]: any} - - - returnType - () - comment - Removes all elements from a table - - - name - freeze - parameters - - - name - t - type - {[any]: any} - - - returnType - {[any]: any} - comment - Makes a table read-only - - - name - isfrozen - parameters - - - name - t - type - {[any]: any} - - - returnType - boolean - comment - Returns true if a table is frozen - - - name - clone - parameters - - - name - t - type - {[any]: any} - - - returnType - {[any]: any} - comment - Creates a shallow copy of a table - - - - - name - utf8 - comment - UTF-8 support library - properties - - - name - charpattern - type - string - comment - Pattern that matches exactly one UTF-8 byte sequence - - - functions - - - name - char - parameters - - - type - number - variadic - true - - - returnType - string - comment - Returns a string from UTF-8 codepoints - - - name - codes - parameters - - - name - s - type - string - - - returnType - ((string, number) -> (number, number), string, number) - comment - Returns an iterator for UTF-8 codepoints in a string - - - name - codepoint - parameters - - - name - s - type - string - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - ...number - comment - Returns the codepoints of characters in a string - - - name - len - parameters - - - name - s - type - string - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - (number?, number?) - comment - Returns the number of UTF-8 characters in a string, or nil and error position - - - name - offset - parameters - - - name - s - type - string - - - name - n - type - number - - - name - i - type - number - optional - true - - - returnType - number? - comment - Returns the byte position of the n-th character - - - - - globalFunctions - - - name - dangerouslyexecuterequiredmodule - parameters - - - name - f - type - (...any) -> ...any - - - returnType - ...any - comment - Dangerously executes a required module function - - - name - touuid - parameters - - - name - val - type - string | buffer | uuid - - - returnType - uuid? - comment - Converts a string, buffer, or uuid to a uuid, returns nil if invalid - - - name - tovector - parameters - - - name - val - type - any - - - returnType - vector? - comment - Converts a value to a vector, returns nil if invalid - - - name - toquaternion - parameters - - - name - val - type - any - - - returnType - quaternion? - comment - Converts a value to a quaternion, returns nil if invalid - - - name - torotation - parameters - - - name - val - type - any - - - returnType - quaternion? - comment - Converts a value to a rotation (quaternion), returns nil if invalid - - - constants - - - diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index a9e3afbc58..544473ba77 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -507,11 +507,10 @@ bool LLScriptEdCore::postBuild() initMenu(); - mSyntaxIDConnection = LLSyntaxIdLSL::getInstance()->addSyntaxIDCallback(boost::bind(&LLScriptEdCore::processKeywords, this)); + mSyntaxIDConnection = LLSyntaxDefCache::getInstance()->addSyntaxIDCallback(boost::bind(&LLScriptEdCore::processKeywords, this)); // Intialise keyword highlighting for the current simulator's version of LSL - LLSyntaxIdLSL::getInstance()->initialize(); - LLSyntaxLua::getInstance()->initialize(); + LLSyntaxDefCache::getInstance(); processKeywords(); mCommitCallbackRegistrar.add("FontSize.Set", boost::bind(&LLScriptEdCore::onChangeFontSize, this, _2)); diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 2a4c2b9d0b..ac6b81f677 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -342,8 +342,8 @@ void LLScriptEditor::drawLineNumbers() void LLScriptEditor::initKeywords(bool luau_language) { - mKeywordsLua.initialize(LLSyntaxLua::getInstance()->getKeywordsXML(), true); - mKeywordsLSL.initialize(LLSyntaxIdLSL::getInstance()->getKeywordsXML(), false); + mKeywordsLua.initialize(LLSyntaxDefCache::getInstance()->getLuaKeywords(), true); + mKeywordsLSL.initialize(LLSyntaxDefCache::getInstance()->getLSLKeywords(), false); mLuauLanguage = luau_language; diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp index 30a1ca53f6..11aacd791e 100644 --- a/indra/newview/llscripteditorws.cpp +++ b/indra/newview/llscripteditorws.cpp @@ -73,7 +73,7 @@ LLWebsocketMgr::WSConnection::ptr_t LLScriptEditorWSServer::connectionFactory(LL void LLScriptEditorWSServer::onStarted() { - LLSyntaxIdLSL& syntax_id_mgr = LLSyntaxIdLSL::instance(); + LLSyntaxDefCache& syntax_id_mgr = LLSyntaxDefCache::instance(); wptr_t that(std::static_pointer_cast(shared_from_this())); mLastSyntaxId = syntax_id_mgr.getSyntaxID(); @@ -302,7 +302,7 @@ void LLScriptEditorWSServer::setupConnectionMethods(LLJSONRPCConnection::ptr_t c void LLScriptEditorWSServer::broadcastLanguageChange() { - LLUUID syntax_id = LLSyntaxIdLSL::instance().getSyntaxID(); + LLUUID syntax_id = LLSyntaxDefCache::instance().getSyntaxID(); if (syntax_id != mLastSyntaxId) { @@ -340,12 +340,12 @@ LLSD LLScriptEditorWSServer::handleSyntaxRequest(const LLSD& params) const response["id"] = mLastSyntaxId; if (category == "defs.lua") { - response["defs"] = LLSyntaxLua::instance().getTypesXML(); + response["defs"] = LLSyntaxDefCache::instance().getLuaKeywords(); response["success"] = response["defs"].isDefined(); } else if (category == "defs.lsl") { - response["defs"] = LLSyntaxIdLSL::instance().getKeywordsXML(); + response["defs"] = LLSyntaxDefCache::instance().getLSLKeywords(); response["success"] = response["defs"].isDefined(); } else @@ -710,7 +710,7 @@ void LLScriptEditorWSConnection::onOpen() languages.append("lsl"); languages.append("luau"); handshake["languages"] = languages; - handshake["syntax_id"] = LLSyntaxIdLSL::instance().getSyntaxID(); + handshake["syntax_id"] = LLSyntaxDefCache::instance().getSyntaxID(); // Features object LLSD features; diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 16274f59bc..3d0a9d80b4 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -38,37 +38,53 @@ //----------------------------------------------------------------------------- // LLSyntaxIdLSL //----------------------------------------------------------------------------- -const std::string SYNTAX_ID_CAPABILITY_NAME = "LSLSyntax"; -const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; -const std::string FILENAME_DEFAULT_LSL = "keywords_lsl_default.xml"; -const std::string FILENAME_DEFAULT_LUA = "keywords_lua_default.xml"; - -/** - * @brief LLSyntaxIdLSL constructor - */ -LLSyntaxIdLSL::LLSyntaxIdLSL() -: mKeywordsXml(LLSD()) -, mCapabilityURL(std::string()) -, mFilePath(LL_PATH_APP_SETTINGS) -, mSyntaxId(LLUUID()) -, mInitialized(false) +namespace +{ + const std::string SYNTAX_ID_CAPABILITY_NAME = "LSLSyntax"; + const std::string SYNTAX_DEF_CAPABILITY_NAME = "ScriptDefinitions"; + const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; + const std::string FILENAME_INTERNAL_LSL = "lsl_keywords.xml"; + const std::string FILENAME_INTERNAL_LUA = "slua_keywords.xml"; + + constexpr U32 LLSD_SYNTAX_LSL_VERSION_EXPECTED = 2; + const std::string LLSD_SYNTAX_LSL_VERSION_KEY("llsd-lsl-syntax-version"); + + const std::unordered_set MEMCACHED_LLSD = { + FILENAME_INTERNAL_LSL, + FILENAME_INTERNAL_LUA + }; +} // namespace + +//======================================================================== +void LLSyntaxDefCache::initSingleton() { - loadDefaultKeywordsIntoLLSD(); - mRegionChangedCallback = gAgent.addRegionChangedCallback(boost::bind(&LLSyntaxIdLSL::handleRegionChanged, this)); + buildDefaultCache(); + loadKeywordsIntoLLSD(); + mRegionChangedCallback = gAgent.addRegionChangedCallback(boost::bind(&LLSyntaxDefCache::handleRegionChanged, this)); handleRegionChanged(); // Kick off an initial caps query and fetch } -void LLSyntaxIdLSL::buildFullFileSpec() +void LLSyntaxDefCache::cleanupSingleton() { - ELLPath path = mSyntaxId.isNull() ? LL_PATH_APP_SETTINGS : LL_PATH_CACHE; - const std::string filename = mSyntaxId.isNull() ? FILENAME_DEFAULT_LSL : "keywords_lsl_" + mSyntaxId.asString() + ".llsd.xml"; - mFullFileSpec = gDirUtilp->getExpandedFilename(path, filename); + gAgent.removeRegionChangedCallback(mRegionChangedCallback); + mLSLKeywords = LLSD(); + mLuaKeywords = LLSD(); + mCapabilityURL = std::string(); + mSyntaxId = LLUUID(); + mFileCachePaths.clear(); } -//----------------------------------------------------------------------------- -// syntaxIdChange() -//----------------------------------------------------------------------------- -bool LLSyntaxIdLSL::syntaxIdChanged() +boost::signals2::connection LLSyntaxDefCache::addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb) +{ + return mSyntaxIDChangedSignal.connect(cb); +} + +//======================================================================== +// checkSyntaxId() +// Checks the current region for the LSLSyntaxId feature and capability, and +// if found checks the syntax ID against the one we have. If they differ, +// updates the syntax ID and returns true. Otherwise returns false. +bool LLSyntaxDefCache::updateSyntaxId() { LLViewerRegion* region = gAgent.getRegion(); @@ -83,51 +99,97 @@ bool LLSyntaxIdLSL::syntaxIdChanged() { // get and check the hash LLUUID new_syntax_id = sim_features[SYNTAX_ID_SIMULATOR_FEATURE].asUUID(); - mCapabilityURL = region->getCapability(SYNTAX_ID_CAPABILITY_NAME); + + // *Note* the syntax ID may not have changed, but the region almost certainly has. + // update the cap URL + mCapabilityURL = region->getCapability(SYNTAX_DEF_CAPABILITY_NAME); + mUseDefsCap = !mCapabilityURL.empty(); + if (!mUseDefsCap) + { + mCapabilityURL = region->getCapability(SYNTAX_ID_CAPABILITY_NAME); + } LL_DEBUGS("SyntaxLSL") << SYNTAX_ID_SIMULATOR_FEATURE << " capability URL: " << mCapabilityURL << LL_ENDL; + if (new_syntax_id != mSyntaxId) { LL_DEBUGS("SyntaxLSL") << "New SyntaxID '" << new_syntax_id << "' found." << LL_ENDL; mSyntaxId = new_syntax_id; return true; } - else - LL_DEBUGS("SyntaxLSL") << "SyntaxID matches what we have." << LL_ENDL; + + LL_DEBUGS("SyntaxLSL") << "SyntaxID has not changed. Still " << mSyntaxId << LL_ENDL; } } else { - region->setCapabilitiesReceivedCallback(boost::bind(&LLSyntaxIdLSL::handleCapsReceived, this, _1)); + region->setCapabilitiesReceivedCallback(boost::bind(&LLSyntaxDefCache::handleCapsReceived, this, _1)); LL_DEBUGS("SyntaxLSL") << "Region has not received capabilities. Waiting for caps..." << LL_ENDL; } } return false; } +void LLSyntaxDefCache::handleRegionChanged() +{ + if (updateSyntaxId()) + { + if (!checkCacheAndLoad(mSyntaxId)) + { + fetchKeywords(); + } + } +} + +void LLSyntaxDefCache::handleCapsReceived(const LLUUID& region_uuid) +{ + LLViewerRegion* current_region = gAgent.getRegion(); + + if (region_uuid.notNull() && current_region->getRegionID() == region_uuid) + { + updateSyntaxId(); + if (!checkCacheAndLoad(mSyntaxId)) + { + fetchKeywords(); + } + } +} + //----------------------------------------------------------------------------- -// fetchKeywordsFile -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::fetchKeywordsFile(const std::string& filespec) +// fetchKeywords +// Initiates a fetch of the current language definitions from the region caps. +void LLSyntaxDefCache::fetchKeywords() { - LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsFileCoro", - boost::bind(&LLSyntaxIdLSL::fetchKeywordsFileCoro, this, mCapabilityURL, filespec)); - LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId capability URL is: " << mCapabilityURL << ". Filename to use is: '" << filespec << "'." << LL_ENDL; + if (mCapabilityURL.empty()) + { + LL_WARNS("SyntaxLSL") << "No capability URL for fetching syntax definitions." << LL_ENDL; + return; + } + if (mUseDefsCap) + { + LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsDefsCoro", + boost::bind(&LLSyntaxDefCache::fetchKeywordsDefsCoro, this, mCapabilityURL, mSyntaxId)); + } + else + { + LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsFileCoro", + boost::bind(&LLSyntaxDefCache::fetchKeywordsFileCoro, this, mCapabilityURL, mSyntaxId)); + } } //----------------------------------------------------------------------------- // fetchKeywordsFileCoro -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) +// This uses the legacy languge cap which only sends the LSL keywords file. +void LLSyntaxDefCache::fetchKeywordsFileCoro(std::string url, LLUUID syntax_id) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter = std::make_shared("fetchKeywordsFileCoro", httpPolicy); LLCore::HttpRequest::ptr_t httpRequest = std::make_shared(); - std::pair::iterator, bool> insrt = mInflightFetches.insert(fileSpec); + auto insrt = mInflightFetches.insert(syntax_id); if (!insrt.second) { - LL_WARNS("SyntaxLSL") << "Already downloading keyword file called \"" << fileSpec << "\"." << LL_ENDL; + LL_WARNS("SyntaxLSL") << "Already downloading keyword file for syntax ID \"" << syntax_id << "\"." << LL_ENDL; return; } @@ -136,11 +198,11 @@ void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - mInflightFetches.erase(fileSpec); + mInflightFetches.erase(syntax_id); if (!status) { - LL_WARNS("SyntaxLSL") << "Failed to fetch syntax file \"" << fileSpec << "\"" << LL_ENDL; + LL_WARNS("SyntaxLSL") << "Failed to fetch syntax file for syntax ID \"" << syntax_id << "\"" << LL_ENDL; return; } @@ -148,13 +210,38 @@ void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) if (isSupportedVersion(result)) { - // Shuttle this task to the main coro/worker. - // loadKeywordsIntoLLSD will attempt to get a mutex which is not coro aware. - LLAppViewer::instance()->postToMainCoro([this, result, fileSpec]() + std::string path = buildCacheDirectoryName(syntax_id); + + if (!LLFile::exists(path)) + { + LL_DEBUGS("SyntaxLSL") << "Cache directory '" << path << "' does not exist. Attempting to create." << LL_ENDL; + if (LLFile::mkdir(path)) { - setKeywordsXml(result); - cacheFile(fileSpec, result); - loadKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Failed to create cache directory '" << path << "'. Cannot cache syntax defs file." << LL_ENDL; + return; + } + } + + // Note that after this call the file cache will have all well known files pointing + // to the default versions, so below, where we get the path to the lua keywords + // well be loading the default version. + buildDefaultCache(); + + // The LSL keywords we just received + std::string full_path = gDirUtilp->add(path, FILENAME_INTERNAL_LSL); + if (writeCacheFile(full_path, result)) + { + mFileCachePaths.addNamePath(FILENAME_INTERNAL_LSL, full_path); + } + // We need to manually load the Lua keywords + full_path = mFileCachePaths.getPath(FILENAME_INTERNAL_LUA); + LLSD lua_defs = loadDeserializedCacheFile(full_path); + + setKeywords(result, lua_defs); + + // Shuttle this task to the main coro/worker. + LLAppViewer::instance()->postToMainCoro([this]() { + mSyntaxIDChangedSignal(); }); } else @@ -164,73 +251,196 @@ void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) } -//----------------------------------------------------------------------------- -// cacheFile -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::cacheFile(const std::string &fileSpec, const LLSD& content_ref) +void LLSyntaxDefCache::fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id) { - std::stringstream str; - LLSDSerialize::toXML(content_ref, str); - const std::string xml = str.str(); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter = + std::make_shared("fetchKeywordsDefsCoro", httpPolicy); + LLCore::HttpRequest::ptr_t httpRequest = std::make_shared(); - // save the str to disk, usually to the cache. - llofstream file(fileSpec.c_str(), std::ios_base::out); - file.write(xml.c_str(), str.str().size()); - file.close(); + static std::set inflightDefsFetches; + auto insrt = inflightDefsFetches.insert(syntax_id); + //auto insrt = mInflightFetches.insert(syntax_id); + if (!insrt.second) + { + LL_WARNS("SyntaxLSL") << "Already downloading keyword defs for \"" << syntax_id << "\"." << LL_ENDL; + return; + } - LL_DEBUGS("SyntaxLSL") << "Syntax file received, saving as: '" << fileSpec << "'" << LL_ENDL; -} + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); -//----------------------------------------------------------------------------- -// initialize -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::initialize() -{ - if(mInitialized) return; - if (mSyntaxId.isNull()) + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + inflightDefsFetches.erase(syntax_id); + + if (!status) { - loadDefaultKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Failed to fetch syntax file \"" << syntax_id << "\"" << LL_ENDL; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + return; } - else if (!mCapabilityURL.empty()) + + // Now we need to walk through the returned LLSD. It consists of a map keyed on the file name containing a binary + // blob of the actual file contents. + LLSD files = result["files"]; + LLSD memcached_keywords; + if (files.isMap()) { - LL_DEBUGS("SyntaxLSL") << "LSL version has changed, getting appropriate file." << LL_ENDL; + std::string path = buildCacheDirectoryName(syntax_id); - // Need a full spec regardless of file source, so build it now. - buildFullFileSpec(); - if (mSyntaxId.notNull()) + if (!LLFile::exists(path)) { - if (!gDirUtilp->fileExists(mFullFileSpec)) - { // Does not exist, so fetch it from the capability - LL_DEBUGS("SyntaxLSL") << "LSL syntax not cached, attempting download." << LL_ENDL; - fetchKeywordsFile(mFullFileSpec); - } - else + LL_DEBUGS("SyntaxLSL") << "Cache directory '" << path << "' does not exist. Attempting to create." << LL_ENDL; + if (LLFile::mkdir(path)) { - LL_DEBUGS("SyntaxLSL") << "Found cached Syntax file: " << mFullFileSpec << " Loading keywords." << LL_ENDL; - loadKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Failed to create cache directory '" << path << "'. Cannot cache syntax defs file." << LL_ENDL; + return; } } - else + + buildDefaultCache(); + + for (const auto &[filename, contents] : llsd::inMap(files)) { - LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId is null. Loading default values" << LL_ENDL; - loadDefaultKeywordsIntoLLSD(); + std::string full_path = gDirUtilp->add(path, filename); + + if (MEMCACHED_LLSD.find(filename) != MEMCACHED_LLSD.end()) + { // Maintain some keyword LLSDs internally, LSL and Lua + memcached_keywords[filename] = contents; + } + + if (writeCacheFile(full_path, contents)) + { + mFileCachePaths.addNamePath(filename, full_path); + } } } else { - LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId capability URL is empty." << LL_ENDL; - loadDefaultKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Malformed syntax defs response, missing 'files' map." << LL_ENDL; } - mInitialized = true; + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + + setKeywords(memcached_keywords["lsl_keywords.xml"], memcached_keywords["lua_keywords.xml"]); + LLAppViewer::instance()->postToMainCoro( + [this]() + { + mSyntaxIDChangedSignal(); + }); } +//----------------------------------------------------------------------------- +// buildCache +// Constructs the cache file paths for the given syntax ID. If the syntax ID is null, +// constructs the default cache file paths. +void LLSyntaxDefCache::buildCachePaths(const LLUUID &syntax_id) +{ + if (syntax_id.notNull()) + { // Initialize the cache files to point to the default files + buildCachePaths(LLUUID::null); + } + else + { + mFileCachePaths.clear(); + } + + std::string cache_dir = buildCacheDirectoryName(syntax_id); + if (!LLFile::exists(cache_dir)) + { + LL_DEBUGS("SyntaxLSL") << "Cache directory '" << cache_dir << "' does not exist." << LL_ENDL; + return; + } + + auto files = gDirUtilp->getFilesInDir(cache_dir); + for (const auto &file : files) + { + mFileCachePaths.addNamePath(file, gDirUtilp->add(cache_dir, file)); + } +} + +bool LLSyntaxDefCache::writeCacheFile(const std::string &fileSpec, const LLSD& content_ref) +{ + bool binary(content_ref.isBinary()); + std::ofstream file(fileSpec.c_str(), (binary)? std::ios_base::binary : 0); + + if (!file.is_open()) + { + LL_WARNS("SyntaxLSL") << "Failed to open file for writing: '" << fileSpec << "'" << LL_ENDL; + return false; + } + + if (binary) + { + LL_DEBUGS("SyntaxLSL") << "Caching raw content to '" << fileSpec << "'" << LL_ENDL; + file.write((const char*)content_ref.asBinary().data(), content_ref.asBinary().size()); + } + else + { + LL_DEBUGS("SyntaxLSL") << "Caching XML content to '" << fileSpec << "'" << LL_ENDL; + LLSDSerialize::serialize(content_ref, file, LLSDSerialize::LLSD_XML, LLSDFormatter::OPTIONS_PRETTY); + } + file.close(); + + if (!file.good()) + { + LL_WARNS("SyntaxLSL") << "Failed to write content to file: '" << fileSpec << "'" << LL_ENDL; + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// checkCacheAndLoad +// Tests the local cache for the given syntax ID. If found it loads the keywords +// from the cache into LLSD and returns true. Otherwise returns false. +bool LLSyntaxDefCache::checkCacheAndLoad(const LLUUID& syntax_id) +{ + if (checkLocalCache(syntax_id)) + { + buildCachePaths(syntax_id); + loadKeywordsIntoLLSD(); + return true; + } + return false; +} + +std::string LLSyntaxDefCache::buildCacheDirectoryName(const LLUUID& syntax_id) +{ + if (syntax_id.isNull()) + { + LL_DEBUGS("SyntaxLSL") << "No SyntaxID, using app settings directory." << LL_ENDL; + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "syntax_default"); + } + else + { + LL_DEBUGS("SyntaxLSL") << "Using cache directory for SyntaxID '" << syntax_id << "'." << LL_ENDL; + std::string cache_dir_name = "syntax_" + syntax_id.asString(); + + return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, cache_dir_name); + } +} + +bool LLSyntaxDefCache::checkLocalCache(const LLUUID& syntax_id) const +{ + if (syntax_id.isNull()) + { // Cache check will always fail if we don't have a valid SyntaxID, so skip it in that case. + LL_DEBUGS("SyntaxLSL") << "No SyntaxID, skipping local cache check." << LL_ENDL; + return false; + } + + // Check for the existence of the cache directory for this syntax ID. If it doesn't exist, then we don't have a cached file. + std::string cache_dir = buildCacheDirectoryName(syntax_id); + return gDirUtilp->fileExists(cache_dir); +} + + //----------------------------------------------------------------------------- // isSupportedVersion //----------------------------------------------------------------------------- -const U32 LLSD_SYNTAX_LSL_VERSION_EXPECTED = 2; -const std::string LLSD_SYNTAX_LSL_VERSION_KEY("llsd-lsl-syntax-version"); -bool LLSyntaxIdLSL::isSupportedVersion(const LLSD& content) +bool LLSyntaxDefCache::isSupportedVersion(const LLSD& content) { bool is_valid = false; /* @@ -256,128 +466,61 @@ bool LLSyntaxIdLSL::isSupportedVersion(const LLSD& content) } //----------------------------------------------------------------------------- -// loadDefaultKeywordsIntoLLSD() -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::loadDefaultKeywordsIntoLLSD() -{ - mSyntaxId.setNull(); - buildFullFileSpec(); - loadKeywordsIntoLLSD(); -} - -//----------------------------------------------------------------------------- -// loadKeywordsFileIntoLLSD +// loadKeywordsIntoLLSD //----------------------------------------------------------------------------- /** * @brief Load xml serialized LLSD - * @desc Opens the specified filespec and attempts to deserializes the - * contained data to the specified LLSD object. indicate success/failure with - * sLoaded/sLoadFailed members. + * @desc Open the internal lsl keywords files and deserialize them into the correct + * members. */ -void LLSyntaxIdLSL::loadKeywordsIntoLLSD() +void LLSyntaxDefCache::loadKeywordsIntoLLSD() { - LLSD content; - llifstream file; - file.open(mFullFileSpec.c_str()); - if (file.is_open()) + for (auto& filename : MEMCACHED_LLSD) { - if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) + // Note, in the case of the legacy language cap (it only delivers the LSL keywords file) + // The mFileCachePaths will have been initialized in such a way that the Lua keywords + // point to the default file. + std::string full_path = mFileCachePaths.getPath(filename); + if (!full_path.empty()) { - if (isSupportedVersion(content)) + LLSD content = loadDeserializedCacheFile(full_path); + if (!content.isUndefined() && isSupportedVersion(content)) { - LL_DEBUGS("SyntaxLSL") << "Deserialized: " << mFullFileSpec << LL_ENDL; + LL_DEBUGS("SyntaxLSL") << "Deserialized cached file: " << full_path << LL_ENDL; + if (filename == FILENAME_INTERNAL_LSL) + { + mLSLKeywords = content; + } + else if (filename == FILENAME_INTERNAL_LUA) + { + mLuaKeywords = content; + } } else { - LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file." << LL_ENDL; + LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file " << full_path << "." << LL_ENDL; } } } - else - { - LL_WARNS("SyntaxLSL") << "Failed to open: " << mFullFileSpec << LL_ENDL; - } - mKeywordsXml = content; - mSyntaxIDChangedSignal(); -} - -bool LLSyntaxIdLSL::keywordFetchInProgress() -{ - return !mInflightFetches.empty(); -} - -void LLSyntaxIdLSL::handleRegionChanged() -{ - if (syntaxIdChanged()) - { - buildFullFileSpec(); - fetchKeywordsFile(mFullFileSpec); - mInitialized = false; - } -} - -void LLSyntaxIdLSL::handleCapsReceived(const LLUUID& region_uuid) -{ - LLViewerRegion* current_region = gAgent.getRegion(); - - if (region_uuid.notNull() - && current_region->getRegionID() == region_uuid) - { - syntaxIdChanged(); - } -} - -boost::signals2::connection LLSyntaxIdLSL::addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb) -{ - return mSyntaxIDChangedSignal.connect(cb); -} - - - -//----------------------------------------------------------------------------- -// LLSyntaxLua -//----------------------------------------------------------------------------- -LLSyntaxLua::LLSyntaxLua() - : mKeywordsXml(LLSD()) - , mInitialized(false) -{ -} - -void LLSyntaxLua::initialize() -{ - if (mInitialized) return; - loadDefaultKeywordsIntoLLSD(); - loadLuaTypesIntoLLSD(); - mInitialized = true; + mSyntaxIDChangedSignal(); } -void LLSyntaxLua::loadDefaultKeywordsIntoLLSD() +LLSD LLSyntaxDefCache::loadDeserializedCacheFile(const std::string& file_path) { - std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, FILENAME_DEFAULT_LUA); - llifstream file(fullFileSpec.c_str()); - + std::ifstream file(file_path.c_str()); if (file.good()) { LLSD content; - if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) + if (LLSDSerialize::deserialize(content, file, -1)) { - mKeywordsXml = content; + return content; } } -} - -void LLSyntaxLua::loadLuaTypesIntoLLSD() -{ - std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "types_lua_default.xml"); - llifstream file(fullFileSpec.c_str()); - - if (file.good()) + else { - LLSD content; - if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) - { - mTypesXml = content; - } + LL_WARNS("SyntaxLSL") << "Failed to open cached file: " << file_path << LL_ENDL; } + return LLSD(); } + diff --git a/indra/newview/llsyntaxid.h b/indra/newview/llsyntaxid.h index 51103e3396..3bd49378b5 100644 --- a/indra/newview/llsyntaxid.h +++ b/indra/newview/llsyntaxid.h @@ -34,66 +34,101 @@ #include "lleventcoro.h" #include "llcoros.h" -class fetchKeywordsFileResponder; - -class LLSyntaxIdLSL : public LLSingleton +class LLSyntaxDefCache : public LLSingleton { - LLSINGLETON(LLSyntaxIdLSL); - friend class fetchKeywordsFileResponder; + LLSINGLETON(LLSyntaxDefCache) = default; public: using syntax_id_changed_signal_t = boost::signals2::signal; using syntax_id_changed_h = boost::signals2::connection; - - void initialize(); - bool keywordFetchInProgress(); - LLSD getKeywordsXML() const { return mKeywordsXml; }; + class cache_names_t + { + public: + using name_path_map_t = std::unordered_map; + using iterator = name_path_map_t::iterator; + using const_iterator = name_path_map_t::const_iterator; + + cache_names_t() = default; + ~cache_names_t() = default; + + std::string getPath(const std::string &name) const + { + auto it = mNamePathMap.find(name); + if (it != mNamePathMap.end()) + { + return it->second; + } + return std::string(); + } + + bool hasName(const std::string &name) const + { + return mNamePathMap.find(name) != mNamePathMap.end(); + } + + void addNamePath(const std::string &name, const std::string &path) + { + mNamePathMap[name] = path; + } + + iterator begin() { return mNamePathMap.begin(); } + const_iterator begin() const { return mNamePathMap.begin(); } + iterator end() { return mNamePathMap.end(); } + const_iterator end() const { return mNamePathMap.end(); } + + size_t size() const { return mNamePathMap.size(); } + void clear() { mNamePathMap.clear(); } + bool empty() const { return mNamePathMap.empty(); } + + private: + name_path_map_t mNamePathMap; + }; + + bool keywordFetchInProgress() const { return !mInflightFetches.empty(); } + LLSD getLSLKeywords() const { return mLSLKeywords; }; + LLSD getLuaKeywords() const { return mLuaKeywords; }; LLUUID getSyntaxID() const { return mSyntaxId; } syntax_id_changed_h addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb); + bool checkCacheAndLoad(const LLUUID& syntax_id); + + static std::string buildCacheDirectoryName(const LLUUID& syntax_id); + +protected: + void initSingleton() override; + void cleanupSingleton() override; + private: - std::set mInflightFetches; - syntax_id_changed_signal_t mSyntaxIDChangedSignal; - syntax_id_changed_h mRegionChangedCallback; - - bool syntaxIdChanged(); - bool isSupportedVersion(const LLSD& content); - void handleRegionChanged(); - void handleCapsReceived(const LLUUID& region_uuid); - void setKeywordsXml(const LLSD& content) { mKeywordsXml = content; }; - void buildFullFileSpec(); - void fetchKeywordsFile(const std::string& filespec); - void loadDefaultKeywordsIntoLLSD(); - void loadKeywordsIntoLLSD(); - - void fetchKeywordsFileCoro(std::string url, std::string fileSpec); - void cacheFile(const std::string &fileSpec, const LLSD& content_ref); - - std::string mCapabilityURL; - std::string mFullFileSpec; - ELLPath mFilePath; - LLUUID mSyntaxId; - LLSD mKeywordsXml; - bool mInitialized; -}; + bool updateSyntaxId(); + static bool isSupportedVersion(const LLSD& content); + void handleRegionChanged(); + void handleCapsReceived(const LLUUID& region_uuid); + void setKeywords(const LLSD& lsl, const LLSD& lua) { mLSLKeywords = lsl; mLuaKeywords = lua; }; + void loadKeywordsIntoLLSD(); -class LLSyntaxLua : public LLSingleton -{ - LLSINGLETON(LLSyntaxLua); + void fetchKeywords(); + void fetchKeywordsFileCoro(std::string url, LLUUID syntax_id); + void fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id); -public: - void initialize(); - LLSD getKeywordsXML() const { return mKeywordsXml; } - LLSD getTypesXML() const { return mTypesXml; } + bool checkLocalCache(const LLUUID& syntax_id) const; + void buildDefaultCache() { buildCachePaths(LLUUID::null); } + void buildCachePaths(const LLUUID &syntax_id); + bool writeCacheFile(const std::string &fileSpec, const LLSD& content_ref); -private: - void loadDefaultKeywordsIntoLLSD(); - void loadLuaTypesIntoLLSD(); - LLSD mKeywordsXml; - LLSD mTypesXml; - bool mInitialized; + static LLSD loadDeserializedCacheFile(const std::string& file_path); + + std::set mInflightFetches; + syntax_id_changed_signal_t mSyntaxIDChangedSignal; + syntax_id_changed_h mRegionChangedCallback; + + std::string mCapabilityURL; + cache_names_t mFileCachePaths; + LLUUID mSyntaxId; + LLSD mLSLKeywords; + LLSD mLuaKeywords; + bool mUseDefsCap{ false }; }; #endif // LLSYNTAXID_H diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 42a587f376..01ce2a7a21 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3325,6 +3325,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("ViewerMetrics"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); + capabilityNames.append("ScriptDefinitions"); // Please add new capabilities alphabetically to reduce // merge conflicts. diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 21e50f4dcb..524077f1b2 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -64,6 +64,16 @@ class ViewerManifest(LLManifest): super(ViewerManifest, self).construct() self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") + # ... and the lsl/lua script definition files (needed at build time and packaging) + pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') + with self.prefix(src=os.path.join(pkgdir, 'lsl_definitions'), dst="app_settings/syntax_default"): + self.path("*.txt") + self.path("*.yaml") + self.path("*.xml") + self.path("*.json") + self.path("*.luau") + self.path("*.yml") + if self.is_packaging_viewer(): with self.prefix(src_dst="app_settings"): self.exclude("logcontrol.xml") @@ -92,7 +102,7 @@ class ViewerManifest(LLManifest): with self.prefix(src=pkgdir): self.path("dictionaries") - # include the extracted packages information (see BuildPackagesInfo.cmake) + # include the extracted packages information self.path(src=os.path.join(self.args['build'],"packages-info.txt"), dst="packages-info.txt") # CHOP-955: If we have "sourceid" or "viewer_channel" in the # build process environment, generate it into -- cgit v1.3 From 84e806f71492a76ab28e049ec255df479ffbb339 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 29 Apr 2026 14:43:08 -0700 Subject: Added support to jsonrpc server for the viewer based syntax cache. --- doc/external-editor-json-rpc.md | 236 +++++++++++++++++++++++++++---------- indra/newview/llscripteditorws.cpp | 89 ++++++++++++++ indra/newview/llscripteditorws.h | 2 + indra/newview/llsyntaxid.cpp | 39 ++++++ indra/newview/llsyntaxid.h | 18 +++ 5 files changed, 324 insertions(+), 60 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/doc/external-editor-json-rpc.md b/doc/external-editor-json-rpc.md index b8c7fa69e3..8d657b0d33 100644 --- a/doc/external-editor-json-rpc.md +++ b/doc/external-editor-json-rpc.md @@ -1,6 +1,6 @@ # Viewer to External Editor JSON-RPC
Message Interfaces Documentation -This document describes all the message interfaces defined in for WebSocket communication between the Second Life viewer and an external editor such as a VSCode extension. +This document describes all the message interfaces defined for WebSocket communication between the Second Life viewer and an external editor such as a VSCode extension. ## Table of Contents @@ -15,10 +15,13 @@ This document describes all the message interfaces defined in for WebSocket comm - [SyntaxChange](#syntaxchange) - [Language Syntax ID Request](#language-syntax-id-request) - [Language Syntax Request](#language-syntax-request) + - [Language Syntax Cache List](#language-syntax-cache-list) + - [Language Syntax Cache Get](#language-syntax-cache-get) - [Script Subscription Interfaces](#script-subscription-interfaces) - [ScriptSubscribe](#scriptsubscribe) - [ScriptSubscribeResponse](#scriptsubscriberesponse) - [ScriptUnsubscribe](#scriptunsubscribe) + - [ScriptList](#scriptlist) - [Compilation Interfaces](#compilation-interfaces) - [CompilationError](#compilationerror) - [CompilationResult](#compilationresult) @@ -33,7 +36,7 @@ This document describes all the message interfaces defined in for WebSocket comm 1. **Connection Establishment:** - - Viewer sends `session.handshake` notification with `SessionHandshake` data + - Viewer sends `session.handshake` call with `SessionHandshake` data - Extension responds with `SessionHandshakeResponse` - Viewer confirms with `session.ok` notification @@ -41,7 +44,7 @@ This document describes all the message interfaces defined in for WebSocket comm - Extension makes `language.syntax.id` call to get current syntax version - Extension makes `language.syntax` calls with different `kind` parameters to get specific language data - - Viewer responds with `LanguageInfo` data containing the requested information + - Viewer responds with a `LanguageInfo` object containing the requested definitions 3. **Script Subscription Management:** @@ -65,17 +68,23 @@ This document describes all the message interfaces defined in for WebSocket comm | Method | Direction | Type | Interface/Parameters | | ------------------------------- | ------------------ | ------------ | -------------------------- | -| `session.handshake` | Viewer → Extension | Notification | `SessionHandshake` | +| `session.handshake` | Viewer → Extension | Call | `SessionHandshake` | | `session.handshake` (response) | Extension → Viewer | Response | `SessionHandshakeResponse` | | `session.ok` | Viewer → Extension | Notification | _(no interface)_ | | `session.disconnect` | Bidirectional | Notification | `SessionDisconnect` | | `script.subscribe` | Extension → Viewer | Call | `ScriptSubscribe` | | `script.subscribe` (response) | Viewer → Extension | Response | `ScriptSubscribeResponse` | | `script.unsubscribe` | Viewer → Extension | Notification | `ScriptUnsubscribe` | +| `script.list` | Extension → Viewer | Call | _(no parameters)_ | +| `script.list` (response) | Viewer → Extension | Response | `ScriptList` | | `language.syntax.id` | Extension → Viewer | Call | _(no parameters)_ | | `language.syntax.id` (response) | Viewer → Extension | Response | `{ id: string }` | | `language.syntax` | Extension → Viewer | Call | `{ kind: string }` | | `language.syntax` (response) | Viewer → Extension | Response | `LanguageInfo` | +| `language.syntax.cache` | Extension → Viewer | Call | _(no parameters)_ | +| `language.syntax.cache` (response) | Viewer → Extension | Response | `SyntaxCacheList` | +| `language.syntax.get` | Extension → Viewer | Call | `{ filename: string, as_json?: boolean }` | +| `language.syntax.get` (response) | Viewer → Extension | Response | `SyntaxCacheFile` | | `language.syntax.change` | Viewer → Extension | Notification | `SyntaxChange` | | `script.compiled` | Viewer → Extension | Notification | `CompilationResult` | | `runtime.debug` | Viewer → Extension | Notification | `RuntimeDebug` | @@ -85,9 +94,9 @@ This document describes all the message interfaces defined in for WebSocket comm ### SessionHandshake -**JSON-RPC Method:** `session.handshake` (notification from viewer) +**JSON-RPC Method:** `session.handshake` (call from viewer) -The initial handshake message sent by the viewer to establish a connection. +The initial handshake call sent by the viewer to establish a session. ```typescript interface SessionHandshake { @@ -112,10 +121,13 @@ interface SessionHandshake { - `viewer_version`: Version string of the viewer - `agent_id`: Unique identifier for the user/agent - `agent_name`: Human-readable name of the agent -- `challenge` (optional): Security challenge string for authentication -- `languages`: Array of supported scripting languages (e.g., ["lsl", "luau"]) -- `syntax_id`: Current active syntax/language identifier -- `features`: Dictionary of feature flags indicating viewer capabilities +- `challenge` (optional): Path to a temporary file on the local filesystem containing a UUID. The client must read this file and return the UUID as `challenge_response` to authenticate the connection. +- `languages`: Array of supported scripting languages (e.g., `["lsl", "luau"]`) +- `syntax_id`: Current active syntax identifier as a UUID string +- `features`: Dictionary of feature flags indicating viewer capabilities. Known flags: + - `live_sync`: Viewer supports live script synchronisation with the external editor + - `compilation`: Viewer will forward compilation results via `script.compiled` + - `syntax__cache`: Viewer supports `language.syntax.cache` and `language.syntax.get` for retrieving syntax definition files ### SessionHandshakeResponse @@ -131,6 +143,8 @@ interface SessionHandshakeResponse { challenge_response?: string; languages: string[]; features: { [feature: string]: boolean }; + script_name?: string; + script_language?: string; } ``` @@ -139,15 +153,17 @@ interface SessionHandshakeResponse { - `client_name`: Name of the client (VS Code extension) - `client_version`: Fixed version "1.0" of the client - `protocol_version`: Protocol version the client supports -- `challenge_response` (optional): Response to the security challenge if provided +- `challenge_response` (optional): The UUID read from the temporary file identified by the `challenge` field in the handshake. Must be provided if `challenge` was present, otherwise the connection will be closed. - `languages`: Array of languages supported by the client - `features`: Dictionary of features supported by the client +- `script_name` (optional): Name of the script currently open in the editor +- `script_language` (optional): Language of the script currently open in the editor (e.g. `"lsl"`, `"luau"`) ### Session OK **JSON-RPC Method:** `session.ok` (notification from viewer) -Confirmation notification sent by the viewer after successful handshake completion. This interface has no defined structure as it appears to be a simple confirmation message. +Confirmation notification sent by the viewer after successful handshake completion. No parameters are sent with this notification. ### SessionDisconnect @@ -164,7 +180,12 @@ interface SessionDisconnect { **Fields:** -- `reason`: Numeric code indicating the reason for disconnection +- `reason`: Numeric code indicating the reason for disconnection: + - `0`: Normal closure + - `1`: Editor closed + - `2`: Protocol error + - `3`: Connection timeout + - `4`: Internal server error - `message`: Human-readable description of the disconnect reason ## Language and Syntax Interfaces @@ -183,7 +204,7 @@ interface SyntaxChange { **Fields:** -- `id`: Identifier for the new syntax/language +- `id`: UUID string identifying the new syntax version ### Language Syntax ID Request @@ -191,63 +212,130 @@ interface SyntaxChange { Requests the current active language syntax identifier from the viewer. This method takes no parameters. -**Response:** Returns an object with an `id` field containing the current syntax identifier. +**Response:** Returns `{ id: string }` where `id` is the current syntax version as a UUID string. ### Language Syntax Request **JSON-RPC Method:** `language.syntax` (call from extension to viewer) -Requests detailed syntax information for a specific language kind. +Requests the in-memory keyword definitions for a specific language. These definitions are the deserialized, viewer-processed form of the syntax data for the current region. **Parameters:** ```typescript { - kind: string; // The type of syntax information requested + kind: string; // The language whose definitions to retrieve } ``` -**Fields:** +**Valid `kind` values:** -- `kind`: The type of syntax information to retrieve (e.g., "functions", "constants", "events", "types.luau") +| Value | Description | +| ----------- | ----------------------------------------- | +| `"defs.lsl"` | Returns the LSL keyword definitions | +| `"defs.lua"` | Returns the Luau keyword definitions | -**Response:** Returns `LanguageInfo` data containing the requested syntax information: +**Response:** ```typescript interface LanguageInfo { id: string; - lslDefs?: { - controls?: any; - types?: any; - constants?: { [name: string]: ConstantDef }; - events?: { [name: string]: FunctionDef }; - functions?: { [name: string]: FunctionDef }; - }; - luaDefs?: { - modules?: { [name: string]: TypeDef }; - classes?: { [name: string]: TypeDef }; - aliases?: { [name: string]: TypeDef }; - functions?: { [name: string]: FunctionDef }; - }; + defs?: object; // Present only on success + success: boolean; + error?: string; // Present only on failure +} +``` + +**Response Fields:** + +- `id`: The current syntax version identifier +- `defs` (optional): The keyword definitions object. Only present when `success` is `true`. Structure varies by language. +- `success`: Whether the definitions were found and returned successfully +- `error` (optional): Human-readable error description. Only present when `success` is `false` + +**Error cases:** + +- No `kind` parameter supplied: `success: false`, `error: "No syntax category specified"` +- Unknown `kind` value: `success: false`, `error: "Unknown syntax category requested"` + +### Language Syntax Cache List + +**JSON-RPC Method:** `language.syntax.cache` (call from extension to viewer) + +Requests the list of file names currently held in the `LLSyntaxDefCache`. This provides the extension with the available syntax definition file names that can subsequently be retrieved with `language.syntax.get`. This method takes no parameters. + +**Response:** + +```typescript +interface SyntaxCacheList { + files: string[]; // Array of file names (e.g. ["lsl_keywords.xml", "slua_definitions.yaml"]) + success: boolean; } ``` **Response Fields:** -- `id`: Version identifier for the language syntax -- `lslDefs` (optional): LSL-specific language definitions containing: - - `controls` (optional): Control flow and language constructs - - `types` (optional): LSL type definitions - - `constants` (optional): Object containing constant definitions keyed by constant name - - `events` (optional): Object containing event definitions keyed by event name - - `functions` (optional): Object containing function definitions keyed by function name -- `luaDefs` (optional): Lua-specific language definitions containing: - - `modules` (optional): Module type definitions keyed by module name - - `classes` (optional): Class type definitions keyed by class name - - `aliases` (optional): Type alias definitions keyed by alias name - - `functions` (optional): Function definitions keyed by function name - -The specific sections returned depend on the `kind` parameter and the active language context. +- `files`: Array of file name strings, each of which can be passed as the `filename` parameter to `language.syntax.get` +- `success`: Whether the request was handled successfully + +**Known cache files:** + +| File name | Description | +| -------------------------------- | ---------------------------------------------------- | +| `builtins.txt` | LSL built-in keyword list in plain text format | +| `lsl_definitions.yaml` | LSL language definitions in YAML format | +| `lsl_keywords.xml` | LSL keyword definitions in LLSD XML format | +| `lsl_keywords_pretty.xml` | LSL keyword definitions in formatted LLSD XML format | +| `slua_default.d.luau` | Luau type definition file for editor tooling | +| `slua_default.docs.json` | Luau documentation data in JSON format | +| `slua_definitions.yaml` | Luau language definitions in YAML format | +| `slua_keywords.xml` | Luau keyword definitions in LLSD XML format | +| `slua_keywords_pretty.xml` | Luau keyword definitions in formatted LLSD XML format | +| `slua_selene.yml` | Luau Selene linter configuration in YAML format | + +Not all files may be present in every cache — the actual list returned by `language.syntax.cache` reflects only what is available on the viewer's local filesystem at the time of the request. + +### Language Syntax Cache Get + +**JSON-RPC Method:** `language.syntax.get` (call from extension to viewer) + +Requests the content of a specific file from the syntax definition cache. The file name must be one of the names returned by a prior `language.syntax.cache` call. Content is returned either as a raw text string or as a parsed JSON/LLSD object depending on the `as_json` parameter. + +**Parameters:** + +```typescript +{ + filename: string; // The file name to retrieve, as returned by language.syntax.cache + as_json?: boolean; // Optional. If true, content is returned as a parsed object rather than raw text +} +``` + +**Fields:** + +- `filename`: The file name to retrieve (e.g. `"lsl_keywords.xml"`, `"slua_definitions.yaml"`) +- `as_json` (optional): When `true`, the file is deserialized and returned as a structured object in `content`. When omitted or `false`, `content` is the raw text of the file. + +**Response:** + +```typescript +interface SyntaxCacheFile { + content?: string | object; // Present only on success. String if as_json is false/omitted, object if as_json is true + success: boolean; + error?: string; // Present only on failure +} +``` + +**Response Fields:** + +- `content`: The file content. Only present when `success` is `true`. Is a raw text string when `as_json` is omitted or `false`; is a parsed object when `as_json` is `true`. +- `success`: Whether the file was found and read successfully +- `error` (optional): Human-readable error description. Only present when `success` is `false` + +**Error cases:** + +- No `filename` parameter supplied: `success: false`, `error: "No filename specified"` +- Name not found in cache: `success: false`, `error: "Requested syntax cache file not found"` +- File could not be loaded: `success: false`, `error: "Failed to load syntax cache file"` (or `"Failed to load and format syntax cache file."` when `as_json` is `true`) ## Script Subscription Interfaces @@ -283,7 +371,6 @@ interface ScriptSubscribeResponse { success: boolean; status: number; object_id?: string; - object_name?: string; item_id?: string; message?: string; } @@ -293,9 +380,14 @@ interface ScriptSubscribeResponse { - `script_id`: The script identifier that was subscribed to - `success`: Whether the subscription was successful -- `status`: Numeric status code indicating the result -- `object_id` (optional): The in-world ID of the object containing the script -- `object_name` (optional): The name of the object containing the script. +- `status`: Numeric status code indicating the result: + - `0`: Success + - `1`: Invalid editor — the script editor panel is no longer open + - `2`: Invalid subscription — no subscription found for the given `script_id` + - `3`: Already subscribed — another connection is already subscribed to this script + - `4`: Internal server error +- `object_id` (optional): The in-world UUID of the object containing the script +- `item_id` (optional): The inventory item UUID of the script within the object - `message` (optional): Additional information about the subscription result ### ScriptUnsubscribe @@ -314,6 +406,28 @@ interface ScriptUnsubscribe { - `script_id`: Unique identifier for the script to unsubscribe from +### ScriptList + +**JSON-RPC Method:** `script.list` (call from extension to viewer) + +Requests the list of all scripts currently open and tracked by the viewer, along with the viewer's temp directory. This is intended for use by a file watcher tool that needs to discover which script temp files are active without going through the full `script.subscribe` flow. This method takes no parameters. + +**Response:** + +```typescript +interface ScriptList { + temp_dir: string; + script_ids: string[]; + success: boolean; +} +``` + +**Response Fields:** + +- `temp_dir`: The absolute path to the viewer's temp directory where live-sync script files are written. Combined with a `script_id`, the caller can locate the corresponding temp file on disk. +- `script_ids`: Array of script ID strings for all currently subscribed scripts, across all active connections. +- `success`: Always `true`. + ## Compilation Interfaces ### CompilationError @@ -324,17 +438,19 @@ Individual compilation error record. interface CompilationError { row: number; column: number; - level: "ERROR"; + level: string; message: string; + format?: "lsl"; // Present only for LSL compilation errors } ``` **Fields:** -- `row`: Line number where the error occurred (0-based or 1-based depending on context) -- `column`: Column position of the error -- `level`: Severity level (currently only "ERROR" is defined) +- `row`: Line number where the error occurred (1-based for both LSL and Luau) +- `column`: Column position of the error (1-based for LSL; always `0` for Luau as the compiler does not provide column information) +- `level`: Compiler severity string (e.g. `"ERROR"`, `"WARNING"`) - `message`: Error description +- `format` (optional): Present and set to `"lsl"` for LSL compilation errors; absent for Luau errors ### CompilationResult @@ -405,10 +521,10 @@ interface RuntimeError { - `script_id`: Unique identifier for the script that encountered the error - `object_id`: Unique identifier for the object containing the script - `object_name`: Human-readable name of the object -- `message`: Error message description -- `error`: Specific error type or code -- `line`: Line number where the error occurred -- `stack` (optional): Stack trace information if available +- `message`: The full raw chat text of the runtime error message as received from the simulator +- `error`: Extracted error description. Currently always an empty string — runtime error extraction from the simulator's multi-message format is not yet fully implemented. +- `line`: Line number where the error occurred. Currently always `0` for the same reason. +- `stack` (optional): Stack trace lines if they could be extracted from the error message ## Handler and Configuration Interfaces @@ -436,7 +552,7 @@ interface WebSocketHandlers { - `onHandshake`: Handler for initial handshake message, returns handshake response - `onHandshakeOk`: Handler called when handshake is successfully completed - `onDisconnect`: Handler for disconnect notifications -- `onSubscribe`: Handler for script subscription requests from viewer, returns subscription response +- `onSubscribe`: Handler called when the extension sends a `script.subscribe` request, returns subscription response - `onUnsubscribe`: Handler for script unsubscription notifications from viewer - `onSyntaxChange`: Handler for syntax change notifications - `onConnectionClosed`: Handler called when connection is closed diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp index 11aacd791e..3ca9be44bc 100644 --- a/indra/newview/llscripteditorws.cpp +++ b/indra/newview/llscripteditorws.cpp @@ -2,6 +2,9 @@ * @file llscripteditorws.cpp * @brief JSON-RPC 2.0 WebSocket server implementation for external script editor integration * + * For a full description of the JSON-RPC protocol and all supported methods, + * see doc/external-editor-json-rpc.md in the repository root. + * * $LicenseInfo:firstyear=2025&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2025, Linden Research, Inc. @@ -272,6 +275,26 @@ void LLScriptEditorWSServer::setupConnectionMethods(LLJSONRPCConnection::ptr_t c } return LLSD(); }); + script_connection->registerMethod("language.syntax.cache", + [that](const std::string&, const LLSD&, const LLSD& params) + { + auto server = that.lock(); + if (server) + { + return server->handleSyntaxCacheRequest(); + } + return LLSD(); + }); + script_connection->registerMethod("language.syntax.get", + [that](const std::string&, const LLSD&, const LLSD& params) + { + auto server = that.lock(); + if (server) + { + return server->handleSyntaxCacheFileRequest(params); + } + return LLSD(); + }); script_connection->registerMethod("script.subscribe", [that, connection_id](const std::string&, const LLSD&, const LLSD& params) -> LLSD { @@ -356,6 +379,71 @@ LLSD LLScriptEditorWSServer::handleSyntaxRequest(const LLSD& params) const return response; } +LLSD LLScriptEditorWSServer::handleSyntaxCacheRequest() const +{ + LLSD response; + // Add array of cached syntax definition files + LLSD syntax_files = LLSD::emptyArray(); + for (const auto& name : LLSyntaxDefCache::instance().getCacheFileNames()) + { + syntax_files.append(name); + } + response["files"] = syntax_files; + response["success"] = true; + return response; +} + +LLSD LLScriptEditorWSServer::handleSyntaxCacheFileRequest(const LLSD& params) const +{ + std::string filename = params["filename"].asString(); + bool as_json = params["as_json"].asBoolean(); + + LLSyntaxDefCache& cache = LLSyntaxDefCache::instance(); + LLSD response; + + if (filename.empty()) + { + response["error"] = "No filename specified"; + response["success"] = false; + return response; + } + if (!cache.hasCacheFile(filename)) + { + response["error"] = "Requested syntax cache file not found"; + response["success"] = false; + return response; + } + bool success = false; + if (as_json) + { + LLSD file_content = cache.loadCacheFileAsLLSD(filename); + if (file_content.isDefined()) + { + response["content"] = file_content; + success = true; + } + else + { + response["error"] = "Failed to load and format syntax cache file."; + } + } + else + { + std::string content = cache.loadCacheFile(filename); + if (!content.empty()) + { + response["content"] = content; + success = true; + } + else + { + response["error"] = "Failed to load syntax cache file"; + } + } + response["success"] = success; + return response; +} + LLSD LLScriptEditorWSServer::handleScriptSubscribe(U32 connection_id, const LLSD& params) { LLSD response(LLSD::emptyMap()); @@ -716,6 +804,7 @@ void LLScriptEditorWSConnection::onOpen() LLSD features; features["live_sync"] = true; features["compilation"] = true; + features["syntax_cache"] = true; handshake["features"] = features; wptr_t that = shared_from_this(); diff --git a/indra/newview/llscripteditorws.h b/indra/newview/llscripteditorws.h index c8898c66a7..765ebe83f2 100644 --- a/indra/newview/llscripteditorws.h +++ b/indra/newview/llscripteditorws.h @@ -192,6 +192,8 @@ protected: LLSD handleLanguageIdRequest() const; LLSD handleSyntaxRequest(const LLSD ¶ms) const; + LLSD handleSyntaxCacheRequest() const; + LLSD handleSyntaxCacheFileRequest(const LLSD& params) const; LLSD handleScriptSubscribe(U32 connection_id, const LLSD& params); LLSD handleScriptUnsubscribe(U32 connection_id, const LLSD& params); LLSD handleFileWatcherFileListRequest() const; diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 3d0a9d80b4..3e6fc2d217 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -506,6 +506,16 @@ void LLSyntaxDefCache::loadKeywordsIntoLLSD() mSyntaxIDChangedSignal(); } +std::vector LLSyntaxDefCache::getCacheFileNames() const +{ + std::vector names; + for (const auto& [name, path] : mFileCachePaths) + { + names.push_back(name); + } + return names; +} + LLSD LLSyntaxDefCache::loadDeserializedCacheFile(const std::string& file_path) { std::ifstream file(file_path.c_str()); @@ -524,3 +534,32 @@ LLSD LLSyntaxDefCache::loadDeserializedCacheFile(const std::string& file_path) return LLSD(); } +std::string LLSyntaxDefCache::loadCacheFile(const std::string& name) const +{ + std::string full_path = mFileCachePaths.getPath(name); + if (!full_path.empty()) + { + std::ifstream file(full_path.c_str()); + if (file.good()) + { + std::ostringstream ss; + ss << file.rdbuf(); + return (file.good()) ? ss.str() : std::string(); + } + else + { + LL_WARNS("SyntaxLSL") << "Failed to open cached file: " << full_path << LL_ENDL; + } + } + return std::string(); +} + +LLSD LLSyntaxDefCache::loadCacheFileAsLLSD(const std::string& name) const +{ + std::string full_path = mFileCachePaths.getPath(name); + if (!full_path.empty()) + { + return loadDeserializedCacheFile(full_path); + } + return LLSD(); +} diff --git a/indra/newview/llsyntaxid.h b/indra/newview/llsyntaxid.h index 3bd49378b5..b38d02c10f 100644 --- a/indra/newview/llsyntaxid.h +++ b/indra/newview/llsyntaxid.h @@ -80,6 +80,9 @@ public: void clear() { mNamePathMap.clear(); } bool empty() const { return mNamePathMap.empty(); } + iterator find(const std::string &name) { return mNamePathMap.find(name); } + const_iterator find(const std::string &name) const { return mNamePathMap.find(name); } + private: name_path_map_t mNamePathMap; }; @@ -94,6 +97,21 @@ public: static std::string buildCacheDirectoryName(const LLUUID& syntax_id); + using const_iterator = cache_names_t::const_iterator; + + const_iterator begin() const { return mFileCachePaths.begin(); } + const_iterator end() const { return mFileCachePaths.end(); } + + size_t size() const { return mFileCachePaths.size(); } + bool empty() const { return mFileCachePaths.empty(); } + + const_iterator find(const std::string& name) const { return mFileCachePaths.find(name); } + + std::vector getCacheFileNames() const; + bool hasCacheFile(const std::string& name) const { return mFileCachePaths.hasName(name); } + std::string loadCacheFile(const std::string& name) const; + LLSD loadCacheFileAsLLSD(const std::string& name) const; + protected: void initSingleton() override; void cleanupSingleton() override; -- cgit v1.3 From 89a568167f419b34a7b61ca126c378cbb387bc45 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 29 Apr 2026 15:09:53 -0700 Subject: Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- doc/external-editor-json-rpc.md | 2 +- indra/newview/llsyntaxid.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/doc/external-editor-json-rpc.md b/doc/external-editor-json-rpc.md index 8d657b0d33..202f1e5b29 100644 --- a/doc/external-editor-json-rpc.md +++ b/doc/external-editor-json-rpc.md @@ -127,7 +127,7 @@ interface SessionHandshake { - `features`: Dictionary of feature flags indicating viewer capabilities. Known flags: - `live_sync`: Viewer supports live script synchronisation with the external editor - `compilation`: Viewer will forward compilation results via `script.compiled` - - `syntax__cache`: Viewer supports `language.syntax.cache` and `language.syntax.get` for retrieving syntax definition files + - `syntax_cache`: Viewer supports `language.syntax.cache` and `language.syntax.get` for retrieving syntax definition files ### SessionHandshakeResponse diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 3e6fc2d217..0ded401d3e 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -411,14 +411,14 @@ std::string LLSyntaxDefCache::buildCacheDirectoryName(const LLUUID& syntax_id) if (syntax_id.isNull()) { LL_DEBUGS("SyntaxLSL") << "No SyntaxID, using app settings directory." << LL_ENDL; - return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "syntax_default"); + return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "syntax_default"); } else { LL_DEBUGS("SyntaxLSL") << "Using cache directory for SyntaxID '" << syntax_id << "'." << LL_ENDL; std::string cache_dir_name = "syntax_" + syntax_id.asString(); - return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, cache_dir_name); + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); } } -- cgit v1.3 From 0f8be801753ade9db2c0dad66d124d71f7314b2d Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 29 Apr 2026 15:22:50 -0700 Subject: trigger the pre-commit. --- indra/newview/llsyntaxid.cpp | 3 ++- indra/newview/llsyntaxid.h | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 0ded401d3e..ad10cee3dd 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -35,6 +35,7 @@ #include "llviewerregion.h" #include "llcorehttputil.h" + //----------------------------------------------------------------------------- // LLSyntaxIdLSL //----------------------------------------------------------------------------- @@ -226,7 +227,7 @@ void LLSyntaxDefCache::fetchKeywordsFileCoro(std::string url, LLUUID syntax_id) // to the default versions, so below, where we get the path to the lua keywords // well be loading the default version. buildDefaultCache(); - + // The LSL keywords we just received std::string full_path = gDirUtilp->add(path, FILENAME_INTERNAL_LSL); if (writeCacheFile(full_path, result)) diff --git a/indra/newview/llsyntaxid.h b/indra/newview/llsyntaxid.h index b38d02c10f..acf8020d58 100644 --- a/indra/newview/llsyntaxid.h +++ b/indra/newview/llsyntaxid.h @@ -25,8 +25,7 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -#ifndef LL_SYNTAXID_H -#define LL_SYNTAXID_H +#pragma once #include "llviewerprecompiledheaders.h" @@ -122,7 +121,7 @@ private: static bool isSupportedVersion(const LLSD& content); void handleRegionChanged(); void handleCapsReceived(const LLUUID& region_uuid); - void setKeywords(const LLSD& lsl, const LLSD& lua) { mLSLKeywords = lsl; mLuaKeywords = lua; }; + void setKeywords(const LLSD& lsl, const LLSD& lua) { mLSLKeywords = lsl; mLuaKeywords = lua; }; void loadKeywordsIntoLLSD(); @@ -148,5 +147,3 @@ private: LLSD mLuaKeywords; bool mUseDefsCap{ false }; }; - -#endif // LLSYNTAXID_H -- cgit v1.3 From e932b04e87cefd922800c9ca24382f9a9c461b2d Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 29 Apr 2026 15:36:21 -0700 Subject: Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- indra/newview/llsyntaxid.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index ad10cee3dd..6670809c85 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -167,19 +167,19 @@ void LLSyntaxDefCache::fetchKeywords() } if (mUseDefsCap) { - LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsDefsCoro", + LLCoros::instance().launch("LLSyntaxDefCache::fetchKeywordsDefsCoro", boost::bind(&LLSyntaxDefCache::fetchKeywordsDefsCoro, this, mCapabilityURL, mSyntaxId)); } else { - LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsFileCoro", + LLCoros::instance().launch("LLSyntaxDefCache::fetchKeywordsFileCoro", boost::bind(&LLSyntaxDefCache::fetchKeywordsFileCoro, this, mCapabilityURL, mSyntaxId)); } } //----------------------------------------------------------------------------- // fetchKeywordsFileCoro -// This uses the legacy languge cap which only sends the LSL keywords file. +// This uses the legacy language cap which only sends the LSL keywords file. void LLSyntaxDefCache::fetchKeywordsFileCoro(std::string url, LLUUID syntax_id) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -324,7 +324,7 @@ void LLSyntaxDefCache::fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id) result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - setKeywords(memcached_keywords["lsl_keywords.xml"], memcached_keywords["lua_keywords.xml"]); + setKeywords(memcached_keywords[FILENAME_INTERNAL_LSL], memcached_keywords[FILENAME_INTERNAL_LUA]); LLAppViewer::instance()->postToMainCoro( [this]() { @@ -363,8 +363,10 @@ void LLSyntaxDefCache::buildCachePaths(const LLUUID &syntax_id) bool LLSyntaxDefCache::writeCacheFile(const std::string &fileSpec, const LLSD& content_ref) { - bool binary(content_ref.isBinary()); - std::ofstream file(fileSpec.c_str(), (binary)? std::ios_base::binary : 0); + bool binary(content_ref.isBinary()); + std::ios_base::openmode mode(binary ? (std::ios_base::out | std::ios_base::binary) + : std::ios_base::out); + std::ofstream file(fileSpec.c_str(), mode); if (!file.is_open()) { -- cgit v1.3 From a453db20bb49193806fb0cafeb4d50db4a16abe4 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 5 May 2026 11:05:11 -0700 Subject: Update to new lsl-definitions --- autobuild.xml | 88 ++++++++++++++++++++-------------------- indra/cmake/LSLDefinitions.cmake | 4 +- indra/newview/llsyntaxid.cpp | 2 +- 3 files changed, 47 insertions(+), 47 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/autobuild.xml b/autobuild.xml index feca45d6d8..3f07900e58 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1326,18 +1326,6 @@ lsl_definitions - canonical_repo - https://github.com/secondlife/lsl-definitions - copyright - Copyright (c) 2026, Linden Lab - description - LSL definitions - license - MIT - license_file - LICENSES/lsl_definitions.txt - name - lsl_definitions platforms common @@ -1345,28 +1333,40 @@ archive hash - 6e4e3b86f5daf6dc2e7d7b2b59deaaad293b7232 + 9e8a23b240897ca2e98f6700fae3bec46ad0d0f2 hash_algorithm sha1 url - https://github.com/secondlife/lsl-definitions/releases/download/v0.4.1/lsl_definitions-0.4.1-common-22871771849.tar.zst + https://github.com/secondlife/lsl-definitions/releases/download/v0.6.3/lsl_definitions-0.6.3-common-25336758775.tar.zst name common - source_type - git + license + MIT + license_file + LICENSES/lsl_definitions.txt + copyright + Copyright (c) 2026, Linden Lab + version + 0.6.3 use_scm_version true + name + lsl_definitions vcs_branch refs/tags/v0.4.1 vcs_revision 03006fb488cba2bae502e9baed051bfc8d00144d vcs_url git://github.com/secondlife/lsl-definitions.git - version - 0.4.1 + canonical_repo + https://github.com/secondlife/lsl-definitions + description + LSL definitions + source_type + git meshoptimizer @@ -2473,16 +2473,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors webrtc - canonical_repo - https://github.com/secondlife/3p-webrtc-build - copyright - Copyright (c) 2011, The WebRTC project authors. All rights reserved. - license - MIT - license_file - LICENSES/webrtc-license.txt - name - webrtc platforms darwin64 @@ -2528,14 +2518,24 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors windows64 + license + MIT + license_file + LICENSES/webrtc-license.txt + copyright + Copyright (c) 2011, The WebRTC project authors. All rights reserved. + version + m137.7151.04.23.22004231636 + name + webrtc vcs_branch secondlife vcs_revision d3f62d32bac8694d3c7423c731ae30c113bf6a11 vcs_url https://github.com/secondlife/3p-webrtc-build - version - m137.7151.04.23.22004231636 + canonical_repo + https://github.com/secondlife/3p-webrtc-build xxhash @@ -2813,18 +2813,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors websocketpp - canonical_repo - https://github.com/secondlife/3p-websocketpp - copyright - Copyright (c) 2014, Peter Thorson - description - WebSocket++ is a C++ header only library for interacting with WebSocket servers and clients. - license - websocketpp - license_file - LICENSES/websocketpp.txt - name - websocketpp platforms common @@ -2842,14 +2830,26 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors common + license + websocketpp + license_file + LICENSES/websocketpp.txt + copyright + Copyright (c) 2014, Peter Thorson + version + 0.8.2.24525603568 + name + websocketpp vcs_branch refs/tags/v0.8.2 vcs_revision bdcf1453101976fc4dc26a62c87bc98c12e9c6dc vcs_url git://github.com/secondlife/3p-websocketpp.git - version - 0.8.2.24525603568 + canonical_repo + https://github.com/secondlife/3p-websocketpp + description + WebSocket++ is a C++ header only library for interacting with WebSocket servers and clients. package_description diff --git a/indra/cmake/LSLDefinitions.cmake b/indra/cmake/LSLDefinitions.cmake index ae0eeebb7e..1a9a477118 100644 --- a/indra/cmake/LSLDefinitions.cmake +++ b/indra/cmake/LSLDefinitions.cmake @@ -3,7 +3,7 @@ include(Prebuilt) use_prebuilt_binary(lsl_definitions) -configure_file("${AUTOBUILD_INSTALL_DIR}/lsl_definitions/lsl_keywords_pretty.xml" +configure_file("${AUTOBUILD_INSTALL_DIR}/lsl_definitions/lsl_keywords.xml" "${CMAKE_SOURCE_DIR}/newview/app_settings/keywords_lsl_default.xml" COPYONLY) -configure_file("${AUTOBUILD_INSTALL_DIR}/lsl_definitions/slua_keywords_pretty.xml" +configure_file("${AUTOBUILD_INSTALL_DIR}/lsl_definitions/lua_keywords.xml" "${CMAKE_SOURCE_DIR}/newview/app_settings/keywords_lua_default.xml" COPYONLY) diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 6670809c85..9c1a0ef403 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -45,7 +45,7 @@ namespace const std::string SYNTAX_DEF_CAPABILITY_NAME = "ScriptDefinitions"; const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; const std::string FILENAME_INTERNAL_LSL = "lsl_keywords.xml"; - const std::string FILENAME_INTERNAL_LUA = "slua_keywords.xml"; + const std::string FILENAME_INTERNAL_LUA = "lua_keywords.xml"; constexpr U32 LLSD_SYNTAX_LSL_VERSION_EXPECTED = 2; const std::string LLSD_SYNTAX_LSL_VERSION_KEY("llsd-lsl-syntax-version"); -- cgit v1.3 From 37152df826133d38cbd337ddcf4b4307490c486b Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Thu, 20 Aug 2026 20:08:45 +0300 Subject: #6162 Preserve script keyword defaults when region omits definitions --- indra/newview/llsyntaxid.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'indra/newview/llsyntaxid.cpp') diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 9c1a0ef403..58d1287979 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -46,6 +46,8 @@ namespace const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; const std::string FILENAME_INTERNAL_LSL = "lsl_keywords.xml"; const std::string FILENAME_INTERNAL_LUA = "lua_keywords.xml"; + const std::string FILENAME_DEFAULT_LSL = "keywords_lsl_default.xml"; + const std::string FILENAME_DEFAULT_LUA = "keywords_lua_default.xml"; constexpr U32 LLSD_SYNTAX_LSL_VERSION_EXPECTED = 2; const std::string LLSD_SYNTAX_LSL_VERSION_KEY("llsd-lsl-syntax-version"); @@ -285,7 +287,10 @@ void LLSyntaxDefCache::fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id) // Now we need to walk through the returned LLSD. It consists of a map keyed on the file name containing a binary // blob of the actual file contents. LLSD files = result["files"]; - LLSD memcached_keywords; + // A region may omit definitions for languages it does not support. Keep + // the defaults/current definitions for any omitted file. + LLSD lsl_keywords = mLSLKeywords; + LLSD lua_keywords = mLuaKeywords; if (files.isMap()) { std::string path = buildCacheDirectoryName(syntax_id); @@ -306,9 +311,13 @@ void LLSyntaxDefCache::fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id) { std::string full_path = gDirUtilp->add(path, filename); - if (MEMCACHED_LLSD.find(filename) != MEMCACHED_LLSD.end()) - { // Maintain some keyword LLSDs internally, LSL and Lua - memcached_keywords[filename] = contents; + if (filename == FILENAME_INTERNAL_LSL) + { + lsl_keywords = contents; + } + else if (filename == FILENAME_INTERNAL_LUA) + { + lua_keywords = contents; } if (writeCacheFile(full_path, contents)) @@ -324,7 +333,7 @@ void LLSyntaxDefCache::fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id) result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - setKeywords(memcached_keywords[FILENAME_INTERNAL_LSL], memcached_keywords[FILENAME_INTERNAL_LUA]); + setKeywords(lsl_keywords, lua_keywords); LLAppViewer::instance()->postToMainCoro( [this]() { @@ -345,6 +354,13 @@ void LLSyntaxDefCache::buildCachePaths(const LLUUID &syntax_id) else { mFileCachePaths.clear(); + mFileCachePaths.addNamePath( + FILENAME_INTERNAL_LSL, + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, FILENAME_DEFAULT_LSL)); + mFileCachePaths.addNamePath( + FILENAME_INTERNAL_LUA, + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, FILENAME_DEFAULT_LUA)); + return; } std::string cache_dir = buildCacheDirectoryName(syntax_id); -- cgit v1.3