diff options
| author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2026-04-01 19:50:33 +0300 |
|---|---|---|
| committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2026-04-01 19:50:33 +0300 |
| commit | f5908eddc683dd705d334c88377139e39bbfb54f (patch) | |
| tree | 75c0ff026fe13c0a507a4527b697aa212736998d /indra/newview/llpreviewscript.cpp | |
| parent | d045f9aa32789a35628aced166145c182751acbf (diff) | |
| parent | f209affdfdf271098843ba400d7a75b00f827233 (diff) | |
Merge branch 'project/lua_editor' into marchcat/devlinux-slua
# Conflicts:
# indra/llcommon/llstl.h
# indra/newview/app_settings/settings.xml
Diffstat (limited to 'indra/newview/llpreviewscript.cpp')
| -rw-r--r-- | indra/newview/llpreviewscript.cpp | 614 |
1 files changed, 437 insertions, 177 deletions
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 53d0a712a8..31909116c7 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -53,6 +53,7 @@ #include "llslider.h" #include "lltooldraganddrop.h" #include "llfilesystem.h" +#include "lllogchat.h" #include "llagent.h" #include "llmenugl.h" @@ -64,22 +65,12 @@ #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" -#include "llkeyboard.h" -#include "llscrollcontainer.h" -#include "llcheckboxctrl.h" #include "llscripteditor.h" -#include "llselectmgr.h" -#include "lltooldraganddrop.h" -#include "llscrolllistctrl.h" #include "lltextbox.h" -#include "llslider.h" -#include "lldir.h" -#include "llcombobox.h" #include "llviewerstats.h" #include "llviewerwindow.h" #include "lluictrlfactory.h" #include "llmediactrl.h" -#include "lluictrlfactory.h" #include "lltrans.h" #include "llviewercontrol.h" #include "llappviewer.h" @@ -90,20 +81,10 @@ #include "lltoggleablemenu.h" #include "llmenubutton.h" #include "llinventoryfunctions.h" +#include "llwebsocketmgr.h" +#include "llscripteditorws.h" +#include <regex> -const std::string HELLO_LSL = - "default\n" - "{\n" - " state_entry()\n" - " {\n" - " llSay(0, \"Hello, Avatar!\");\n" - " }\n" - "\n" - " touch_start(integer total_number)\n" - " {\n" - " llSay(0, \"Touched.\");\n" - " }\n" - "}\n"; const std::string HELP_LSL_PORTAL_TOPIC = "LSL_Portal"; const std::string DEFAULT_SCRIPT_NAME = "New Script"; // *TODO:Translate? @@ -119,6 +100,42 @@ static bool have_script_upload_cap(LLUUID& object_id) return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty()); } +static bool have_lua_enabled(const LLUUID& object_id) +{ + LLViewerRegion* region = nullptr; + LLViewerObject* object = gObjectList.findObject(object_id); + if (object) + { + region = object->getRegion(); + } + else + { + region = gAgent.getRegion(); + } + + if (region && region->simulatorFeaturesReceived()) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + return simulatorFeatures["LuaScriptsEnabled"].asBoolean(); + } + + return false; +} + +// TEMPORARY: Quick check to see if the code is Lua +// since we don't have another way to determine the language yet +bool is_lua_script(const std::string& code) +{ + // Check for LSL's signature "default" state pattern + std::regex lsl_pattern("\\s*default\\s*\\{"); + if (std::regex_search(code, lsl_pattern)) + return false; + + // "default" state not found, assuming it's Lua + return true; +} + /// --------------------------------------------------------------------------- /// LLLiveLSLFile /// --------------------------------------------------------------------------- @@ -373,9 +390,9 @@ LLScriptEdCore::LLScriptEdCore( LLScriptEdContainer* container, const std::string& sample, const LLHandle<LLFloater>& floater_handle, - void (*load_callback)(void*), - void (*save_callback)(void*, bool), - void (*search_replace_callback) (void* userdata), + script_ed_callback_t load_callback, + save_callback_t save_callback, + script_ed_callback_t search_replace_callback, void* userdata, bool live, S32 bottom_pad) @@ -391,7 +408,6 @@ LLScriptEdCore::LLScriptEdCore( mLastHelpToken(NULL), mLiveHelpHistorySize(0), mEnableSave(false), - mLiveFile(NULL), mLive(live), mContainer(container), mHasScriptData(false), @@ -423,7 +439,6 @@ LLScriptEdCore::~LLScriptEdCore() } } - delete mLiveFile; if (mSyntaxIDConnection.connected()) { mSyntaxIDConnection.disconnect(); @@ -441,42 +456,37 @@ void LLLiveLSLEditor::experienceChanged() } } -void LLLiveLSLEditor::onViewProfile( LLUICtrl *ui, void* userdata ) +void LLLiveLSLEditor::onViewProfile() { - LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; - - LLUUID id; - if(self->mExperienceEnabled->get()) + if (mExperienceEnabled->get()) { - id=self->mScriptEd->getAssociatedExperience(); - if(id.notNull()) + LLUUID id = mScriptEd->getAssociatedExperience(); + if (id.notNull()) { LLFloaterReg::showInstance("experience_profile", id, true); } } - } -void LLLiveLSLEditor::onToggleExperience( LLUICtrl *ui, void* userdata ) +void LLLiveLSLEditor::onToggleExperience() { - LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; - LLUUID id; - if(self->mExperienceEnabled->get()) + if (mExperienceEnabled->get()) { - if(self->mScriptEd->getAssociatedExperience().isNull()) + if (mScriptEd->getAssociatedExperience().isNull()) { - id=self->mExperienceIds.beginArray()->asUUID(); + id = mExperienceIds.beginArray()->asUUID(); } } - if(id != self->mScriptEd->getAssociatedExperience()) + if (id != mScriptEd->getAssociatedExperience()) { - self->mScriptEd->enableSave(self->getIsModifiable()); + mScriptEd->enableSave(getIsModifiable()); } - self->mScriptEd->setAssociatedExperience(id); - self->updateExperiencePanel(); + mScriptEd->setAssociatedExperience(id); + + updateExperiencePanel(); } bool LLScriptEdCore::postBuild() @@ -501,6 +511,7 @@ bool LLScriptEdCore::postBuild() // Intialise keyword highlighting for the current simulator's version of LSL LLSyntaxIdLSL::getInstance()->initialize(); + LLSyntaxLua::getInstance()->initialize(); processKeywords(); mCommitCallbackRegistrar.add("FontSize.Set", boost::bind(&LLScriptEdCore::onChangeFontSize, this, _2)); @@ -510,14 +521,30 @@ bool LLScriptEdCore::postBuild() "menu_lsl_font_size.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); getChild<LLMenuButton>("font_btn")->setMenu(context_menu, LLMenuButton::MP_BOTTOM_LEFT, true); + bool lua_scripts_enabled = have_lua_enabled(LLUUID::null); + mCompileTarget = getChild<LLComboBox>("compile_target"); + if (LLScrollListItem* luau_item = mCompileTarget->findItemByValue("luau")) + { + luau_item->setEnabled(lua_scripts_enabled); + } + if (LLScrollListItem* lsl_luau_item = mCompileTarget->findItemByValue("lsl-luau")) + { + lsl_luau_item->setEnabled(lua_scripts_enabled); + } + return true; } void LLScriptEdCore::processKeywords() { + processKeywords(mEditor->getIsLuauLanguage()); +} + +void LLScriptEdCore::processKeywords(bool luau_language) +{ LL_DEBUGS("SyntaxLSL") << "Processing keywords" << LL_ENDL; mEditor->clearSegments(); - mEditor->initKeywords(); + mEditor->initKeywords(luau_language); mEditor->loadKeywords(); string_vec_t primary_keywords; @@ -536,6 +563,8 @@ void LLScriptEdCore::processKeywords() secondary_keywords.push_back( wstring_to_utf8str(token->getToken()) ); } } + + mFunctions->removeall(); for (string_vec_t::const_iterator iter = primary_keywords.begin(); iter!= primary_keywords.end(); ++iter) { @@ -691,12 +720,13 @@ bool LLScriptEdCore::writeToFile(const std::string& filename) void LLScriptEdCore::sync() { // Sync with external editor. - if (mLiveFile) + if (mContainer->mLiveFile) { - std::string tmp_file = mLiveFile->filename(); - if (LLFile::isfile(tmp_file)) // file exists + std::string tmp_file = mContainer->mLiveFile->filename(); + llstat s; + if (LLFile::stat(tmp_file, &s) == 0) // file exists { - mLiveFile->ignoreNextUpdate(); + mContainer->mLiveFile->ignoreNextUpdate(); writeToFile(tmp_file); } } @@ -720,6 +750,8 @@ void LLScriptEdCore::draw() S32 line = 0; S32 column = 0; mEditor->getCurrentLineAndColumn( &line, &column, false ); // don't include wordwrap + line = mEditor->getIsLuauLanguage() ? (line + 1) : line; + column = mEditor->getIsLuauLanguage() ? (column + 1) : column; LLStringUtil::format_map_t args; std::string cursor_pos; args["[LINE]"] = llformat ("%d", line); @@ -1082,15 +1114,21 @@ void LLScriptEdCore::doSave( bool close_after_save ) void LLScriptEdCore::openInExternalEditor() { - delete mLiveFile; // deletes file + //if (mContainer->mLiveFile) + //{ + // // If already open in an external editor, just return + // return; + //} // Generate a suitable filename std::string script_name = mScriptName; - std::string forbidden_chars = "<>:\"\\/|?*"; - for (std::string::iterator c = forbidden_chars.begin(); c != forbidden_chars.end(); c++) - { - script_name.erase(std::remove(script_name.begin(), script_name.end(), *c), script_name.end()); - } + + static const std::set<char> forbidden_chars{ '<', '>', ':', '"', '\\', '/', '|', '?', '*' }; + script_name.erase( + std::remove_if(script_name.begin(), script_name.end(), [](char c) { + return forbidden_chars.contains(c); + }), script_name.end()); + std::string filename = mContainer->getTmpFileName(script_name); // Save the script to a temporary file. @@ -1104,9 +1142,18 @@ void LLScriptEdCore::openInExternalEditor() writeToFile(filename); } + if (mContainer->mLiveFile && mContainer->mLiveFile->filename() != filename) + { // The name may have changed if we changed the type of scipt being edited. + delete mContainer->mLiveFile; + mContainer->mLiveFile = NULL; + } // Start watching file changes. - mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1)); - mLiveFile->addToEventTimer(); + if (!mContainer->mLiveFile) + { + mContainer->mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1)); + mContainer->mLiveFile->addToEventTimer(); + } + mContainer->startWebsocketServer(); // Open it in external editor. { @@ -1165,6 +1212,7 @@ void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) sscanf(line.c_str(), "%d %d", &row, &column); //LL_INFOS() << "LLScriptEdCore::onErrorList() - " << row << ", " //<< column << LL_ENDL; + row = (self->mEditor->getIsLuauLanguage() ? row - 1 : row); self->mEditor->setCursor(row, column); self->mEditor->setFocus(true); } @@ -1375,15 +1423,15 @@ void LLLiveLSLEditor::updateExperiencePanel() mExperienceEnabled->setEnabled(false); mExperienceEnabled->setToolTip(getString("no_experiences")); } - getChild<LLButton>("view_profile")->setVisible(false); + mViewProfileButton->setVisible(false); } else { mExperienceEnabled->setToolTip(getString("experience_enabled")); mExperienceEnabled->setEnabled(getIsModifiable()); - mExperiences->setVisible(true); mExperienceEnabled->set(true); - getChild<LLButton>("view_profile")->setToolTip(getString("show_experience_profile")); + mExperiences->setVisible(true); + mViewProfileButton->setToolTip(getString("show_experience_profile")); buildExperienceList(); } } @@ -1452,7 +1500,7 @@ void LLLiveLSLEditor::buildExperienceList() mExperiences->setEnabled(true); mExperiences->sortByName(true); mExperiences->setCurrentByIndex(mExperiences->getCurrentIndex()); - getChild<LLButton>("view_profile")->setVisible(true); + mViewProfileButton->setVisible(true); } } @@ -1462,8 +1510,6 @@ void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) mAssociatedExperience = experience_id; } - - void LLLiveLSLEditor::requestExperiences() { if (!getIsModifiable()) @@ -1502,11 +1548,42 @@ void LLLiveLSLEditor::receiveExperienceIds(LLSD result, LLHandle<LLLiveLSLEditor LLScriptEdContainer::LLScriptEdContainer(const LLSD& key) : LLPreview(key) -, mScriptEd(NULL) +, mScriptEd(nullptr) +, mLiveFile(nullptr) +, mLiveLogFile(nullptr) +{ +} + +LLScriptEdContainer::~LLScriptEdContainer() { + delete mLiveFile; + mLiveFile = nullptr; + + delete mLiveLogFile; + mLiveLogFile = nullptr; + + if (!mWebSocketServer.expired()) + { + unsubscribeScript(); + } +} + +std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) const +{ + std::string script_id_hash_str(getUniqueHash()); + std::string script_extension = mScriptEd->mEditor->getIsLuauLanguage() ? ".luau" : ".lsl"; + + if (script_name.empty()) + { + return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + script_extension; + } + else + { + return std::string(LLFile::tmpdir()) + "sl_script_" + script_name + "_" + script_id_hash_str + script_extension; + } } -std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) +std::string LLScriptEdContainer::getUniqueHash() const { // Take script inventory item id (within the object inventory) // to consideration so that it's possible to edit multiple scripts @@ -1514,18 +1591,65 @@ std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString(); // Use MD5 sum to make the file name shorter and not exceed maximum path length. - char script_id_hash_str[33]; /* Flawfinder: ignore */ - LLMD5 script_id_hash((const U8 *)script_id.c_str()); + char script_id_hash_str[33]; /* Flawfinder: ignore */ + LLMD5 script_id_hash((const U8*)script_id.c_str()); script_id_hash.hex_digest(script_id_hash_str); - if (script_name.empty()) + return std::string(script_id_hash_str); +} + +std::string LLScriptEdContainer::getErrorLogFileName(const std::string& script_path) +{ + if (script_path.empty()) { - return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + ".lsl"; + return std::string(); } - else + + return script_path + ".log"; +} + +bool LLScriptEdContainer::logErrorsToFile(const LLSD& compile_errors) +{ + if (!isOpenInExternalEditor()) + { + return false; + } + + std::string script_path = getTmpFileName(mScriptEd->mScriptName); + std::string log_path = getErrorLogFileName(script_path); + + llofstream file(log_path.c_str()); + if (!file.is_open()) + { + LL_WARNS() << "Unable to open error log file: " << log_path << LL_ENDL; + return false; + } + + // Write timestamp + std::string timestamp = LLLogChat::timestamp2LogString(0, true); + file << "// " << timestamp << "\n\n"; + + // Write errors + for (LLSD::array_const_iterator line = compile_errors.beginArray(); + line < compile_errors.endArray(); + line++) + { + std::string error_message = line->asString(); + LLStringUtil::stripNonprintable(error_message); + file << error_message << "\n"; + } + + file.close(); + + // Create a log file handler if we don't already have one, + // this is needed to delete the temporary log file properly + if (!mLiveLogFile && !log_path.empty()) { - return std::string(LLFile::tmpdir()) + "sl_script_" + script_name + "_" + script_id_hash_str + ".lsl"; + // Empty callback since we don't need to react to file changes + mLiveLogFile = new LLLiveLSLFile(log_path, [](const std::string& filename) { return true; }); } + + return true; } bool LLScriptEdContainer::onExternalChange(const std::string& filename) @@ -1555,6 +1679,63 @@ bool LLScriptEdContainer::handleKeyHere(KEY key, MASK mask) return true; } +void LLScriptEdContainer::startWebsocketServer() +{ + if (gSavedSettings.getBOOL("ExternalWebsocketSyncEnable")) + { + // Attempt to find an existing server + LLWebsocketMgr& wsmgr = LLWebsocketMgr::instance(); + LLScriptEditorWSServer::ptr_t server = + std::static_pointer_cast<LLScriptEditorWSServer>( + wsmgr.findServerByName(LLScriptEditorWSServer::DEFAULT_SERVER_NAME)); + + if (!server) + { // We couldn't find one, so create it + U16 server_port = static_cast<U16>(gSavedSettings.getS32("ExternalWebsocketSyncPort")); + bool server_localhost = gSavedSettings.getBOOL("ExternalWebsocketSyncLocal"); + server = std::make_shared<LLScriptEditorWSServer>(LLScriptEditorWSServer::DEFAULT_SERVER_NAME, server_port, server_localhost); + wsmgr.addServer(server); + } + + bool is_running = server->isRunning(); + if (!is_running) + { // Server isn't running, so start it + is_running = wsmgr.startServer(LLScriptEditorWSServer::DEFAULT_SERVER_NAME); + } + + if (!is_running && !server->isRunning()) + { // Failed to start the server + LL_WARNS() << "Failed to start script editor websocket server" << LL_ENDL; + return; + } + + std::string script_id_hash_str(getUniqueHash()); + server->subscribeScriptEditor(mObjectUUID, mItemUUID, mScriptEd->mScriptName, getHandle(), script_id_hash_str); + mWebSocketServer = server; + } +} + +void LLScriptEdContainer::unsubscribeScript() +{ + auto server = mWebSocketServer.lock(); + if (server) + { + std::string script_id_hash_str(getUniqueHash()); + server->sendUnsubscribeScriptEditor(script_id_hash_str); + server->unsubscribeEditor(script_id_hash_str); + } +} + +void LLScriptEdContainer::sendCompileResults(LLSD& params) +{ + auto server = mWebSocketServer.lock(); + if (server) + { + std::string script_id_hash_str(getUniqueHash()); + server->sendCompileResults(script_id_hash_str, params); + } +} + /// --------------------------------------------------------------------------- /// LLPreviewLSL /// --------------------------------------------------------------------------- @@ -1579,7 +1760,7 @@ void* LLPreviewLSL::createScriptEdPanel(void* userdata) self->mScriptEd = new LLScriptEdCore( self, - HELLO_LSL, + std::string(), self->getHandle(), LLPreviewLSL::onLoad, LLPreviewLSL::onSave, @@ -1623,6 +1804,9 @@ bool LLPreviewLSL::postBuild() childSetCommitCallback("desc", LLPreview::onText, this); getChild<LLLineEditor>("desc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); + mScriptEd->mCompileTarget = getChild<LLComboBox>("compile_target"); + mScriptEd->mCompileTarget->setCommitCallback([&](LLUICtrl*, const LLSD&) { onCompileTargetChanged(); }); + return LLPreview::postBuild(); } @@ -1648,6 +1832,15 @@ void LLPreviewLSL::callbackLSLCompileSucceeded() LL_INFOS() << "LSL Bytecode saved" << LL_ENDL; mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful")); mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete")); + + if (isOpenInExternalEditor()) + { + LLSD success_msg; + success_msg.append(LLTrans::getString("CompileSuccessful")); + success_msg.append(LLTrans::getString("SaveComplete")); + logErrorsToFile(success_msg); + } + closeIfNeeded(); } @@ -1667,6 +1860,12 @@ void LLPreviewLSL::callbackLSLCompileFailed(const LLSD& compile_errors) row["columns"][0]["font"] = "OCRA"; mScriptEd->mErrorList->addElement(row); } + + if (isOpenInExternalEditor()) + { + logErrorsToFile(compile_errors); + } + mScriptEd->selectFirstError(); closeIfNeeded(); } @@ -1717,12 +1916,6 @@ void LLPreviewLSL::loadAsset() getChildView("lock")->setVisible( !is_modifiable); mScriptEd->getChildView("Insert...")->setEnabled(is_modifiable); } - else - { - mScriptEd->setScriptText(std::string(HELLO_LSL), true); - mScriptEd->setEnableEditing(true); - mAssetStatus = PREVIEW_ASSET_LOADED; - } } @@ -1780,6 +1973,7 @@ void LLPreviewLSL::finishedLSLUpload(LLUUID itemId, LLSD response) { preview->callbackLSLCompileFailed(response["errors"]); } + preview->sendCompileResults(response); } } @@ -1803,6 +1997,12 @@ bool LLPreviewLSL::failedLSLUpload(LLUUID itemId, LLUUID taskId, LLSD response, LLSD errors; errors.append(LLTrans::getString("UploadFailed") + reason); preview->callbackLSLCompileFailed(errors); + + LLSD message; + message["compiled"] = false; + message["errors"] = errors; + preview->sendCompileResults(message); + return true; } @@ -1838,11 +2038,12 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) mPendingUploads++; if (!url.empty()) { + std::string compile_target(mScriptEd->mCompileTarget->getValue()); std::string buffer(mScriptEd->mEditor->getText()); LLUUID old_asset_id = inv_item->getAssetUUID().isNull() ? mScriptEd->getAssetID() : inv_item->getAssetUUID(); - LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>(mItemUUID, buffer, + LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>(mItemUUID, compile_target, buffer, [old_asset_id](LLUUID itemId, LLUUID, LLUUID, LLSD response) { LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); LLPreviewLSL::finishedLSLUpload(itemId, response); @@ -1854,6 +2055,13 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) } } +void LLPreviewLSL::onCompileTargetChanged() +{ + bool is_lua = (mScriptEd->mCompileTarget->getValue().asString() == "luau"); + mScriptEd->mEditor->setLuauLanguage(is_lua); + mScriptEd->processKeywords(is_lua); +} + // static void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) @@ -1880,6 +2088,8 @@ void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType t std::string script_name = DEFAULT_SCRIPT_NAME; LLInventoryItem* item = gInventory.getItem(*item_uuid); bool is_modifiable = false; + std::string compile_target; + bool is_lua = false; if (item) { if (!item->getName().empty()) @@ -1890,11 +2100,39 @@ void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType t { is_modifiable = true; } + + // subtype is the language that the script is written in, + // runtime is the VM that the script is running on. + // LSL scripts have 3 possible runtimes: "lsl2", "mono", and "luau". + // Lua scripts have 1 runtime: "luau". + U8 subtype = item->getInventorySubType(); + std::string runtime = item->getRuntime(); + + is_lua = (subtype == SST_LUA); + if (!is_lua && (runtime == "luau")) + { // an lsl script executing on the luau runtime + compile_target = "lsl-luau"; + } + else + { // otherwise, use the runtime as the compile target + // (this may be blank if the script has never been compiled) + compile_target = runtime; + } + } + + if (compile_target.empty()) + { + compile_target = is_lua ? "luau" : "mono"; } + preview->mScriptEd->setScriptName(script_name); preview->mScriptEd->setEnableEditing(is_modifiable); preview->mScriptEd->setAssetID(asset_uuid); preview->mAssetStatus = PREVIEW_ASSET_LOADED; + + preview->mScriptEd->mCompileTarget->setValue(compile_target); + preview->mScriptEd->mEditor->setLuauLanguage(is_lua); + preview->mScriptEd->processKeywords(is_lua); } else { @@ -1932,7 +2170,7 @@ void* LLLiveLSLEditor::createScriptEdPanel(void* userdata) self->mScriptEd = new LLScriptEdCore( self, - HELLO_LSL, + std::string(), self->getHandle(), &LLLiveLSLEditor::onLoad, &LLLiveLSLEditor::onSave, @@ -1960,28 +2198,26 @@ LLLiveLSLEditor::LLLiveLSLEditor(const LLSD& key) : bool LLLiveLSLEditor::postBuild() { - childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this); - getChildView("running")->setEnabled(false); - - childSetAction("Reset",&LLLiveLSLEditor::onReset,this); - getChildView("Reset")->setEnabled(true); - - mMonoCheckbox = getChild<LLCheckBoxCtrl>("mono"); - childSetCommitCallback("mono", &LLLiveLSLEditor::onMonoCheckboxClicked, this); - getChildView("mono")->setEnabled(true); - - mScriptEd->mEditor->makePristine(); - mScriptEd->mEditor->setFocus(true); + mResetButton = getChild<LLButton>("Reset"); + mResetButton->setClickedCallback([&](LLUICtrl*, const LLSD&) { onReset(); }); + mRunningCheckbox = getChild<LLCheckBoxCtrl>("running"); + mRunningCheckbox->setCommitCallback([&](LLUICtrl*, const LLSD&) { onRunningCheckboxClicked(); }); mExperiences = getChild<LLComboBox>("Experiences..."); - mExperiences->setCommitCallback(boost::bind(&LLLiveLSLEditor::experienceChanged, this)); + mExperiences->setCommitCallback([&](LLUICtrl*, const LLSD&) { experienceChanged(); }); mExperienceEnabled = getChild<LLCheckBoxCtrl>("enable_xp"); + mExperienceEnabled->setCommitCallback([&](LLUICtrl*, const LLSD&) { onToggleExperience(); }); - childSetCommitCallback("enable_xp", onToggleExperience, this); - childSetCommitCallback("view_profile", onViewProfile, this); + mViewProfileButton = getChild<LLButton>("view_profile"); + mViewProfileButton->setClickedCallback([&](LLUICtrl*, const LLSD&) { onViewProfile(); }); + mScriptEd->mEditor->makePristine(); + mScriptEd->mEditor->setFocus(true); + + mScriptEd->mCompileTarget = getChild<LLComboBox>("compile_target"); + mScriptEd->mCompileTarget->setCommitCallback([&](LLUICtrl*, const LLSD&) { onCompileTargetChanged(); }); return LLPreview::postBuild(); } @@ -1994,7 +2230,16 @@ void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id, LL_DEBUGS() << "LSL Bytecode saved" << LL_ENDL; mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful")); mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete")); - getChild<LLCheckBoxCtrl>("running")->set(is_script_running); + + if (isOpenInExternalEditor()) + { + LLSD success_msg; + success_msg.append(LLTrans::getString("CompileSuccessful")); + success_msg.append(LLTrans::getString("SaveComplete")); + logErrorsToFile(success_msg); + } + + mRunningCheckbox->set(is_script_running); mIsSaving = false; closeIfNeeded(); } @@ -2003,6 +2248,7 @@ void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id, void LLLiveLSLEditor::callbackLSLCompileFailed(const LLSD& compile_errors) { LL_DEBUGS() << "Compile failed!" << LL_ENDL; + for(LLSD::array_const_iterator line = compile_errors.beginArray(); line < compile_errors.endArray(); line++) @@ -2015,6 +2261,12 @@ void LLLiveLSLEditor::callbackLSLCompileFailed(const LLSD& compile_errors) row["columns"][0]["font"] = "OCRA"; mScriptEd->mErrorList->addElement(row); } + + if (isOpenInExternalEditor()) + { + logErrorsToFile(compile_errors); + } + mScriptEd->selectFirstError(); mIsSaving = false; closeIfNeeded(); @@ -2057,9 +2309,10 @@ void LLLiveLSLEditor::loadAsset() { mItem = new LLViewerInventoryItem(item); // request the text from the object - LLSD* user_data = new LLSD(); - user_data->with("taskid", mObjectUUID).with("itemid", mItemUUID); - gAssetStorage->getInvItemAsset(object->getRegion()->getHost(), + LLSD* user_data = new LLSD(); + user_data->with("taskid", mObjectUUID).with("itemid", mItemUUID); + gAssetStorage->getInvItemAsset( + object->getRegion()->getHost(), gAgent.getID(), gAgent.getSessionID(), item->getPermissions().getOwner(), @@ -2067,8 +2320,8 @@ void LLLiveLSLEditor::loadAsset() item->getUUID(), item->getAssetUUID(), item->getType(), - &LLLiveLSLEditor::onLoadComplete, - (void*)user_data, + LLLiveLSLEditor::onLoadComplete, + user_data, true); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_GetScriptRunning); @@ -2107,26 +2360,6 @@ void LLLiveLSLEditor::loadAsset() */ } } - else - { - mScriptEd->setScriptText(std::string(HELLO_LSL), true); - mScriptEd->enableSave(false); - LLPermissions perm; - perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, gAgent.getGroupID()); - perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER); - mItem = new LLViewerInventoryItem(mItemUUID, - mObjectUUID, - perm, - LLUUID::null, - LLAssetType::AT_LSL_TEXT, - LLInventoryType::IT_LSL, - DEFAULT_SCRIPT_NAME, - DEFAULT_SCRIPT_DESC, - LLSaleInfo::DEFAULT, - LLInventoryItemFlags::II_FLAGS_NONE, - time_corrected()); - mAssetStatus = PREVIEW_ASSET_LOADED; - } requestExperiences(); } @@ -2202,14 +2435,9 @@ void LLLiveLSLEditor::loadScriptText(const LLUUID &uuid, LLAssetType::EType type } -void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata ) +void LLLiveLSLEditor::onRunningCheckboxClicked() { - LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; - LLViewerObject* object = gObjectList.findObject( self->mObjectUUID ); - LLCheckBoxCtrl* runningCheckbox = self->getChild<LLCheckBoxCtrl>("running"); - bool running = runningCheckbox->get(); - //self->mRunningCheckbox->get(); - if( object ) + if (LLViewerObject* object = gObjectList.findObject(mObjectUUID)) { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_SetScriptRunning); @@ -2217,24 +2445,21 @@ void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata ) msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_Script); - msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID); - msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID); - msg->addBOOLFast(_PREHASH_Running, running); + msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID); + msg->addUUIDFast(_PREHASH_ItemID, mItemUUID); + msg->addBOOLFast(_PREHASH_Running, mRunningCheckbox->get()); msg->sendReliable(object->getRegion()->getHost()); } else { - runningCheckbox->set(!running); + mRunningCheckbox->set(!mRunningCheckbox->get()); LLNotificationsUtil::add("CouldNotStartStopScript"); } } -void LLLiveLSLEditor::onReset(void *userdata) +void LLLiveLSLEditor::onReset() { - LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; - - LLViewerObject* object = gObjectList.findObject( self->mObjectUUID ); - if(object) + if (LLViewerObject* object = gObjectList.findObject(mObjectUUID)) { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_ScriptReset); @@ -2242,8 +2467,8 @@ void LLLiveLSLEditor::onReset(void *userdata) msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_Script); - msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID); - msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID); + msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID); + msg->addUUIDFast(_PREHASH_ItemID, mItemUUID); msg->sendReliable(object->getRegion()->getHost()); } else @@ -2255,34 +2480,33 @@ void LLLiveLSLEditor::onReset(void *userdata) void LLLiveLSLEditor::draw() { LLViewerObject* object = gObjectList.findObject(mObjectUUID); - LLCheckBoxCtrl* runningCheckbox = getChild<LLCheckBoxCtrl>( "running"); - if(object && mAskedForRunningInfo && mHaveRunningInfo) + if (object && mAskedForRunningInfo && mHaveRunningInfo) { - if(object->permAnyOwner()) + if (object->permAnyOwner()) { - runningCheckbox->setLabel(getString("script_running")); - runningCheckbox->setEnabled(!mIsSaving); + mRunningCheckbox->setLabel(getString("script_running")); + mRunningCheckbox->setEnabled(!mIsSaving); } else { - runningCheckbox->setLabel(getString("public_objects_can_not_run")); - runningCheckbox->setEnabled(false); + mRunningCheckbox->setLabel(getString("public_objects_can_not_run")); + mRunningCheckbox->setEnabled(false); // *FIX: Set it to false so that the ui is correct for // a box that is released to public. It could be // incorrect after a release/claim cycle, but will be // correct after clicking on it. - runningCheckbox->set(false); - mMonoCheckbox->set(false); + mRunningCheckbox->set(false); + mScriptEd->mCompileTarget->clear(); } } - else if(!object) + else if (!object) { // HACK: Display this information in the title bar. // Really ought to put in main window. setTitle(LLTrans::getString("ObjectOutOfRange")); - runningCheckbox->setEnabled(false); - mMonoCheckbox->setEnabled(false); + mRunningCheckbox->setEnabled(false); + mScriptEd->mCompileTarget->setEnabled(false); // object may have fallen out of range. mHaveRunningInfo = false; } @@ -2339,6 +2563,8 @@ void LLLiveLSLEditor::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAs { preview->callbackLSLCompileFailed(response["errors"]); } + response["is_running"] = isRunning; + preview->sendCompileResults(response); } } @@ -2389,7 +2615,7 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) mScriptEd->sync(); } - bool isRunning = getChild<LLCheckBoxCtrl>("running")->get(); + bool is_running = mRunningCheckbox->get(); getWindow()->incBusyCount(); mPendingUploads++; @@ -2397,17 +2623,18 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) if (!url.empty()) { + std::string compile_target(mScriptEd->mCompileTarget->getValue()); std::string buffer(mScriptEd->mEditor->getText()); LLUUID old_asset_id = mScriptEd->getAssetID(); LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>(mObjectUUID, mItemUUID, - monoChecked() ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2, - isRunning, mScriptEd->getAssociatedExperience(), buffer, - [isRunning, old_asset_id](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) { - LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); - LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning); - }, - nullptr)); // needs failure handling? + compile_target, is_running, mScriptEd->getAssociatedExperience(), buffer, + [is_running, old_asset_id](LLUUID item_id, LLUUID task_id, LLUUID new_asset_id, LLSD response) + { + LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); + LLLiveLSLEditor::finishLSLUpload(item_id, task_id, new_asset_id, response, is_running); + }, + nullptr)); // needs failure handling? LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); } @@ -2460,28 +2687,61 @@ void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**) if (LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", floater_key)) { instance->mHaveRunningInfo = true; + bool running; msg->getBOOLFast(_PREHASH_Script, _PREHASH_Running, running); - LLCheckBoxCtrl* runningCheckbox = instance->getChild<LLCheckBoxCtrl>("running"); - runningCheckbox->set(running); - bool mono; - msg->getBOOLFast(_PREHASH_Script, "Mono", mono); - LLCheckBoxCtrl* monoCheckbox = instance->getChild<LLCheckBoxCtrl>("mono"); - monoCheckbox->setEnabled(instance->getIsModifiable() && have_script_upload_cap(object_id)); - monoCheckbox->set(mono); - } -} + instance->mRunningCheckbox->set(running); -void LLLiveLSLEditor::onMonoCheckboxClicked(LLUICtrl*, void* userdata) -{ - LLLiveLSLEditor* self = static_cast<LLLiveLSLEditor*>(userdata); - self->mMonoCheckbox->setEnabled(have_script_upload_cap(self->mObjectUUID)); - self->mScriptEd->enableSave(self->getIsModifiable()); + bool mono = false, luau = false, luau_language = false; + msg->getBOOLFast(_PREHASH_Script, _PREHASH_Mono, mono); + msg->getBOOLFast(_PREHASH_Script, _PREHASH_Luau, luau); // Luau compiler is enabled + msg->getBOOLFast(_PREHASH_Script, _PREHASH_LuauLanguage, luau_language); + + std::string compile_target; + if (luau) + { + if (luau_language) + { + compile_target = "luau"; + } + else + { + compile_target = "lsl-luau"; // Luau compiler running in LSL compatibility mode + } + } + else if (mono) + { + compile_target = "mono"; + } + else + { + compile_target = "lsl2"; + } + + instance->mScriptEd->mCompileTarget->setValue(compile_target); + instance->mScriptEd->processKeywords(luau && luau_language); // Use Luau syntax highlighting for Luau scripts + + bool lua_scripts_enabled = have_lua_enabled(object_id); + if (LLScrollListItem* luau_item = instance->mScriptEd->mCompileTarget->findItemByValue("luau")) + { + luau_item->setEnabled(lua_scripts_enabled); + } + if (LLScrollListItem* lsl_luau_item = instance->mScriptEd->mCompileTarget->findItemByValue("lsl-luau")) + { + lsl_luau_item->setEnabled(lua_scripts_enabled); + } + + instance->mScriptEd->mCompileTarget->setEnabled(instance->getIsModifiable() && have_script_upload_cap(object_id)); + } } -bool LLLiveLSLEditor::monoChecked() const +void LLLiveLSLEditor::onCompileTargetChanged() { - return mMonoCheckbox && mMonoCheckbox->getValue(); + mScriptEd->mCompileTarget->setEnabled(have_script_upload_cap(mObjectUUID)); + mScriptEd->enableSave(getIsModifiable()); + bool is_lua = mScriptEd->mCompileTarget->getValue().asString() == "luau"; + mScriptEd->mEditor->setLuauLanguage(is_lua); + mScriptEd->processKeywords(is_lua); } void LLLiveLSLEditor::setAssociatedExperience( LLHandle<LLLiveLSLEditor> editor, const LLSD& experience ) |
