diff options
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> |