diff options
| author | Erik Kundiman <erik@megapahit.org> | 2025-05-22 05:50:46 +0800 | 
|---|---|---|
| committer | Erik Kundiman <erik@megapahit.org> | 2025-05-22 05:50:46 +0800 | 
| commit | f900a9ae67c61b3e53c36c119440cbc3710a2f7c (patch) | |
| tree | 341a78d70c88e414f501a4023318ceabb338d6ec /indra/newview | |
| parent | c06c7e0b0eb4b7a7aaa70a3559b08b18c73aea17 (diff) | |
| parent | 060bebcd3cbb5fbf6045bd777364947a2d8963d3 (diff) | |
Merge branch 'main' into 2025.04
Diffstat (limited to 'indra/newview')
31 files changed, 2118 insertions, 164 deletions
| diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 473ca60728..5fd549dca2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -9,15 +9,11 @@ include(Linking)  include(Boost)  include(bugsplat) -if (NOT USESYSTEMLIBS) -include(BuildPackagesInfo) -endif () +#include(BuildPackagesInfo)  include(BuildVersion)  include(CMakeCopyIfDifferent)  include(CubemapToEquirectangularJS) -if (NOT USESYSTEMLIBS) -include(DBusGlib) -endif () +#include(DBusGlib)  include(DragDrop)  include(EXPAT)  include(Hunspell) @@ -45,18 +41,16 @@ include(ThreeJS)  include(Tracy)  include(UI)  include(ViewerMiscLibs) -if (NOT USESYSTEMLIBS) -include(ViewerManager) -endif () +#include(ViewerManager)  include(VisualLeakDetector) -include(VulkanGltf) +#include(VulkanGltf)  include(ZLIBNG)  include(LLPrimitive)  if (ENABLE_MEDIA_PLUGINS)      include(LibVLCPlugin)      include(CEFPlugin) -endif (ENABLE_MEDIA_PLUGINS) +endif ()  if (NOT HAVOK_TPV)     # When using HAVOK_TPV, the library is precompiled, so no need for this @@ -744,11 +738,16 @@ set(viewer_SOURCE_FILES      llxmlrpctransaction.cpp      noise.cpp      pipeline.cpp +    rlvactions.cpp +    rlvcommon.cpp +    rlvfloaters.cpp +    rlvhelper.cpp +    rlvhandler.cpp      )  if (CMAKE_SYSTEM_NAME MATCHES FreeBSD)      list(REMOVE_ITEM viewer_SOURCE_FILES llvoicewebrtc.cpp) -endif (CMAKE_SYSTEM_NAME MATCHES FreeBSD) +endif ()  set(VIEWER_BINARY_NAME "secondlife-bin" CACHE STRING      "The name of the viewer executable to create.") @@ -1414,6 +1413,12 @@ set(viewer_HEADER_FILES      llxmlrpctransaction.h      noise.h      pipeline.h +    rlvdefines.h +    rlvactions.h +    rlvcommon.h +    rlvfloaters.h +    rlvhelper.h +    rlvhandler.h      roles_constants.h      VertexCache.h      VorbisFramework.h @@ -1487,18 +1492,19 @@ if (DARWIN)    list(APPEND viewer_SOURCE_FILES ${viewer_RESOURCE_FILES})  endif (DARWIN) -if (USESYSTEMLIBS AND NOT DARWIN) +if (NOT DARWIN)      LIST(APPEND viewer_SOURCE_FILES llappviewerlinux.cpp)      set_source_files_properties(        llappviewerlinux.cpp        PROPERTIES        COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}"        ) -  if (NOT CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") +    #LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp) +  if (NOT CMAKE_CXX_COMPILER_ID MATCHES AppleClang)      SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")    endif () -endif (USESYSTEMLIBS AND NOT DARWIN) +endif ()  if (WINDOWS) @@ -1680,15 +1686,11 @@ set(viewer_APPSETTINGS_FILES      app_settings/viewerart.xml      ${CMAKE_SOURCE_DIR}/../etc/message.xml      ${CMAKE_SOURCE_DIR}/../scripts/messages/message_template.msg -    packages-info.txt +    #packages-info.txt      featuretable.txt      featuretable_mac.txt      ) -if (USESYSTEMLIBS) -    list(REMOVE_ITEM viewer_APPSETTINGS_FILES packages-info.txt) -endif () -  source_group("App Settings" FILES ${viewer_APPSETTINGS_FILES})  set_source_files_properties(${viewer_APPSETTINGS_FILES} @@ -2019,14 +2021,14 @@ target_link_libraries(${VIEWER_BINARY_NAME}  if (NOT CMAKE_SYSTEM_NAME MATCHES FreeBSD)     target_link_libraries(${VIEWER_BINARY_NAME} llwebrtc ) -endif (NOT CMAKE_SYSTEM_NAME MATCHES FreeBSD) +endif ()  if (ENABLE_MEDIA_PLUGINS)     target_link_libraries(${VIEWER_BINARY_NAME} ll::libvlc )     if (DARWIN OR LINUX)        target_link_libraries(${VIEWER_BINARY_NAME} ll::cef ) -   endif (DARWIN OR LINUX) -endif (ENABLE_MEDIA_PLUGINS) +   endif () +endif ()  if( TARGET ll::intel_memops )     target_link_libraries(${VIEWER_BINARY_NAME} ll::intel_memops ) @@ -2041,13 +2043,13 @@ set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH  set_source_files_properties(llinventorygallery.cpp PROPERTIES COMPILE_FLAGS      -Wno-unused-but-set-variable) -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") +if (CMAKE_CXX_COMPILER_ID MATCHES Clang)      set_source_files_properties(llappviewerlinux.cpp PROPERTIES          COMPILE_FLAGS -Wno-dangling-gsl      )      set_source_files_properties(llviewerstats.cpp PROPERTIES          COMPILE_FLAGS -Wno-unused-value) -elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU") +elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU)      set_source_files_properties(          llface.cpp          llhttpretrypolicy.cpp @@ -2117,100 +2119,86 @@ foreach(elem ${country_codes})     configure_file(${emoji_mapping_src_file} ${emoji_mapping_dst_file} COPYONLY)  endforeach() -if (PACKAGE AND USESYSTEMLIBS) +if (PACKAGE)    set(CPACK_PACKAGE_NAME ${VIEWER_BINARY_NAME}      CACHE STRING "Viewer binary name.")    set(CPACK_PACKAGE_VERSION ${VIEWER_VERSION_MAJOR}.${VIEWER_VERSION_MINOR}.${VIEWER_VERSION_PATCH}.${VIEWER_VERSION_REVISION}      CACHE STRING "Viewer major.minor.patch.revision versions.") -  set(VIEWER_PACKAGE_COMMENT -    "A fork of the Second Life viewer" -    ) -  set(VIEWER_PACKAGE_DESCRIPTION -    "An entrance to virtual empires in only megabytes. A shelter for the metaverse refugees, especially those from less supported operating systems." -    ) -  set(VIEWER_PACKAGE_DOMAIN_NAME -    ${VIEWER_BINARY_NAME}.net -    ) -endif (PACKAGE AND USESYSTEMLIBS) +  set(VIEWER_PACKAGE_COMMENT "A fork of the Second Life viewer") +  set(VIEWER_PACKAGE_DESCRIPTION "An entrance to virtual empires in only megabytes. A shelter for the metaverse refugees, especially those from less supported operating systems.") +  set(VIEWER_PACKAGE_DOMAIN_NAME ${VIEWER_BINARY_NAME}.net) +endif ()  if (LINUX) -  if (USESYSTEMLIBS) -    add_custom_command( -      TARGET ${VIEWER_BINARY_NAME} POST_BUILD -      COMMAND ${CMAKE_SYSROOT}/usr/bin/sed -      ARGS -e '/Linden Lab.*/d' ${CMAKE_HOME_DIRECTORY}/../doc/contributions.txt > ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -      COMMAND ${CMAKE_SYSROOT}/usr/bin/sed -      ARGS -i ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -e '/following residents.*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -      COMMAND ${CMAKE_SYSROOT}/usr/bin/sed -      ARGS -i ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -e '/along with.*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -      COMMAND ${CMAKE_SYSROOT}/usr/bin/sed -      ARGS -i ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -e '/^$$/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -      COMMAND ${CMAKE_SYSROOT}/usr/bin/sed -      ARGS -i ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -e '/\t.*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -      COMMAND ${CMAKE_SYSROOT}/usr/bin/sed -      ARGS -i ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -e '/^    .*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -      COMMAND sort -      ARGS -R contributions.txt -o ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -      COMMAND paste -      ARGS -s -d, ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt > ${CMAKE_CURRENT_BINARY_DIR}/contributors.txt -      COMMAND ${CMAKE_SYSROOT}/usr/bin/sed -      ARGS -i ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt -e 's/,/, /g' ${CMAKE_CURRENT_BINARY_DIR}/contributors.txt -      ) -    if (PACKAGE) -      if (${LINUX_DISTRO} MATCHES debian OR ${LINUX_DISTRO} MATCHES ubuntu) -        set(CPACK_BINARY_DEB ON CACHE BOOL "Able to package Debian DEB.") -        set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE -          amd64 -          CACHE STRING "Debian package architecture.") -        set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${VIEWER_PACKAGE_COMMENT} -          CACHE STRING "Debian package description.") -        set(CPACK_DEBIAN_PACKAGE_MAINTAINER -          $ENV{USER}@${VIEWER_PACKAGE_DOMAIN_NAME} -          CACHE STRING "Debian package maintainer.") -        set(CPACK_DEBIAN_PACKAGE_SECTION net -          CACHE STRING "Debian package section.") -        if (${LINUX_DISTRO} MATCHES debian) -          set(CPACK_DEBIAN_PACKAGE_DEPENDS -            "libalut0, libaprutil1, libboost-fiber1.81.0, libboost-filesystem1.81.0, libboost-program-options1.81.0, libboost-regex1.81.0, libboost-thread1.81.0, libboost-url1.81.0, libexpat1, libfltk1.3, libgles-dev, libglu1-mesa, libhunspell-1.7-0, libmeshoptimizer2d, libminizip1, libnghttp2-14, libsdl2-2.0-0, libvlc5, libvorbisenc2, libvorbisfile3, vlc-plugin-base" -          CACHE STRING "Debian package dependencies.") -        elseif (${LINUX_DISTRO} MATCHES ubuntu) -          set(CPACK_DEBIAN_PACKAGE_DEPENDS -            "libaprutil1t64, libboost-fiber1.83.0, libboost-filesystem1.83.0, libboost-program-options1.83.0, libboost-regex1.83.0, libboost-thread1.83.0, libboost-url1.83.0, libexpat1, libfltk1.3t64, libgles-dev, libglu1-mesa, libhunspell-1.7-0, libmeshoptimizer2d, libminizip1, libnghttp2-14, libsdl2-2.0-0, libvlc5, libvorbisenc2, libvorbisfile3, vlc-plugin-base" -          CACHE STRING "Debian package dependencies.") -        endif (${LINUX_DISTRO} MATCHES debian) -      elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed)) -        set(CPACK_BINARY_RPM ON CACHE BOOL "Able to package Fedora RPM.") -        set(CPACK_RPM_PACKAGE_SUMMARY ${VIEWER_PACKAGE_COMMENT} -          CACHE STRING "RPM package summary.") -        set(CPACK_RPM_PACKAGE_ARCHITECTURE -          ${CMAKE_SYSTEM_PROCESSOR} -          CACHE STRING "RPM package architecture.") -        set(CPACK_RPM_PACKAGE_LICENSE LGPL-2.1-only -          CACHE STRING "RPM package license.") -        set(CPACK_RPM_PACKAGE_VENDOR ${VIEWER_CHANNEL} -          CACHE STRING "RPM package vendor.") -        set(CPACK_RPM_PACKAGE_URL -          https://${VIEWER_PACKAGE_DOMAIN_NAME} -          CACHE STRING "RPM package URL.") -        set(CPACK_RPM_PACKAGE_DESCRIPTION ${VIEWER_PACKAGE_DESCRIPTION} -          CACHE STRING "RPM package description.") -        if (${LINUX_DISTRO} MATCHES fedora) -          set(CPACK_RPM_PACKAGE_REQUIRES -            "apr-util, boost-fiber, boost-program-options, boost-regex, boost-thread, boost-url, expat, fltk, mesa-libGLU, hunspell, libnghttp2, SDL2, vlc-libs, vlc-plugins-base, libvorbis" -            CACHE STRING "RPM package requirements.") -        elseif (${LINUX_DISTRO} MATCHES opensuse-tumbleweed) -          set(CPACK_RPM_PACKAGE_REQUIRES -            "libapr-util1-0, libboost_fiber1_87_0, libboost_program_options1_87_0, libboost_regex1_87_0, libboost_thread1_87_0, libboost_url1_87_0, libboost_url1_87_0-x86-64-v3, expat, libfltk1_3, libGLU1, libhunspell-1_7-0, libnghttp2-14, libSDL2-2_0-0, libvlc5, vlc-codecs, libvorbis0" -            CACHE STRING "RPM package requirements.") -        endif (${LINUX_DISTRO} MATCHES fedora) -      elseif (${LINUX_DISTRO} MATCHES arch) -        configure_file( -          ${CMAKE_CURRENT_SOURCE_DIR}/PKGBUILD.in -          ${CMAKE_BINARY_DIR}/PKGBUILD -          ) -      endif (${LINUX_DISTRO} MATCHES debian OR ${LINUX_DISTRO} MATCHES ubuntu) -    endif (PACKAGE) -  else (USESYSTEMLIBS) +  add_custom_command( +    TARGET ${VIEWER_BINARY_NAME} POST_BUILD +    COMMAND ${CMAKE_SYSROOT}/usr/bin/sed +    ARGS -e '/Linden Lab.*/d' ${CMAKE_HOME_DIRECTORY}/../doc/contributions.txt > ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt +    COMMAND ${CMAKE_SYSROOT}/usr/bin/sed +    ARGS -i '/following residents.*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt +    COMMAND ${CMAKE_SYSROOT}/usr/bin/sed +    ARGS -i '/along with.*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt +    COMMAND ${CMAKE_SYSROOT}/usr/bin/sed +    ARGS -i '/^$$/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt +    COMMAND ${CMAKE_SYSROOT}/usr/bin/sed +    ARGS -i '/\t.*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt +    COMMAND ${CMAKE_SYSROOT}/usr/bin/sed +    ARGS -i '/^    .*/d' ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt +    COMMAND sort +    ARGS -R contributions.txt -o ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt +    COMMAND paste +    ARGS -s -d, ${CMAKE_CURRENT_BINARY_DIR}/contributions.txt > ${CMAKE_CURRENT_BINARY_DIR}/contributors.txt +    COMMAND ${CMAKE_SYSROOT}/usr/bin/sed +    ARGS -i 's/,/, /g' ${CMAKE_CURRENT_BINARY_DIR}/contributors.txt +    ) +  if (PACKAGE) +    if (${LINUX_DISTRO} MATCHES arch) +      configure_file( +        ${CMAKE_CURRENT_SOURCE_DIR}/PKGBUILD.in +        ${CMAKE_BINARY_DIR}/PKGBUILD +        ) +    elseif (${LINUX_DISTRO} MATCHES debian OR ${LINUX_DISTRO} MATCHES ubuntu) +      set(CPACK_BINARY_DEB ON CACHE BOOL "Able to package Debian DEB.") +      set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64 +        CACHE STRING "Debian package architecture.") +      set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${VIEWER_PACKAGE_COMMENT} +        CACHE STRING "Debian package description.") +      set(CPACK_DEBIAN_PACKAGE_MAINTAINER $ENV{USER}@${VIEWER_PACKAGE_DOMAIN_NAME} +        CACHE STRING "Debian package maintainer.") +      set(CPACK_DEBIAN_PACKAGE_SECTION net +        CACHE STRING "Debian package section.") +      if (${LINUX_DISTRO} MATCHES debian) +        set(CPACK_DEBIAN_PACKAGE_DEPENDS "libalut0, libaprutil1, libboost-fiber1.81.0, libboost-filesystem1.81.0, libboost-program-options1.81.0, libboost-regex1.81.0, libboost-thread1.81.0, libboost-url1.81.0, libexpat1, libfltk1.3, libgles-dev, libglu1-mesa, libhunspell-1.7-0, libmeshoptimizer2d, libminizip1, libnghttp2-14, libsdl2-2.0-0, libvlc5, libvorbisenc2, libvorbisfile3, vlc-plugin-base" +        CACHE STRING "Debian package dependencies.") +      else () +        set(CPACK_DEBIAN_PACKAGE_DEPENDS "libaprutil1t64, libboost-fiber1.83.0, libboost-filesystem1.83.0, libboost-program-options1.83.0, libboost-regex1.83.0, libboost-thread1.83.0, libboost-url1.83.0, libexpat1, libfltk1.3t64, libgles-dev, libglu1-mesa, libhunspell-1.7-0, libmeshoptimizer2d, libminizip1, libnghttp2-14, libsdl2-2.0-0, libvlc5, libvorbisenc2, libvorbisfile3, vlc-plugin-base" +        CACHE STRING "Debian package dependencies.") +      endif () +    elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed)) +      set(CPACK_BINARY_RPM ON CACHE BOOL "Able to package Fedora RPM.") +      set(CPACK_RPM_PACKAGE_SUMMARY ${VIEWER_PACKAGE_COMMENT} +        CACHE STRING "RPM package summary.") +      set(CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR} +        CACHE STRING "RPM package architecture.") +      set(CPACK_RPM_PACKAGE_LICENSE LGPL-2.1-only +        CACHE STRING "RPM package license.") +      set(CPACK_RPM_PACKAGE_VENDOR ${VIEWER_CHANNEL} +        CACHE STRING "RPM package vendor.") +      set(CPACK_RPM_PACKAGE_URL https://${VIEWER_PACKAGE_DOMAIN_NAME} +        CACHE STRING "RPM package URL.") +      set(CPACK_RPM_PACKAGE_DESCRIPTION ${VIEWER_PACKAGE_DESCRIPTION} +        CACHE STRING "RPM package description.") +      if (${LINUX_DISTRO} MATCHES fedora) +        set(CPACK_RPM_PACKAGE_REQUIRES "apr-util, boost-fiber, boost-program-options, boost-regex, boost-thread, boost-url, expat, fltk, mesa-libGLU, hunspell, libnghttp2, openjpeg2, SDL2, vlc-libs, vlc-plugins-base, libvorbis" +          CACHE STRING "RPM package requirements.") +      else () +        set(CPACK_RPM_PACKAGE_REQUIRES "libapr-util1-0, libboost_fiber1_87_0, libboost_program_options1_87_0, libboost_regex1_87_0, libboost_thread1_87_0, libboost_url1_87_0, libboost_url1_87_0-x86-64-v3, expat, libfltk1_3, libGLU1, libhunspell-1_7-0, libnghttp2-14, openjpeg2, libSDL2-2_0-0, libvlc5, vlc-codecs, libvorbis0" +          CACHE STRING "RPM package requirements.") +      endif () +    endif () +  endif (PACKAGE) + +  if (FALSE)    set(product SecondLife-${ARCH}-${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION})    # These are the generated targets that are copied to package/ @@ -2285,8 +2273,9 @@ if (LINUX)      add_dependencies(llpackage copy_l_viewer_manifest)      check_message_template(llpackage)    endif (PACKAGE) -  endif (USESYSTEMLIBS) -elseif (USESYSTEMLIBS) +  endif (FALSE) + +else (LINUX)    add_custom_command(      TARGET ${VIEWER_BINARY_NAME} POST_BUILD      COMMAND sed @@ -2314,20 +2303,18 @@ elseif (USESYSTEMLIBS)        CACHE STRING "FreeBSD package comment.")      set(CPACK_FREEBSD_PACKAGE_DESCRIPTION ${VIEWER_PACKAGE_DESCRIPTION}        CACHE STRING "FreeBSD package description.") -    set(CPACK_FREEBSD_PACKAGE_WWW -      https://${VIEWER_PACKAGE_DOMAIN_NAME} +    set(CPACK_FREEBSD_PACKAGE_WWW https://${VIEWER_PACKAGE_DOMAIN_NAME}        CACHE STRING "FreeBSD package WWW.")      set(CPACK_FREEBSD_PACKAGE_LICENSE LGPL21        CACHE STRING "FreeBSD package license.") -    set(CPACK_FREEBSD_PACKAGE_MAINTAINER -      $ENV{USER}@${VIEWER_PACKAGE_DOMAIN_NAME} +    set(CPACK_FREEBSD_PACKAGE_MAINTAINER $ENV{USER}@${VIEWER_PACKAGE_DOMAIN_NAME}        CACHE STRING "FreeBSD package maintainer.")      set(CPACK_FREEBSD_PACKAGE_ORIGIN net/${VIEWER_BINARY_NAME}        CACHE STRING "FreeBSD package origin.") -    set(CPACK_FREEBSD_PACKAGE_DEPS -      "audio/freealut;graphics/libGLU;textproc/hunspell;misc/meshoptimizer;archivers/minizip;www/libnghttp2;multimedia/vlc;audio/libvorbis" +    set(CPACK_FREEBSD_PACKAGE_DEPS "audio/freealut;graphics/libGLU;textproc/hunspell;misc/meshoptimizer;archivers/minizip;www/libnghttp2;graphics/openjpeg;multimedia/vlc;audio/libvorbis"        CACHE STRING "FreeBSD package dependencies.") -  endif (CMAKE_SYSTEM_NAME MATCHES FreeBSD AND PACKAGE) +  endif () +  endif (LINUX)  if (DARWIN) @@ -2372,12 +2359,12 @@ if (DARWIN)       "${VIEWER_APP_BUNDLE}/Contents/Info.plist"      ) - if (USESYSTEMLIBS)    configure_file(      ${CMAKE_CURRENT_SOURCE_DIR}/English.lproj/InfoPlist.strings      ${CMAKE_CURRENT_BINARY_DIR}/InfoPlist.strings      ) - else (USESYSTEMLIBS) + +  if (FALSE)    add_custom_command(      TARGET ${VIEWER_BINARY_NAME} POST_BUILD      COMMAND ${PYTHON_EXECUTABLE} @@ -2402,11 +2389,11 @@ if (DARWIN)        ${VIEWER_BINARY_NAME}        ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py      ) - endif (USESYSTEMLIBS) +  endif () - if (ENABLE_MEDIA_PLUGINS) +  if (ENABLE_MEDIA_PLUGINS)    add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_libvlc media_plugin_cef) - endif () +  endif ()    if (ENABLE_SIGNING)        set(SIGNING_SETTING "--signature=${SIGNING_IDENTITY}") @@ -2415,26 +2402,22 @@ if (DARWIN)    endif (ENABLE_SIGNING)    if (PACKAGE) -    if (USESYSTEMLIBS) -      set(CPACK_DMG_VOLUME_NAME "${product} Installer" -        CACHE STRING "Disk image volume name.") -      set(CPACK_DMG_FORMAT UDRW CACHE STRING "Disk image format.") -      set(CPACK_DMG_DS_STORE -        ${CMAKE_CURRENT_SOURCE_DIR}/installers/darwin/release-dmg/_DS_Store -        CACHE STRING "Disk image .DS_Store file.") -      set(CPACK_DMG_DS_STORE_SETUP_SCRIPT -        ${CMAKE_CURRENT_SOURCE_DIR}/installers/darwin/dmg-cleanup.applescript -        CACHE STRING "Disk image AppleScript file.") -      set(CPACK_DMG_BACKGROUND_IMAGE -        ${CMAKE_CURRENT_SOURCE_DIR}/installers/darwin/release-dmg/background.jpg -        CACHE STRING "Disk image background image.") -      set(CPACK_BUNDLE_NAME ${product} CACHE STRING "Bundle name.") -      set(CPACK_BUNDLE_PLIST ${VIEWER_APP_BUNDLE}/Contents/Info.plist -        CACHE STRING "Bundle Property List file.") -      set(CPACK_BUNDLE_ICON ${CMAKE_CURRENT_SOURCE_DIR}/secondlife.icns -        CACHE STRING "Bundle icon file.") - -    else (USESYSTEMLIBS) +    set(CPACK_DMG_VOLUME_NAME "${product} Installer" +      CACHE STRING "Disk image volume name.") +    set(CPACK_DMG_FORMAT UDRW CACHE STRING "Disk image format.") +    set(CPACK_DMG_DS_STORE ${CMAKE_CURRENT_SOURCE_DIR}/installers/darwin/release-dmg/_DS_Store +      CACHE STRING "Disk image .DS_Store file.") +    set(CPACK_DMG_DS_STORE_SETUP_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/installers/darwin/dmg-cleanup.applescript +      CACHE STRING "Disk image AppleScript file.") +    set(CPACK_DMG_BACKGROUND_IMAGE ${CMAKE_CURRENT_SOURCE_DIR}/installers/darwin/release-dmg/background.jpg +      CACHE STRING "Disk image background image.") +    set(CPACK_BUNDLE_NAME ${product} CACHE STRING "Bundle name.") +    set(CPACK_BUNDLE_PLIST ${VIEWER_APP_BUNDLE}/Contents/Info.plist +      CACHE STRING "Bundle Property List file.") +    set(CPACK_BUNDLE_ICON ${CMAKE_CURRENT_SOURCE_DIR}/secondlife.icns +      CACHE STRING "Bundle icon file.") + +    if (FALSE)        add_custom_target(llpackage ALL DEPENDS ${VIEWER_BINARY_NAME})        add_custom_command( @@ -2460,16 +2443,16 @@ if (DARWIN)          DEPENDS            ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py        ) -    endif (USESYSTEMLIBS) +    endif ()    endif (PACKAGE)  endif (DARWIN)  if (INSTALL)    include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake)  endif (INSTALL) -if (PACKAGE AND USESYSTEMLIBS) +if (PACKAGE)    include(CPack) -endif (PACKAGE AND USESYSTEMLIBS) +endif ()  if (PACKAGE AND (RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) AND VIEWER_SYMBOL_FILE)    if (USE_BUGSPLAT) diff --git a/indra/newview/FixBundle.cmake.in b/indra/newview/FixBundle.cmake.in index e2fa244577..829c19e5e4 100644 --- a/indra/newview/FixBundle.cmake.in +++ b/indra/newview/FixBundle.cmake.in @@ -10,6 +10,11 @@ file(CREATE_LINK      SYMBOLIC      )  file(CREATE_LINK +    "../../../../Frameworks/libopenjp2.7.dylib" +    "${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/libopenjp2.7.dylib" +    SYMBOLIC +    ) +file(CREATE_LINK      "../../../../Frameworks/libpng16.16.dylib"      "${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/libpng16.16.dylib"      SYMBOLIC @@ -235,6 +240,9 @@ execute_process(      COMMAND lipo libopenal.1.24.2.dylib          -thin ${CMAKE_OSX_ARCHITECTURES}          -output libopenal.1.24.2.dylib +    COMMAND lipo libopenjp2.2.5.3.dylib +        -thin ${CMAKE_OSX_ARCHITECTURES} +        -output libopenjp2.2.5.3.dylib      COMMAND lipo libpng16.16.dylib          -thin ${CMAKE_OSX_ARCHITECTURES}          -output libpng16.16.dylib @@ -301,6 +309,7 @@ execute_process(          Frameworks/libnghttp2.14.dylib          Frameworks/libogg.0.dylib          Frameworks/libopenal.1.24.2.dylib +        Frameworks/libopenjp2.2.5.3.dylib          Frameworks/libpng16.16.dylib          Frameworks/libvlc.5.dylib          Frameworks/libvlccore.9.dylib diff --git a/indra/newview/FixPackage.cmake.in b/indra/newview/FixPackage.cmake.in index 909e07885d..49f7b75b4d 100644 --- a/indra/newview/FixPackage.cmake.in +++ b/indra/newview/FixPackage.cmake.in @@ -10,6 +10,11 @@ file(CREATE_LINK      SYMBOLIC      )  file(CREATE_LINK +    "../../../../Frameworks/libopenjp2.7.dylib" +    "${CMAKE_CACHEFILE_DIR}/_CPack_Packages/${CMAKE_SYSTEM_NAME}/Bundle/${CPACK_BUNDLE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}/${CPACK_BUNDLE_NAME}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/libopenjp2.7.dylib" +    SYMBOLIC +    ) +file(CREATE_LINK      "../../../../Frameworks/libpng16.16.dylib"      "${CMAKE_CACHEFILE_DIR}/_CPack_Packages/${CMAKE_SYSTEM_NAME}/Bundle/${CPACK_BUNDLE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}/${CPACK_BUNDLE_NAME}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/libpng16.16.dylib"      SYMBOLIC @@ -235,6 +240,9 @@ execute_process(      COMMAND lipo libopenal.1.24.2.dylib          -thin ${CMAKE_OSX_ARCHITECTURES}          -output libopenal.1.24.2.dylib +    COMMAND lipo libopenjp2.2.5.3.dylib +        -thin ${CMAKE_OSX_ARCHITECTURES} +        -output libopenjp2.2.5.3.dylib      COMMAND lipo libpng16.16.dylib          -thin ${CMAKE_OSX_ARCHITECTURES}          -output libpng16.16.dylib @@ -301,6 +309,7 @@ execute_process(          Frameworks/libnghttp2.14.dylib          Frameworks/libogg.0.dylib          Frameworks/libopenal.1.24.2.dylib +        Frameworks/libopenjp2.2.5.3.dylib          Frameworks/libpng16.16.dylib          Frameworks/libvlc.5.dylib          Frameworks/libvlccore.9.dylib diff --git a/indra/newview/PKGBUILD.in b/indra/newview/PKGBUILD.in index 4d617c132c..1f0c83cc41 100644 --- a/indra/newview/PKGBUILD.in +++ b/indra/newview/PKGBUILD.in @@ -6,7 +6,7 @@ pkgdesc="${VIEWER_PACKAGE_COMMENT}"  arch=('${CMAKE_SYSTEM_PROCESSOR}')  url="https://${VIEWER_PACKAGE_DOMAIN_NAME}"  license=('LGPL-2.1') -depends=(apr-util boost-libs fltk glu hunspell libnghttp2 sdl2 vlc libvorbis) +depends=(apr-util boost-libs fltk glu hunspell libnghttp2 openjpeg2 sdl2 vlc libvorbis)  package() {  	cd "$startdir" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 1f5ec007d5..f65294b3ec 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10096,6 +10096,83 @@  			<key>Value</key>  			<string>https://megapahit.com/enter_bug.cgi?product=Viewer</string>  		</map> +    <key>RestrainedLove</key> +    <map> +      <key>Comment</key> +      <string>Toggles RLVa features (requires restart)</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <boolean>0</boolean> +    </map> +    <key>RestrainedLoveDebug</key> +    <map> +      <key>Comment</key> +      <string>Toggles RLVa debug mode (displays the commands when in debug mode)</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <boolean>0</boolean> +    </map> +    <key>RLVaBlockedExperiences</key> +    <map> +      <key>Comment</key> +      <string>List of experiences blocked from interacting with RLVa</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>bfe25fb4-222c-11e5-85a2-fa4c4ccaa202</string> +    </map> +    <key>RLVaDebugHideUnsetDuplicate</key> +    <map> +      <key>Comment</key> +      <string>Suppresses reporting "unset" or "duplicate" command restrictions when RestrainedLoveDebug is TRUE</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <boolean>0</boolean> +    </map> +    <key>RLVaEnableTemporaryAttachments</key> +    <map> +      <key>Comment</key> +      <string>Allows temporary attachments (regardless of origin) to issue RLV commands</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <boolean>1</boolean> +    </map> +    <key>RLVaExperimentalCommands</key> +    <map> +      <key>Comment</key> +      <string>Enables the experimental command set</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <boolean>1</boolean> +    </map> +    <key>RLVaTopLevelMenu</key> +    <map> +      <key>Comment</key> +      <string>Show the RLVa specific menu as a top level menu</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <boolean>1</boolean> +    </map>  	<key>RevokePermsOnStopAnimation</key>      <map>        <key>Comment</key> diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp index 5de45119fc..388a6eee01 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -32,11 +32,7 @@  #include "mikktspace/mikktspace.hh" -#if LL_USESYSTEMLIBS  #include <meshoptimizer.h> -#else -#include "meshoptimizer/meshoptimizer.h" -#endif  using namespace LL::GLTF; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f21b02b04e..7580100977 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -255,6 +255,9 @@ using namespace LL;  #include "llviewereventrecorder.h"  #include <chrono> +#include "rlvactions.h" +#include "rlvcommon.h" +#include "rlvhandler.h"  // *FIX: These extern globals should be cleaned up.  // The globals either represent state/config/resource-storage of either @@ -1710,6 +1713,9 @@ bool LLAppViewer::cleanup()      //ditch LLVOAvatarSelf instance      gAgentAvatarp = NULL; +    // Sanity check to catch cases where someone forgot to do an RlvActions::isRlvEnabled() check +    LL_ERRS_IF(!RlvHandler::isEnabled() && RlvHandler::instanceExists()) << "RLV handler instance exists even though RLVa is disabled" << LL_ENDL; +      LLNotifications::instance().clear();      // workaround for DEV-35406 crash on shutdown @@ -3371,6 +3377,7 @@ LLSD LLAppViewer::getViewerInfo() const      }  #endif +    info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? Rlv::Strings::getVersionAbout() : "(disabled)";      info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION)));      // Settings diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 72295ffba9..feb7ed9774 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -209,6 +209,7 @@  #include "threadpool.h"  #include "llperfstats.h" +#include "rlvhandler.h"  #if LL_WINDOWS  #include "lldxhardware.h" @@ -934,6 +935,8 @@ bool idle_startup()              return false;          } +        RlvHandler::setEnabled(gSavedSettings.get<bool>(Rlv::Settings::Main)); +          // reset the values that could have come in from a slurl          // DEV-42215: Make sure they're not empty -- gUserCredential          // might already have been set from gSavedSettings, and it's too bad diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 598ad89907..9043a5a89e 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -76,6 +76,7 @@  #include "llslurl.h"  #include "llstartup.h"  #include "llperfstats.h" +#include "rlvcommon.h"  #if LL_DARWIN  #include "llwindowmacosx.h" @@ -972,6 +973,8 @@ void settings_setup_listeners()      setting_setup_signal_listener(gSavedSettings, "TerrainPaintBitDepth", handleSetShaderChanged);      setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged); + +    setting_setup_signal_listener(gSavedSettings, Rlv::Settings::TopLevelMenu, Rlv::Util::menuToggleVisible);  }  #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 39c5b9f218..89ccf49f14 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -178,6 +178,7 @@  #include "llpreviewtexture.h"  #include "llscriptfloater.h"  #include "llsyswellwindow.h" +#include "rlvfloaters.h"  #include "fsfloatersearch.h" @@ -490,6 +491,7 @@ void LLViewerFloaterReg::registerFloaters()      LLFloaterReg::add("region_debug_console", "floater_region_debug_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionDebugConsole>);      LLFloaterReg::add("region_info", "floater_region_info.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionInfo>);      LLFloaterReg::add("region_restarting", "floater_region_restarting.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionRestarting>); +    LLFloaterReg::add("rlv_console", "floater_rlv_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<Rlv::FloaterConsole>);      LLFloaterReg::add("script_debug", "floater_script_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebug>);      LLFloaterReg::add("script_debug_output", "floater_script_debug_panel.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebugOutput>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 1f8f960300..63527d8594 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -152,6 +152,7 @@  #include "llviewershadermgr.h"  #include "gltfscenemanager.h"  #include "gltf/asset.h" +#include "rlvcommon.h"  using namespace LLAvatarAppearanceDefines; @@ -6284,6 +6285,8 @@ void show_debug_menus()          gMenuBarView->setItemVisible("Advanced", debug);  //      gMenuBarView->setItemEnabled("Advanced", debug); // Don't disable Advanced keyboard shortcuts when hidden +        Rlv::Util::menuToggleVisible(); +          gMenuBarView->setItemVisible("Debug", qamode);          gMenuBarView->setItemEnabled("Debug", qamode); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1501ba41c2..b35be7d385 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -118,6 +118,8 @@  #include "llpanelplaceprofile.h"  #include "llviewerregion.h"  #include "llfloaterregionrestarting.h" +#include "rlvactions.h" +#include "rlvhandler.h"  #include "llnotificationmanager.h" //  #include "llexperiencecache.h" @@ -2398,15 +2400,16 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)      }      bool is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); +    bool show_script_chat_particles = chat.mSourceType == CHAT_SOURCE_OBJECT +        && chat.mChatType != CHAT_TYPE_DEBUG_MSG +        && gSavedSettings.getBOOL("EffectScriptChatParticles");      chatter = gObjectList.findObject(from_id);      if (chatter)      {          chat.mPosAgent = chatter->getPositionAgent();          // Make swirly things only for talking objects. (not script debug messages, though) -        if (chat.mSourceType == CHAT_SOURCE_OBJECT -            && chat.mChatType != CHAT_TYPE_DEBUG_MSG -            && gSavedSettings.getBOOL("EffectScriptChatParticles") ) +        if (show_script_chat_particles && (!RlvActions::isRlvEnabled() || CHAT_TYPE_OWNER != chat.mChatType) )          {              LLPointer<LLViewerPartSourceChat> psc = new LLViewerPartSourceChat(chatter->getPositionAgent());              psc->setSourceObject(chatter); @@ -2486,8 +2489,25 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)              case CHAT_TYPE_WHISPER:                  chat.mText = LLTrans::getString("whisper") + " ";                  break; -            case CHAT_TYPE_DEBUG_MSG:              case CHAT_TYPE_OWNER: +                if (RlvActions::isRlvEnabled()) +                { +                    if (RlvHandler::instance().handleSimulatorChat(mesg, chat, chatter)) +                    { +                        break; +                    } +                    else if (show_script_chat_particles) +                    { +                        LLPointer<LLViewerPartSourceChat> psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); +                        psc->setSourceObject(chatter); +                        psc->setColor(color); +                        //We set the particles to be owned by the object's owner, +                        //just in case they should be muted by the mute list +                        psc->setOwnerUUID(owner_id); +                        LLViewerPartSim::getInstance()->addPartSource(psc); +                    } +                } +            case CHAT_TYPE_DEBUG_MSG:              case CHAT_TYPE_NORMAL:              case CHAT_TYPE_DIRECT:                  break; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index d72d428c08..c15fd86f71 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -68,7 +68,7 @@  #include "u64.h"  #include "llviewertexturelist.h"  #include "lldatapacker.h" -#ifdef LL_USESYSTEMLIBS +#if 1  #include <zlib.h>  #else  #include "zlib-ng/zlib.h" diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 7faef8cc41..14f0b52174 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -36,7 +36,7 @@  #include "llbufferstream.h"  #include "llfile.h"  #include "llmenugl.h" -#ifdef LL_USESYSTEMLIBS +#if 1  # include "expat.h"  #else  # include "expat/expat.h" diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 3e0a237905..ca1258e05e 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -43,7 +43,7 @@ class LLVivoxProtocolParser;  #include "llmutelist.h"  #include <queue> -#ifdef LL_USESYSTEMLIBS +#if 1  # include "expat.h"  #else  # include "expat/expat.h" diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 08fcec86ac..c2b74eb1dd 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -34,7 +34,7 @@  #include "llbufferstream.h"  #include "llfile.h"  #include "llmenugl.h" -#ifdef LL_USESYSTEMLIBS +#if 1  # include "expat.h"  #else  # include "expat/expat.h" diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ff82d2739d..946a80e874 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -43,7 +43,7 @@ class LLWebRTCProtocolParser;  #include <queue>  #include "boost/json.hpp" -#ifdef LL_USESYSTEMLIBS +#if 1  # include "expat.h"  #else  # include "expat/expat.h" diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp new file mode 100644 index 0000000000..110beeafc0 --- /dev/null +++ b/indra/newview/rlvactions.cpp @@ -0,0 +1,42 @@ +/** + * @file rlvactions.cpp + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "rlvactions.h" +#include "rlvhandler.h" + +// ============================================================================ +// Helper functions +// + +bool RlvActions::isRlvEnabled() +{ +    return RlvHandler::isEnabled(); +} + +// ============================================================================ diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h new file mode 100644 index 0000000000..cb0df95e37 --- /dev/null +++ b/indra/newview/rlvactions.h @@ -0,0 +1,46 @@ +/** + * @file rlvactions.h + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +// ============================================================================ +// RlvActions - developer-friendly non-RLVa code facing class, use in lieu of RlvHandler whenever possible +// + +class RlvActions +{ +    // ================ +    // Helper functions +    // ================ +public: +    /* +     * Convenience function to check if RLVa is enabled without having to include rlvhandler.h +     */ +    static bool isRlvEnabled(); +}; + +// ============================================================================ diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp new file mode 100644 index 0000000000..4140659715 --- /dev/null +++ b/indra/newview/rlvcommon.cpp @@ -0,0 +1,134 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llchat.h" +#include "lldbstrings.h" +#include "llversioninfo.h" +#include "llviewermenu.h" +#include "llviewerstats.h" +#include "message.h" +#include <boost/algorithm/string.hpp> + +#include "rlvcommon.h" + +#include "llviewercontrol.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// RlvStrings +// + +std::string Strings::getVersion(bool wants_legacy) +{ +    return llformat("%s viewer v%d.%d.%d (RLVa %d.%d.%d)", +        !wants_legacy ? "RestrainedLove" : "RestrainedLife", +        SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, +        ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch); +} + +std::string Strings::getVersionAbout() +{ +    return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d", +        SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, +        ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, LLVersionInfo::instance().getBuild()); +} + +std::string Strings::getVersionNum() +{ +    return llformat("%d%02d%02d%02d", +        SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, SpecVersion::Build); +} + +std::string Strings::getVersionImplNum() +{ +    return llformat("%d%02d%02d%02d", +        ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, ImplVersion::ImplId); +} + +// ============================================================================ +// RlvUtil +// + +void Util::menuToggleVisible() +{ +    bool isTopLevel = gSavedSettings.getBOOL(Settings::TopLevelMenu); +    bool isRlvEnabled = RlvHandler::isEnabled(); + +    LLMenuGL* menuRLVaMain = gMenuBarView->findChildMenuByName("RLVa Main", false); +    LLMenuGL* menuAdvanced = gMenuBarView->findChildMenuByName("Advanced", false); +    LLMenuGL* menuRLVaEmbed= menuAdvanced->findChildMenuByName("RLVa Embedded", false); + +    gMenuBarView->setItemVisible("RLVa Main", isRlvEnabled && isTopLevel); +    menuAdvanced->setItemVisible("RLVa Embedded", isRlvEnabled && !isTopLevel); + +    if ( isRlvEnabled && menuRLVaMain && menuRLVaEmbed && +         ( (isTopLevel && 1 == menuRLVaMain->getItemCount()) || (!isTopLevel && 1 == menuRLVaEmbed->getItemCount())) ) +    { +        LLMenuGL* menuFrom = isTopLevel ? menuRLVaEmbed : menuRLVaMain; +        LLMenuGL* menuTo = isTopLevel ? menuRLVaMain : menuRLVaEmbed; +        while (LLMenuItemGL* pItem = menuFrom->getItem(1)) +        { +            menuFrom->removeChild(pItem); +            menuTo->addChild(pItem); +            pItem->updateBranchParent(menuTo); +        } +    } +} + +bool Util::parseStringList(const std::string& strInput, std::vector<std::string>& optionList, std::string_view strSeparator) +{ +    if (!strInput.empty()) +        boost::split(optionList, strInput, boost::is_any_of(strSeparator)); +    return !optionList.empty(); +} + +bool Util::sendChatReply(S32 nChannel, const std::string& strUTF8Text) +{ +    if (!isValidReplyChannel(nChannel)) +        return false; + +    // Copy/paste from send_chat_from_viewer() +    gMessageSystem->newMessageFast(_PREHASH_ChatFromViewer); +    gMessageSystem->nextBlockFast(_PREHASH_AgentData); +    gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); +    gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); +    gMessageSystem->nextBlockFast(_PREHASH_ChatData); +    gMessageSystem->addStringFast(_PREHASH_Message, utf8str_truncate(strUTF8Text, MAX_MSG_STR_LEN)); +    gMessageSystem->addU8Fast(_PREHASH_Type, CHAT_TYPE_SHOUT); +    gMessageSystem->addS32("Channel", nChannel); +    gAgent.sendReliableMessage(); +    add(LLStatViewer::CHAT_COUNT, 1); + +    return true; +} + +// ============================================================================ diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h new file mode 100644 index 0000000000..6f1bbbbdc6 --- /dev/null +++ b/indra/newview/rlvcommon.h @@ -0,0 +1,72 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "rlvdefines.h" + +namespace Rlv +{ +    // ============================================================================ +    // RlvStrings +    // + +    class Strings +    { +    public: +        static std::string        getVersion(bool wants_legacy); +        static std::string        getVersionAbout(); +        static std::string        getVersionImplNum(); +        static std::string        getVersionNum(); +    }; + +    // ============================================================================ +    // RlvUtil +    // + +    namespace Util +    { +        bool isValidReplyChannel(S32 nChannel, bool isLoopback = false); +        void menuToggleVisible(); +        bool parseStringList(const std::string& strInput, std::vector<std::string>& optionList, std::string_view strSeparator = Constants::OptionSeparator); +        bool sendChatReply(S32 nChannel, const std::string& strUTF8Text); +        bool sendChatReply(const std::string& strChannel, const std::string& strUTF8Text); +    }; + +    inline bool Util::isValidReplyChannel(S32 nChannel, bool isLoopback) +    { +        return (nChannel > (!isLoopback ? 0 : -1)) && (CHAT_CHANNEL_DEBUG != nChannel); +    } + +    inline bool Util::sendChatReply(const std::string& strChannel, const std::string& strUTF8Text) +    { +        S32 nChannel; +        return LLStringUtil::convertToS32(strChannel, nChannel) ? sendChatReply(nChannel, strUTF8Text) : false; +    } + +    // ============================================================================ +} diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h new file mode 100644 index 0000000000..e39328fdd6 --- /dev/null +++ b/indra/newview/rlvdefines.h @@ -0,0 +1,194 @@ +/** + * @file rlvdefines.h + * @author Kitty Barnett + * @brief RLVa common defines, constants and enums + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +// ============================================================================ +// Defines +// + +// Defining these makes it easier if we ever need to change our tag +#define RLV_WARNS		LL_WARNS("RLV") +#define RLV_INFOS		LL_INFOS("RLV") +#define RLV_DEBUGS		LL_DEBUGS("RLV") +#define RLV_ENDL		LL_ENDL +#define RLV_VERIFY(f)	(f) + +#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG +    // Make sure we halt execution on errors +    #define RLV_ERRS				LL_ERRS("RLV") +    // Keep our asserts separate from LL's +    #define RLV_ASSERT(f)			if (!(f)) { RLV_ERRS << "ASSERT (" << #f << ")" << RLV_ENDL; } +    #define RLV_ASSERT_DBG(f)		RLV_ASSERT(f) +#else +    // Don't halt execution on errors in release +    #define RLV_ERRS				LL_WARNS("RLV") +    // We don't want to check assertions in release builds +    #ifdef RLV_DEBUG +        #define RLV_ASSERT(f)		RLV_VERIFY(f) +        #define RLV_ASSERT_DBG(f) +    #else +        #define RLV_ASSERT(f) +        #define RLV_ASSERT_DBG(f) +    #endif // RLV_DEBUG +#endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + +namespace Rlv +{ +    // Version of the specification we report +    namespace SpecVersion { +        constexpr S32 Major = 4; +        constexpr S32 Minor = 0; +        constexpr S32 Patch = 0; +        constexpr S32 Build = 0; +    }; + +    // RLVa implementation version +    namespace ImplVersion { +        constexpr S32 Major = 3; +        constexpr S32 Minor = 0; +        constexpr S32 Patch = 0; +        constexpr S32 ImplId = 13; +    }; + +    namespace Constants +    { +        constexpr char CmdPrefix = '@'; +        constexpr char ConsolePrompt[] = "> "; +        constexpr std::string_view OptionSeparator = ";"; +    } +} + +// ============================================================================ +// Enumeration declarations +// + +namespace Rlv +{ +    enum class EBehaviour { +        Version = 0, +        VersionNew, +        VersionNum, +        GetCommand, + +        Count, +        Unknown, +    }; + +    enum class EBehaviourOptionType +    { +        EmptyOrException,                   // Behaviour takes no parameters +        Exception,                          // Behaviour requires an exception as a parameter +        NoneOrException,                    // Behaviour takes either no parameters or an exception +    }; + +    enum class EParamType { +        Unknown = 0x00, +        Add     = 0x01,                 // <param> == "n"|"add" +        Remove  = 0x02,                 // <param> == "y"|"rem" +        Force   = 0x04,                 // <param> == "force" +        Reply   = 0x08,                 // <param> == <number> +        Clear   = 0x10, +        AddRem  = Add | Remove +    }; + +    enum class ECmdRet { +        Unknown = 0x0000,               // Unknown error (should only be used internally) +        Retained,                       // Command was retained +        Succeeded = 0x0100,             // Command executed successfully +        SuccessUnset,                   // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) +        SuccessDuplicate,               // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) +        SuccessDeprecated,              // Command executed successfully but has been marked as deprecated +        SuccessDelayed,                 // Command parsed valid but will execute at a later time +        Failed = 0x0200,                // Command failed (general failure) +        FailedSyntax,                   // Command failed (syntax error) +        FailedOption,                   // Command failed (invalid option) +        FailedParam,                    // Command failed (invalid param) +        FailedLock,                     // Command failed (command is locked by another object) +        FailedDisabled,                 // Command failed (command disabled by user) +        FailedUnknown,                  // Command failed (unknown command) +        FailedNoSharedRoot,             // Command failed (missing #RLV) +        FailedDeprecated,               // Command failed (deprecated and no longer supported) +        FailedNoBehaviour,              // Command failed (force modifier on an object with no active restrictions) +        FailedUnheldBehaviour,          // Command failed (local modifier on an object that doesn't hold the base behaviour) +        FailedBlocked,                  // Command failed (object is blocked) +        FailedThrottled,                // Command failed (throttled) +        FailedNoProcessor               // Command doesn't have a template processor define (legacy code) +    }; + +    enum class EExceptionCheck +    { +        Permissive,						// Exception can be set by any object +        Strict,							// Exception must be set by all objects holding the restriction +        Default,						// Permissive or strict will be determined by currently enforced restrictions +    }; + +    // Replace&remove in c++23 +    template <typename E> +    constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, std::underlying_type_t<E>> to_underlying(E e) noexcept +    { +        return static_cast<std::underlying_type_t<E>>(e); +    } + +    template <typename E> +    constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, bool> has_flag(E value, E flag) noexcept +    { +        return (to_underlying(value) & to_underlying(flag)) != 0; +    } + +    constexpr bool isReturnCodeSuccess(ECmdRet eRet) +    { +        return (to_underlying(eRet) & to_underlying(ECmdRet::Succeeded)) == to_underlying(ECmdRet::Succeeded); +    } + +    constexpr bool isReturnCodeFailed(ECmdRet eRet) +    { +        return (to_underlying(eRet) & to_underlying(ECmdRet::Failed)) == to_underlying(ECmdRet::Failed); +    } +} + +// ============================================================================ +// Settings +// + +namespace Rlv +{ +    namespace Settings +    { +        constexpr char Main[] = "RestrainedLove"; +        constexpr char Debug[] = "RestrainedLoveDebug"; + +        constexpr char DebugHideUnsetDup[] = "RLVaDebugHideUnsetDuplicate"; +        constexpr char EnableExperimentalCommands[] = "RLVaExperimentalCommands"; +        constexpr char EnableIMQuery[] = "RLVaEnableIMQuery"; +        constexpr char EnableTempAttach[] = "RLVaEnableTemporaryAttachments"; +        constexpr char TopLevelMenu[] = "RLVaTopLevelMenu"; +    }; + +}; + +// ============================================================================ diff --git a/indra/newview/rlvfloaters.cpp b/indra/newview/rlvfloaters.cpp new file mode 100644 index 0000000000..8a074fd14d --- /dev/null +++ b/indra/newview/rlvfloaters.cpp @@ -0,0 +1,122 @@ +/** + * @file rlvfloaters.cpp + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagentdata.h" +#include "llchatentry.h" +#include "lltexteditor.h" +#include "lltrans.h" +#include "llvoavatarself.h" + +#include "rlvfloaters.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// FloaterConsole +// + +bool FloaterConsole::postBuild() +{ +    mInputEdit = getChild<LLChatEntry>("console_input"); +    mInputEdit->setCommitCallback(std::bind(&FloaterConsole::onInput, this)); +    mInputEdit->setTextExpandedCallback(std::bind(&FloaterConsole::reshapeLayoutPanel, this)); +    mInputEdit->setFocus(true); +    mInputEdit->setCommitOnFocusLost(false); + +    mInputPanel = getChild<LLLayoutPanel>("input_panel"); +    mInputEditPad = mInputPanel->getRect().getHeight() - mInputEdit->getRect().getHeight(); + +    mOutputText = getChild<LLTextEditor>("console_output"); +    mOutputText->appendText(Constants::ConsolePrompt, false); + +    if (RlvHandler::isEnabled()) +    { +        mCommandOutputConn = RlvHandler::instance().setCommandOutputCallback([this](const RlvCommand& rlvCmd, S32, const std::string strText) +        { +            if (rlvCmd.getObjectID() == gAgentID) +            { +                mOutputText->appendText(rlvCmd.getBehaviour() + ": ", true); +                mOutputText->appendText(strText, false); +            } +        }); +    } + +    return true; +} + +void FloaterConsole::onClose(bool fQuitting) +{ +    if (RlvHandler::isEnabled()) +    { +        RlvHandler::instance().processCommand(gAgentID, "clear", true); +    } +} + +void FloaterConsole::onInput() +{ +    if (!isAgentAvatarValid()) +    { +        return; +    } + +    std::string strText = mInputEdit->getText(); +    LLStringUtil::trim(strText); + +    mOutputText->appendText(strText, false); +    mInputEdit->setText(LLStringUtil::null); + +    if (!RlvHandler::isEnabled()) +    { +        mOutputText->appendText(LLTrans::getString("RlvConsoleDisable"), true); +    } +    else if (strText.length() <= 3 || Constants::CmdPrefix != strText[0]) +    { +        mOutputText->appendText(LLTrans::getString("RlvConsoleInvalidCmd"), true); +    } +    else +    { +        LLChat chat; +        chat.mFromID = gAgentID; +        chat.mChatType = CHAT_TYPE_OWNER; + +        RlvHandler::instance().handleSimulatorChat(strText, chat, gAgentAvatarp); + +        mOutputText->appendText(strText, true); +    } + +    mOutputText->appendText(Constants::ConsolePrompt, true); +} + +void FloaterConsole::reshapeLayoutPanel() +{ +    mInputPanel->reshape(mInputPanel->getRect().getWidth(), mInputEdit->getRect().getHeight() + mInputEditPad, false); +} + +// ============================================================================ diff --git a/indra/newview/rlvfloaters.h b/indra/newview/rlvfloaters.h new file mode 100644 index 0000000000..8acfa43f28 --- /dev/null +++ b/indra/newview/rlvfloaters.h @@ -0,0 +1,68 @@ +/** + * @file rlvfloaters.h + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "llfloater.h" + +#include "rlvdefines.h" + +class LLChatEntry; +class LLFloaterReg; +class LLLayoutPanel; +class LLTextEditor; +class RlvCommand; +class RlvHandler; + +namespace Rlv +{ +    // ============================================================================ +    // FloaterConsole - debug console to allow command execution without the need for a script +    // + +    class FloaterConsole : public LLFloater +    { +        friend class ::LLFloaterReg; +        FloaterConsole(const LLSD& sdKey) : LLFloater(sdKey) {} + +    public: +        bool postBuild() override; +        void onClose(bool fQuitting) override; +    protected: +        void onInput(); +        void reshapeLayoutPanel(); + +    private: +        boost::signals2::scoped_connection mCommandOutputConn; +        int mInputEditPad = 0; +        LLLayoutPanel* mInputPanel = nullptr; +        LLChatEntry* mInputEdit = nullptr; +        LLTextEditor* mOutputText = nullptr; +    }; + +    // ============================================================================ +}; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp new file mode 100644 index 0000000000..6c4b439105 --- /dev/null +++ b/indra/newview/rlvhandler.cpp @@ -0,0 +1,225 @@ +/** + * @file rlvhandler.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llagent.h" +#include "llstartup.h" +#include "llviewercontrol.h" +#include "llviewerobject.h" + +#include "rlvcommon.h" +#include "rlvhandler.h" +#include "rlvhelper.h" + +#include <boost/algorithm/string.hpp> + +using namespace Rlv; + +// ============================================================================ +// Static variable initialization +// + +bool RlvHandler::mIsEnabled = false; + +// ============================================================================ +// Command processing functions +// + +bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj) +{ +    // *TODO: There's an edge case for temporary attachments when going from enabled -> disabled with restrictions already in place +    static LLCachedControl<bool> enable_temp_attach(gSavedSettings, Settings::EnableTempAttach); +    static LLCachedControl<bool> show_debug_output(gSavedSettings, Settings::Debug); +    static LLCachedControl<bool> hide_unset_dupes(gSavedSettings, Settings::DebugHideUnsetDup); + +    if ( message.length() <= 3 || Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType || +         (chatObj && chatObj->isTempAttachment() && !enable_temp_attach()) ) +    { +        return false; +    } + +    message.erase(0, 1); +    LLStringUtil::toLower(message); +    CommandDbgOut cmdDbgOut(message, chatObj->getID() == gAgentID); + +    boost_tokenizer tokens(message, boost::char_separator<char>(",", "", boost::drop_empty_tokens)); +    for (const std::string& strCmd : tokens) +    { +        ECmdRet eRet = processCommand(chat.mFromID, strCmd, true); +        if ( show_debug_output() && +             (!hide_unset_dupes() || (ECmdRet::SuccessUnset != eRet && ECmdRet::SuccessDuplicate != eRet)) ) +        { +            cmdDbgOut.add(strCmd, eRet); +        } +    } + +    message = cmdDbgOut.get(); +    return true; +} + +ECmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCmd, bool fromObj) +{ +    const RlvCommand rlvCmd(idObj, strCmd); +    return processCommand(std::ref(rlvCmd), fromObj); +} + +ECmdRet RlvHandler::processCommand(std::reference_wrapper<const RlvCommand> rlvCmd, bool fromObj) +{ +    { +        const RlvCommand& rlvCmdTmp = rlvCmd; // Reference to the temporary with limited variable scope since we don't want it to leak below + +        RLV_DEBUGS << "[" << rlvCmdTmp.getObjectID() << "]: " << rlvCmdTmp.asString() << RLV_ENDL; +        if (!rlvCmdTmp.isValid()) +        { +            RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; +            return ECmdRet::FailedSyntax; +        } +        if (rlvCmdTmp.isBlocked()) +        { +            RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL; +            return ECmdRet::FailedDisabled; +        } +    } + +    ECmdRet eRet = ECmdRet::Unknown; +    switch (rlvCmd.get().getParamType()) +    { +        case EParamType::Reply: +            eRet = rlvCmd.get().processCommand(); +            break; +        case EParamType::Unknown: +        default: +            eRet = ECmdRet::FailedParam; +            break; +    } +    RLV_ASSERT(ECmdRet::Unknown != eRet); + +    RLV_DEBUGS << "\t--> command " << (isReturnCodeSuccess(eRet) ? "succeeded" : "failed") << RLV_ENDL; + +    return eRet; +} + +// ============================================================================ +// Initialization helper functions +// + +bool RlvHandler::canEnable() +{ +    return LLStartUp::getStartupState() <= STATE_LOGIN_CLEANUP; +} + +bool RlvHandler::setEnabled(bool enable) +{ +    if (mIsEnabled == enable) +        return enable; + +    if (enable && canEnable()) +    { +        RLV_INFOS << "Enabling Restrained Love API support - " << Strings::getVersionAbout() << RLV_ENDL; +        mIsEnabled = true; +    } + +    return mIsEnabled; +} + +// ============================================================================ +// Command handlers (RLV_TYPE_REPLY) +// + +ECmdRet CommandHandlerBaseImpl<EParamType::Reply>::processCommand(const RlvCommand& rlvCmd, ReplyHandlerFunc* pHandler) +{ +    // Sanity check - <param> should specify a - valid - reply channel +    S32 nChannel; +    if (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel) || !Util::isValidReplyChannel(nChannel, rlvCmd.getObjectID() == gAgentID)) +        return ECmdRet::FailedParam; + +    std::string strReply; +    ECmdRet eRet = (*pHandler)(rlvCmd, strReply); + +    // If we made it this far then: +    //   - the command was handled successfully so we send off the response +    //   - the command failed but we still send off an - empty - response to keep the issuing script from blocking +    if (nChannel != 0) +    { +        Util::sendChatReply(nChannel, strReply); +    } +    RlvHandler::instance().mOnCommandOutput(rlvCmd, nChannel, strReply); + +    return eRet; +} + +// Handles: @getcommand[:<behaviour>[;<type>[;<separator>]]]=<channel> +template<> template<> +ECmdRet ReplyHandler<EBehaviour::GetCommand>::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ +    std::vector<std::string> optionList; +    Util::parseStringList(rlvCmd.getOption(), optionList); + +    // If a second parameter is present it'll specify the command type +    EParamType eType = EParamType::Unknown; +    if (optionList.size() >= 2) +    { +        if (optionList[1] == "any" || optionList[1].empty()) +            eType = EParamType::Unknown; +        else if (optionList[1] == "add") +            eType = EParamType::AddRem; +        else if (optionList[1] == "force") +            eType = EParamType::Force; +        else if (optionList[1] == "reply") +            eType = EParamType::Reply; +        else +            return ECmdRet::FailedOption; +    } + +    std::list<std::string> cmdList; +    if (BehaviourDictionary::instance().getCommands(!optionList.empty() ? optionList[0] : LLStringUtil::null, eType, cmdList)) +        strReply = boost::algorithm::join(cmdList, optionList.size() >= 3 ? optionList[2] : Constants::OptionSeparator); +    return ECmdRet::Succeeded; +} + +// Handles: @version=<chnannel> and @versionnew=<channel> +template<> template<> +ECmdRet VersionReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ +    strReply = Strings::getVersion(EBehaviour::Version == rlvCmd.getBehaviourType()); +    return ECmdRet::Succeeded; +} + +// Handles: @versionnum[:impl]=<channel> +template<> template<> +ECmdRet ReplyHandler<EBehaviour::VersionNum>::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ +    if (!rlvCmd.hasOption()) +        strReply = Strings::getVersionNum(); +    else if ("impl" == rlvCmd.getOption()) +        strReply = Strings::getVersionImplNum(); +    else +        return ECmdRet::FailedOption; +    return ECmdRet::Succeeded; +} + +// ============================================================================ diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h new file mode 100644 index 0000000000..38612485b1 --- /dev/null +++ b/indra/newview/rlvhandler.h @@ -0,0 +1,80 @@ +/** + * @file rlvhandler.h + * @author Kitty Barnett + * @brief Primary command process and orchestration class + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "llchat.h" +#include "llsingleton.h" + +#include "rlvhelper.h" + +class LLViewerObject; + +// ============================================================================ +// RlvHandler class +// + +class RlvHandler : public LLSingleton<RlvHandler> +{ +    template<Rlv::EParamType> friend struct Rlv::CommandHandlerBaseImpl; + +    LLSINGLETON_EMPTY_CTOR(RlvHandler); + +    /* +     * Command processing +     */ +public: +    // Command processing helper functions +    bool         handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj); +    Rlv::ECmdRet processCommand(const LLUUID& idObj, const std::string& stCmd, bool fromObj); +protected: +    Rlv::ECmdRet processCommand(std::reference_wrapper<const RlvCommand> rlvCmdRef, bool fromObj); + +    /* +     * Helper functions +     */ +public: +    // Initialization (deliberately static so they can safely be called in tight loops) +    static bool canEnable(); +    static bool isEnabled() { return mIsEnabled; } +    static bool setEnabled(bool enable); + +    /* +     * Event handling +     */ +public: +    // The command output signal is triggered whenever a command produces channel or debug output +    using command_output_signal_t = boost::signals2::signal<void (const RlvCommand&, S32, const std::string&)>; +    boost::signals2::connection setCommandOutputCallback(const command_output_signal_t::slot_type& cb) { return mOnCommandOutput.connect(cb); } + +protected: +    command_output_signal_t mOnCommandOutput; +private: +    static bool mIsEnabled; +}; + +// ============================================================================ diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp new file mode 100644 index 0000000000..7cb1473c8c --- /dev/null +++ b/indra/newview/rlvhelper.cpp @@ -0,0 +1,389 @@ +/** + * @file rlvhelper.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltrans.h" +#include "llviewercontrol.h" + +#include "rlvhelper.h" + +#include <boost/algorithm/string.hpp> + +using namespace Rlv; + +// ============================================================================ +// BehaviourDictionary +// + +BehaviourDictionary::BehaviourDictionary() +{ +    // +    // Restrictions +    // + +    // +    // Reply-only +    // +    addEntry(new ReplyProcessor<EBehaviour::GetCommand>("getcommand")); +    addEntry(new ReplyProcessor<EBehaviour::Version, VersionReplyHandler>("version")); +    addEntry(new ReplyProcessor<EBehaviour::VersionNew, VersionReplyHandler>("versionnew")); +    addEntry(new ReplyProcessor<EBehaviour::VersionNum>("versionnum")); + +    // Populate mString2InfoMap (the tuple <behaviour, type> should be unique) +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        mString2InfoMap.insert(std::make_pair(std::make_pair(bhvr_info_p->getBehaviour(), static_cast<EParamType>(bhvr_info_p->getParamTypeMask())), bhvr_info_p)); +    } + +    // Populate m_Bhvr2InfoMap (there can be multiple entries per ERlvBehaviour) +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        if ((bhvr_info_p->getParamTypeMask() & to_underlying(EParamType::AddRem)) && !bhvr_info_p->isSynonym()) +        { +#ifdef RLV_DEBUG +            for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(bhvr_info_p->getBehaviourType()), mBhvr2InfoMap.upper_bound(bhvr_info_p->getBehaviourType()))) +            { +                RLV_ASSERT((itBhvr.first != bhvr_info_p->getBehaviourType()) || (itBhvr.second->getBehaviourFlags() != bhvr_info_p->getBehaviourFlags())); +            } +#endif // RLV_DEBUG +            mBhvr2InfoMap.insert(std::pair(bhvr_info_p->getBehaviourType(), bhvr_info_p)); +        } +    } +} + +BehaviourDictionary::~BehaviourDictionary() +{ +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        delete bhvr_info_p; +    } +    mBhvrInfoList.clear(); +} + +void BehaviourDictionary::addEntry(const BehaviourInfo* entry_p) +{ +    // Filter experimental commands (if disabled) +    static LLCachedControl<bool> sEnableExperimental(gSavedSettings, Settings::EnableExperimentalCommands); +    if (!entry_p || (!sEnableExperimental && entry_p->isExperimental())) +    { +        return; +    } + +    // Sanity check for duplicate entries +#ifndef LL_RELEASE_FOR_DOWNLOAD +    std::for_each(mBhvrInfoList.begin(), mBhvrInfoList.end(), +        [&entry_p](const BehaviourInfo* bhvr_info_p) { +            RLV_ASSERT_DBG((bhvr_info_p->getBehaviour() != entry_p->getBehaviour()) || ((bhvr_info_p->getParamTypeMask() & entry_p->getParamTypeMask()) == 0)); +        }); +#endif // LL_RELEASE_FOR_DOWNLOAD + +    mBhvrInfoList.push_back(entry_p); +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const +{ +    const BehaviourInfo* bhvr_info_p = nullptr; +    for (auto itBhvrLower = mBhvr2InfoMap.lower_bound(eBhvr), itBhvrUpper = mBhvr2InfoMap.upper_bound(eBhvr); +        std::find_if(itBhvrLower, itBhvrUpper, [eParamType](const auto& bhvrEntry) { return bhvrEntry.second->getParamTypeMask() == to_underlying(eParamType); }) != itBhvrUpper; +        ++itBhvrLower) +    { +        if (bhvr_info_p) +            return nullptr; +        bhvr_info_p = itBhvrLower->second; +    } +    return bhvr_info_p; +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p) const +{ +    size_t idxBhvrLastPart = strBhvr.find_last_of('_'); +    std::string strBhvrLastPart((std::string::npos != idxBhvrLastPart) && (idxBhvrLastPart < strBhvr.size()) ? strBhvr.substr(idxBhvrLastPart + 1) : LLStringUtil::null); + +    bool isStrict = (strBhvrLastPart.compare("sec") == 0); +    if (is_strict_p) +        *is_strict_p = isStrict; + +    auto itBhvr = mString2InfoMap.find(std::make_pair((!isStrict) ? strBhvr : strBhvr.substr(0, strBhvr.size() - 4), (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); +    if ((mString2InfoMap.end() == itBhvr) && (!isStrict) && (!strBhvrLastPart.empty()) && (EParamType::Force == eParamType)) +    { +        // No match found but it could still be a local scope modifier +        auto itBhvrMod = mString2InfoMap.find(std::make_pair(strBhvr.substr(0, idxBhvrLastPart), EParamType::AddRem)); +    } + +    return ((itBhvr != mString2InfoMap.end()) && ((!isStrict) || (itBhvr->second->hasStrict()))) ? itBhvr->second : nullptr; +} + +EBehaviour BehaviourDictionary::getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* pisStrict) const +{ +    const BehaviourInfo* bhvr_info_p = getBehaviourInfo(strBhvr, eParamType, pisStrict); +    // Filter out locally scoped modifier commands since they don't actually have a unique behaviour value of their own +    return bhvr_info_p->getBehaviourType(); +} + +bool BehaviourDictionary::getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const +{ +    cmdList.clear(); +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        if ((bhvr_info_p->getParamTypeMask() & to_underlying(eParamType)) || (EParamType::Unknown == eParamType)) +        { +            std::string strCmd = bhvr_info_p->getBehaviour(); +            if ((std::string::npos != strCmd.find(strMatch)) || (strMatch.empty())) +                cmdList.push_back(strCmd); +            if ((bhvr_info_p->hasStrict()) && ((std::string::npos != strCmd.append("_sec").find(strMatch)) || (strMatch.empty()))) +                cmdList.push_back(strCmd); +        } +    } +    return !cmdList.empty(); +} + +bool BehaviourDictionary::getHasStrict(EBehaviour eBhvr) const +{ +    for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(eBhvr), mBhvr2InfoMap.upper_bound(eBhvr))) +    { +        // Only restrictions can be strict +        if (to_underlying(EParamType::AddRem) != itBhvr.second->getParamTypeMask()) +            continue; +        return itBhvr.second->hasStrict(); +    } +    RLV_ASSERT(false); +    return false; +} + +void BehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBhvrFlag, bool fEnable) +{ +    auto itBhvr = mString2InfoMap.find(std::make_pair(strBhvr, (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); +    if (mString2InfoMap.end() != itBhvr) +    { +        const_cast<BehaviourInfo*>(itBhvr->second)->toggleBehaviourFlag(eBhvrFlag, fEnable); +    } +} + +// ============================================================================ +// RlvCommmand +// + +RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCmd) +    : mObjId(idObj) +{ +    if (parseCommand(strCmd, mBehaviour, mOption, mParam)) +    { +         if ("n" == mParam || "add" == mParam) +             mParamType = EParamType::Add; +         else if ("y" == mParam || "rem" == mParam) +             mParamType = EParamType::Remove; +         else if ("clear" == mBehaviour)                                     // clear is the odd one out so just make it its own type +             mParamType = EParamType::Clear; +         else if ("force" == mParam) +             mParamType = EParamType::Force; +         else if (S32 nTemp; LLStringUtil::convertToS32(mParam, nTemp))  // Assume it's a reply command if we can convert <param> to an S32 +            mParamType = EParamType::Reply; +    } + +    mIsValid = mParamType != EParamType::Unknown; +    if (!mIsValid) +    { +        mOption.clear(); +        mParam.clear(); +        return; +    } + +    mBhvrInfo = BehaviourDictionary::instance().getBehaviourInfo(mBehaviour, mParamType, &mIsStrict); +} + +RlvCommand::RlvCommand(const RlvCommand& rlvCmd, EParamType eParamType) +    : mIsValid(rlvCmd.mIsValid), mObjId(rlvCmd.mObjId), mBehaviour(rlvCmd.mBehaviour), mBhvrInfo(rlvCmd.mBhvrInfo) +    , mParamType( (EParamType::Unknown == eParamType) ? rlvCmd.mParamType : eParamType) +    , mIsStrict(rlvCmd.mIsStrict), mOption(rlvCmd.mOption), mParam(rlvCmd.mParam), mIsRefCounted(rlvCmd.mIsRefCounted) +{ +} + +bool RlvCommand::parseCommand(const std::string& strCmd, std::string& strBhvr, std::string& strOption, std::string& strParam) +{ +    // Format: <behaviour>[:<option>]=<param> +    const size_t idxOption = strCmd.find(':'); +    const size_t idxParam  = strCmd.find('='); + +    // If <behaviour> is missing it's always an improperly formatted command +    // If there's an option, but it comes after <param> it's also invalid +    if ( (idxOption == 0 || idxParam == 0) || +         (idxOption != std::string::npos && idxOption >= idxParam) ) +    { +        return false; +    } + +    strBhvr = strCmd.substr(0, std::string::npos != idxOption ? idxOption : idxParam); +    strOption = strParam = ""; + +    // If <param> is missing it's an improperly formatted command +    if (idxParam == std::string::npos || idxParam + 1 == strCmd.length()) +    { +        // Unless "<behaviour> == "clear" AND (idxOption == 0)" +        // OR <behaviour> == "clear" AND (idxParam != 0) +        if (strBhvr == "clear" && (!idxOption || idxParam)) +            return true; +        return false; +    } + +    if (idxOption != std::string::npos && idxOption + 1 != idxParam) +        strOption = strCmd.substr(idxOption + 1, idxParam - idxOption - 1); +    strParam = strCmd.substr(idxParam + 1); + +    return true; +} + +std::string RlvCommand::asString() const +{ +    // NOTE: @clear=<param> should be represented as clear:<param> +    return mParamType != EParamType::Clear +        ? getBehaviour() + (!mOption.empty() ? ":" + mOption : "") +        : getBehaviour() + (!mParam.empty() ? ":" + mParam : ""); +} + +// ========================================================================= +// Various helper classes/timers/functors +// + +namespace Rlv +{ +    // =========================================================================== +    // CommandDbgOut +    // + +    void CommandDbgOut::add(std::string strCmd, ECmdRet eRet) +    { +        const std::string strSuffix = getReturnCodeString(eRet); +        if (!strSuffix.empty()) +            strCmd.append(llformat(" (%s)", strSuffix.c_str())); +        else if (mForConsole) +            return; // Only show console feedback on successful commands when there's an informational notice + +        std::string& strResult = mCommandResults[isReturnCodeSuccess(eRet) ? ECmdRet::Succeeded : (ECmdRet::Retained == eRet ? ECmdRet::Retained : ECmdRet::Failed)]; +        if (!strResult.empty()) +            strResult.append(", "); +        strResult.append(strCmd); +    } + +    std::string CommandDbgOut::get() const { +        std::ostringstream result; + +        if (1 == mCommandResults.size() && !mForConsole) +        { +            auto it = mCommandResults.begin(); +            result << " " << getDebugVerbFromReturnCode(it->first) << ": @" << it->second; +        } +        else if (!mCommandResults.empty()) +        { +            auto appendResult = [&](ECmdRet eRet, const std::string& name) +                { +                    auto it = mCommandResults.find(eRet); +                    if (it == mCommandResults.end()) return; +                    if (!mForConsole) result << "\n    - "; +                    result << LLTrans::getString(name) << ": @" << it->second; +                }; +            if (!mForConsole) +                result << ": @" << mOrigCmd; +            appendResult(ECmdRet::Succeeded, !mForConsole ? "RlvDebugExecuted" : "RlvConsoleExecuted"); +            appendResult(ECmdRet::Failed, !mForConsole ? "RlvDebugFailed" : "RlvConsoleFailed"); +            appendResult(ECmdRet::Retained, !mForConsole ? "RlvDebugRetained" : "RlvConsoleRetained"); +        } + +        return result.str(); +    } + +    std::string CommandDbgOut::getDebugVerbFromReturnCode(ECmdRet eRet) +    { +        switch (eRet) +        { +            case ECmdRet::Succeeded: +                return LLTrans::getString("RlvDebugExecuted"); +            case ECmdRet::Failed: +                return LLTrans::getString("RlvDebugFailed"); +            case ECmdRet::Retained: +                return LLTrans::getString("RlvDebugRetained"); +            default: +                RLV_ASSERT(false); +                return LLStringUtil::null; +        } +    } + +    std::string CommandDbgOut::getReturnCodeString(ECmdRet eRet) +    { +        switch (eRet) +        { +            case ECmdRet::SuccessUnset: +                return LLTrans::getString("RlvReturnCodeUnset"); +            case ECmdRet::SuccessDuplicate: +                return LLTrans::getString("RlvReturnCodeDuplicate"); +            case ECmdRet::SuccessDelayed: +                return LLTrans::getString("RlvReturnCodeDelayed"); +            case ECmdRet::SuccessDeprecated: +                return LLTrans::getString("RlvReturnCodeDeprecated"); +            case ECmdRet::FailedSyntax: +                return LLTrans::getString("RlvReturnCodeSyntax"); +            case ECmdRet::FailedOption: +                return LLTrans::getString("RlvReturnCodeOption"); +            case ECmdRet::FailedParam: +                return LLTrans::getString("RlvReturnCodeParam"); +            case ECmdRet::FailedLock: +                return LLTrans::getString("RlvReturnCodeLock"); +            case ECmdRet::FailedDisabled: +                return LLTrans::getString("RlvReturnCodeDisabled"); +            case ECmdRet::FailedUnknown: +                return LLTrans::getString("RlvReturnCodeUnknown"); +            case ECmdRet::FailedNoSharedRoot: +                return LLTrans::getString("RlvReturnCodeNoSharedRoot"); +            case ECmdRet::FailedDeprecated: +                return LLTrans::getString("RlvReturnCodeDeprecatedAndDisabled"); +            case ECmdRet::FailedNoBehaviour: +                return LLTrans::getString("RlvReturnCodeNoBehaviour"); +            case ECmdRet::FailedUnheldBehaviour: +                return LLTrans::getString("RlvReturnCodeUnheldBehaviour"); +            case ECmdRet::FailedBlocked: +                return LLTrans::getString("RlvReturnCodeBlocked"); +            case ECmdRet::FailedThrottled: +                return LLTrans::getString("RlvReturnCodeThrottled"); +            case ECmdRet::FailedNoProcessor: +                return LLTrans::getString("RlvReturnCodeNoProcessor"); +            // The following are identified by the chat verb +            case ECmdRet::Retained: +            case ECmdRet::Succeeded: +            case ECmdRet::Failed: +                return LLStringUtil::null; +            // The following shouldn't occur +            case ECmdRet::Unknown: +            default: +                RLV_ASSERT(false); +                return LLStringUtil::null; +        } +    } + +    // =========================================================================== +} + +// ============================================================================ diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h new file mode 100644 index 0000000000..f241332594 --- /dev/null +++ b/indra/newview/rlvhelper.h @@ -0,0 +1,299 @@ +/** + * @file rlvhelper.h + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "rlvdefines.h" + +// ============================================================================ +// Forward declarations +// + +class RlvCommand; + +// ============================================================================ + +namespace Rlv +{ +    // ============================================================================ +    // BehaviourInfo class - Generic behaviour descriptor (used by restrictions, reply and force commands) +    // + +    class BehaviourInfo +    { +    public: +        enum EBehaviourFlags : uint32_t +        { +            // General behaviour flags +            Strict = 0x0001,				// Behaviour has a "_sec" version +            Synonym = 0x0002,				// Behaviour is a synonym of another +            Extended = 0x0004,				// Behaviour is part of the RLVa extended command set +            Experimental = 0x0008,			// Behaviour is part of the RLVa experimental command set +            Blocked = 0x0010,				// Behaviour is blocked +            Deprecated = 0x0020,			// Behaviour is deprecated +            MaskGeneral = 0x0FFF, + +            // Force-wear specific flags +            ForceWear_WearReplace = 0x0001 << 16, +            ForceWear_WearAdd = 0x0002 << 16, +            ForceWear_WearRemove = 0x0004 << 16, +            ForceWear_Node = 0x0010 << 16, +            ForceWear_Subtree = 0x0020 << 16, +            ForceWear_ContextNone = 0x0100 << 16, +            ForceWear_ContextObject = 0x0200 << 16, +            MaskForceWear = 0xFFFFu << 16 +        }; + +        BehaviourInfo(const std::string& strBhvr, EBehaviour eBhvr, EParamType maskParamType, std::underlying_type_t<EBehaviourFlags> nBhvrFlags = 0) +            : mBhvr(strBhvr), mBhvrType(eBhvr), mBhvrFlags(nBhvrFlags), mMaskParamType(to_underlying(maskParamType)) {} +        virtual ~BehaviourInfo() {} + +        const std::string&    getBehaviour() const { return mBhvr; } +        EBehaviour            getBehaviourType() const { return mBhvrType; } +        std::underlying_type_t<EBehaviourFlags> getBehaviourFlags() const { return mBhvrFlags; } +        std::underlying_type_t<EParamType> getParamTypeMask() const { return mMaskParamType; } +        bool                  hasStrict() const { return mBhvrFlags & Strict; } +        bool                  isBlocked() const { return mBhvrFlags & Blocked; } +        bool                  isExperimental() const { return mBhvrFlags & Experimental; } +        bool                  isExtended() const { return mBhvrFlags & Extended; } +        bool                  isSynonym() const { return mBhvrFlags & Synonym; } +        void                  toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable); + +        virtual ECmdRet  processCommand(const RlvCommand& rlvCmd) const { return ECmdRet::FailedNoProcessor; } + +    protected: +        std::string mBhvr; +        EBehaviour mBhvrType; +        std::underlying_type_t<EBehaviourFlags> mBhvrFlags; +        std::underlying_type_t<EParamType> mMaskParamType; +    }; + +    inline void BehaviourInfo::toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable) +    { +        if (fEnable) +            mBhvrFlags |= eBhvrFlag; +        else +            mBhvrFlags &= ~eBhvrFlag; +    } + +    // ============================================================================ +    // BehaviourDictionary and related classes +    // + +    class BehaviourDictionary : public LLSingleton<BehaviourDictionary> +    { +        LLSINGLETON(BehaviourDictionary); +    protected: +        ~BehaviourDictionary() override; +    public: +        void addEntry(const BehaviourInfo* entry_p); + +        /* +         * General helper functions +         */ +    public: +        EBehaviour           getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; +        const BehaviourInfo* getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const; +        const BehaviourInfo* getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; +        bool                 getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const; +        bool                 getHasStrict(EBehaviour eBhvr) const; +        void                 toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBvhrFlag, bool fEnable); + +        /* +         * Member variables +         */ +    protected: +        std::list<const BehaviourInfo*> mBhvrInfoList; +        std::map<std::pair<std::string, EParamType>, const BehaviourInfo*> mString2InfoMap; +        std::multimap<EBehaviour, const BehaviourInfo*> mBhvr2InfoMap; +    }; + +    // ============================================================================ +    // CommandHandler and related classes +    // + +    typedef ECmdRet(BhvrHandlerFunc)(const RlvCommand&, bool&); +    typedef void(BhvrToggleHandlerFunc)(EBehaviour, bool); +    typedef ECmdRet(ForceHandlerFunc)(const RlvCommand&); +    typedef ECmdRet(ReplyHandlerFunc)(const RlvCommand&, std::string&); + +    // +    // CommandHandlerBaseImpl - Base implementation for each command type (the old process(AddRem|Force|Reply)Command functions) +    // +    template<EParamType paramType> struct CommandHandlerBaseImpl; +    template<> struct CommandHandlerBaseImpl<EParamType::AddRem> { static ECmdRet processCommand(const RlvCommand&, BhvrHandlerFunc*, BhvrToggleHandlerFunc* = nullptr); }; +    template<> struct CommandHandlerBaseImpl<EParamType::Force>  { static ECmdRet processCommand(const RlvCommand&, ForceHandlerFunc*); }; +    template<> struct CommandHandlerBaseImpl<EParamType::Reply>  { static ECmdRet processCommand(const RlvCommand&, ReplyHandlerFunc*); }; + +    // +    // CommandHandler - The actual command handler (Note that a handler is more general than a processor; a handler can - for instance - be used by multiple processors) +    // +    #if LL_WINDOWS +        #define RLV_TEMPL_FIX(x) template<x> +    #else +        #define RLV_TEMPL_FIX(x) template<typename Placeholder = int> +    #endif // LL_WINDOWS + + +    template <EParamType templParamType, EBehaviour templBhvr> +    struct CommandHandler +    { +        RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::AddRem>::type) static ECmdRet onCommand(const RlvCommand&, bool&); +        RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::AddRem>::type) static void onCommandToggle(EBehaviour, bool); +        RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::Force>::type)  static ECmdRet onCommand(const RlvCommand&); +        RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::Reply>::type)  static ECmdRet onCommand(const RlvCommand&, std::string&); +    }; + +    // Aliases to improve readability in definitions +    template<EBehaviour templBhvr> using BehaviourHandler = CommandHandler<EParamType::AddRem, templBhvr>; +    template<EBehaviour templBhvr> using BehaviourToggleHandler = BehaviourHandler<templBhvr>; +    template<EBehaviour templBhvr> using ForceHandler = CommandHandler<EParamType::Force, templBhvr>; +    template<EBehaviour templBhvr> using ReplyHandler = CommandHandler<EParamType::Reply, templBhvr>; + +    // List of shared handlers +    using VersionReplyHandler = ReplyHandler<EBehaviour::Version>;				// Shared between @version and @versionnew + +    // +    // CommandProcessor - Templated glue class that brings BehaviourInfo, CommandHandlerBaseImpl and CommandHandler together +    // +    template <EParamType templParamType, EBehaviour templBhvr, typename handlerImpl = CommandHandler<templParamType, templBhvr>, typename baseImpl = CommandHandlerBaseImpl<templParamType>> +    class CommandProcessor : public BehaviourInfo +    { +    public: +        // Default constructor used by behaviour specializations +        RLV_TEMPL_FIX(typename = typename std::enable_if<templBhvr != EBehaviour::Unknown>::type) +        CommandProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, templBhvr, templParamType, nBhvrFlags) {} + +        // Constructor used when we don't want to specialize on behaviour (see BehaviourGenericProcessor) +        RLV_TEMPL_FIX(typename = typename std::enable_if<templBhvr == EBehaviour::Unknown>::type) +        CommandProcessor(const std::string& strBhvr, EBehaviour eBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, templParamType, nBhvrFlags) {} + +        ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return baseImpl::processCommand(rlvCmd, &handlerImpl::onCommand); } +    }; + +    // Aliases to improve readability in definitions +    template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::AddRem, templBhvr>> using BehaviourProcessor = CommandProcessor<EParamType::AddRem, templBhvr, handlerImpl>; +    template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Force, templBhvr>> using ForceProcessor = CommandProcessor<EParamType::Force, templBhvr, handlerImpl>; +    template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Reply, templBhvr>> using ReplyProcessor = CommandProcessor<EParamType::Reply, templBhvr, handlerImpl>; + +    // Provides pre-defined generic implementations of basic behaviours (template voodoo - see original commit for something that still made sense) +    template<EBehaviourOptionType templOptionType> struct BehaviourGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd, bool& fRefCount); }; +    template<EBehaviourOptionType templOptionType> using BehaviourGenericProcessor = BehaviourProcessor<EBehaviour::Unknown, BehaviourGenericHandler<templOptionType>>; +    template<EBehaviourOptionType templOptionType> struct ForceGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd); }; +    template<EBehaviourOptionType templOptionType> using ForceGenericProcessor = ForceProcessor<EBehaviour::Unknown, ForceGenericHandler<templOptionType>>; + +    // ============================================================================ +    // BehaviourProcessor and related classes - Handles add/rem comamnds aka "restrictions) +    // + +    template <EBehaviour eBhvr, typename handlerImpl = BehaviourHandler<eBhvr>, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> +    class BehaviourToggleProcessor : public BehaviourInfo +    { +    public: +        BehaviourToggleProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, EParamType::AddRem, nBhvrFlags) {} +        ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return CommandHandlerBaseImpl<EParamType::AddRem>::processCommand(rlvCmd, &handlerImpl::onCommand, &toggleHandlerImpl::onCommandToggle); } +    }; +    template <EBehaviour eBhvr, EBehaviourOptionType optionType, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> using RlvBehaviourGenericToggleProcessor = BehaviourToggleProcessor<eBhvr, BehaviourGenericHandler<optionType>, toggleHandlerImpl>; + +    // ============================================================================ +    // Various helper classes/timers/functors +    // + +    struct CommandDbgOut +    { +        CommandDbgOut(const std::string& orig_cmd, bool for_console) : mOrigCmd(orig_cmd), mForConsole(for_console) {} +        void add(std::string strCmd, ECmdRet eRet); +        std::string get() const; +        static std::string getDebugVerbFromReturnCode(ECmdRet eRet); +        static std::string getReturnCodeString(ECmdRet eRet); +    private: +        std::string mOrigCmd; +        std::map<ECmdRet, std::string> mCommandResults; +        bool mForConsole = false; +    }; +} + +// ============================================================================ +// RlvCommand +// + +class RlvCommand +{ +public: +    explicit RlvCommand(const LLUUID& idObj, const std::string& strCmd); +    RlvCommand(const RlvCommand& rlvCmd, Rlv::EParamType eParamType = Rlv::EParamType::Unknown); + +    /* +     * Member functions +     */ +public: +    std::string        asString() const; +    const std::string& getBehaviour() const { return mBehaviour; } +    const Rlv::BehaviourInfo* getBehaviourInfo() const { return mBhvrInfo; } +    Rlv::EBehaviour    getBehaviourType() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourType() : Rlv::EBehaviour::Unknown; } +    U32                getBehaviourFlags() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourFlags() : 0; } +    const LLUUID&      getObjectID() const { return mObjId; } +    const std::string& getOption() const { return mOption; } +    const std::string& getParam() const { return mParam; } +    Rlv::EParamType    getParamType() const { return mParamType; } +    bool               hasOption() const { return !mOption.empty(); } +    bool               isBlocked() const { return (mBhvrInfo) ? mBhvrInfo->isBlocked() : false; } +    bool               isRefCounted() const { return mIsRefCounted; } +    bool               isStrict() const { return mIsStrict; } +    bool               isValid() const { return mIsValid; } +    Rlv::ECmdRet       processCommand() const { return (mBhvrInfo) ? mBhvrInfo->processCommand(*this) : Rlv::ECmdRet::FailedNoProcessor; } + +protected: +    static bool parseCommand(const std::string& strCommand, std::string& strBehaviour, std::string& strOption, std::string& strParam); +    bool               markRefCounted() const { return mIsRefCounted = true; } + +    /* +     * Operators +     */ +public: +    bool operator ==(const RlvCommand&) const; + +    /* +     * Member variables +     */ +protected: +    bool                    mIsValid = false; +    LLUUID                  mObjId; +    std::string             mBehaviour; +    const Rlv::BehaviourInfo* mBhvrInfo = nullptr; +    Rlv::EParamType         mParamType = Rlv::EParamType::Unknown; +    bool                    mIsStrict = false; +    std::string             mOption; +    std::string             mParam; +    mutable bool            mIsRefCounted = false; + +    friend class RlvHandler; +    friend class RlvObject; +    template<Rlv::EParamType> friend struct Rlv::CommandHandlerBaseImpl; +}; + +// ============================================================================ diff --git a/indra/newview/skins/default/xui/en/floater_rlv_console.xml b/indra/newview/skins/default/xui/en/floater_rlv_console.xml new file mode 100644 index 0000000000..708055d1b6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_rlv_console.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_resize="true" + height="400" + layout="topleft" + min_height="300" + min_width="300" + name="rlv_console" + title="RLVa console" + width="600" + > +    <layout_stack +     animate="false" +     bottom="-1" +     default_tab_group="2" +     follows="all" +     left="5" +     layout="topleft" +     mouse_opaque="false" +     name="main_stack" +     right="-5" +     orientation="vertical" +     tab_group="1" +     top="1" +     > +        <layout_panel +         name="body_panel" +         height="235"> +            <text_editor +             follows="all" +             left="1" +			 right="-1" +			 top="0" +             length="1" +             font="Monospace" +             bottom="-1" +             ignore_tab="false" +             layout="topleft" +             max_length="65536" +             name="console_output" +             read_only="true" +             track_end="true" +             type="string" +             word_wrap="true" +            > +            </text_editor> +        </layout_panel> + +        <layout_panel +         height="26" +         auto_resize="false" +         name="input_panel"> +            <chat_editor +             layout="topleft" +             expand_lines_count="5" +             follows="left|right|bottom" +             font="SansSerifSmall" +             height="20" +             is_expandable="true" +             text_tentative_color="TextFgTentativeColor" +             name="console_input" +             max_length="1023" +             spellcheck="true" +             tab_group="3" +             bottom_delta="20" +             left="1" +			 top="1" +			 right="-1" +             show_emoji_helper="false" +             wrap="true" +            /> +        </layout_panel> +    </layout_stack> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 3de2473927..f52e1cc952 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1836,6 +1836,71 @@ function="World.EnvPreset"      </menu>      <menu       create_jump_keys="true" +     label="RLVa" +     name="RLVa Main" +     tear_off="true" +     visible="true"> +      <menu +       label="Debug" +       name="Debug" +       tear_off="true"> +        <menu_item_check +         label="Show Top-level RLVa Menu" +         name="Show Top-level RLVa Menu"> +          <menu_item_check.on_check +           function="CheckControl" +           parameter="RLVaTopLevelMenu" /> +          <menu_item_check.on_click +           function="ToggleControl" +           parameter="RLVaTopLevelMenu" /> +        </menu_item_check> +      	<menu_item_separator/> +      	<menu_item_check +      	 label="Show Debug Messages" +      	 name="Show Debug Messages"> +          <menu_item_check.on_check +           function="CheckControl" +           parameter="RestrainedLoveDebug" /> +          <menu_item_check.on_click +           function="ToggleControl" +           parameter="RestrainedLoveDebug" /> +      	</menu_item_check> +      	<menu_item_check +      	 label="Hide Unset or Duplicate Messages" +      	 name="Hide Unset or Duplicate Messages"> +          <menu_item_check.on_check +           function="CheckControl" +           parameter="RLVaDebugHideUnsetDuplicate" /> +          <menu_item_check.on_click +           function="ToggleControl" +           parameter="RLVaDebugHideUnsetDuplicate" /> +      	</menu_item_check> +      </menu> +      <menu_item_separator/> +      <menu_item_check +       label="Allow Temporary Attachments" +       name="Allow Temporary Attachments"> +      	<menu_item_check.on_check +      	 function="CheckControl" +      	 parameter="RLVaEnableTemporaryAttachments" /> +      	<menu_item_check.on_click +      	 function="ToggleControl" +      	 parameter="RLVaEnableTemporaryAttachments" /> +      </menu_item_check> +      <menu_item_separator /> +      <menu_item_check +       label="Console..." +       name="Console"> +        <menu_item_check.on_check +      	 function="Floater.Visible" +      	 parameter="rlv_console" /> +        <menu_item_check.on_click +      	 function="Floater.Toggle" +      	 parameter="rlv_console" /> +      </menu_item_check> +    </menu> +    <menu +     create_jump_keys="true"       label="Advanced"       name="Advanced"       tear_off="true" @@ -2342,6 +2407,11 @@ function="World.EnvPreset"                   parameter="flexible" />              </menu_item_check>          </menu>         +        <menu +         label="RLVa" +         name="RLVa Embedded" +         tear_off="true" +         visible="true" />          <menu_item_check           label="Use Plugin Read Thread"           name="Use Plugin Read Thread"> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 14123de82c..c5a59fac2a 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -59,6 +59,7 @@ Disk cache: [DISK_CACHE_INFO]  HiDPI display mode: [HIDPI]  	</string>  	<string name="AboutLibs"> +RestrainedLove API: [RLV_VERSION]  J2C Decoder Version: [J2C_VERSION]  Audio Driver Version: [AUDIO_DRIVER_VERSION]  [LIBCEF_VERSION] @@ -4401,5 +4402,31 @@ and report the problem.    <string name="NotAvailableOnPlatform">Not available on this platform</string>    <string name="NowPlaying">Now Playing</string>    <string name="GridInfoTitle">GRID INFO</string> +  <!-- RLVa --> +  <string name="RlvConsoleDisable">RLVa is disabled</string> +  <string name="RlvConsoleInvalidCmd">Invalid command</string> +  <string name="RlvConsoleExecuted">INFO</string> +  <string name="RlvConsoleFailed">ERR</string> +  <string name="RlvConsoleRetained">RET</string> +  <string name="RlvDebugExecuted">executed</string> +  <string name="RlvDebugFailed">failed</string> +  <string name="RlvDebugRetained">retained</string> +  <string name="RlvReturnCodeUnset">unset</string> +  <string name="RlvReturnCodeDuplicate">duplicate</string> +  <string name="RlvReturnCodeDelayed">delayed</string> +  <string name="RlvReturnCodeDeprecated">deprecated</string> +  <string name="RlvReturnCodeSyntax">thingy error</string> +  <string name="RlvReturnCodeOption">invalid option</string> +  <string name="RlvReturnCodeParam">invalid param</string> +  <string name="RlvReturnCodeLock">locked command</string> +  <string name="RlvReturnCodeDisabled">disabled command</string> +  <string name="RlvReturnCodeUnknown">unknown command</string> +  <string name="RlvReturnCodeNoSharedRoot">missing #RLV</string> +  <string name="RlvReturnCodeDeprecatedAndDisabled">deprecated and disabled</string> +  <string name="RlvReturnCodeNoBehaviour">no active behaviours</string> +  <string name="RlvReturnCodeUnheldBehaviour">base behaviour not held</string> +  <string name="RlvReturnCodeBlocked">blocked object</string> +  <string name="RlvReturnCodeThrottled">throttled</string> +  <string name="RlvReturnCodeNoProcessor">no command processor found</string>  </strings> | 
