mirror of
https://github.com/overte-org/overte.git
synced 2025-04-13 18:42:11 +02:00
Merge branch 'master' into pr/441
This commit is contained in:
commit
b8181d92a5
220 changed files with 19227 additions and 1058 deletions
20
BUILD.md
20
BUILD.md
|
@ -1,13 +1,13 @@
|
|||
# General Build Information
|
||||
|
||||
*Last Updated on May 17, 2020*
|
||||
*Last Updated on August 26, 2020*
|
||||
|
||||
### OS Specific Build Guides
|
||||
|
||||
* [Build Windows](BUILD_WIN.md) - complete instructions for Windows.
|
||||
* [Build Linux](BUILD_LINUX.md) - additional instructions for Linux.
|
||||
* [Build OSX](BUILD_OSX.md) - additional instructions for OS X.
|
||||
* [Build Android](BUILD_ANDROID.md) - additional instructions for Android
|
||||
* [Build Android](BUILD_ANDROID.md) - additional instructions for Android.
|
||||
|
||||
### Dependencies
|
||||
- [git](https://git-scm.com/downloads): >= 1.6
|
||||
|
@ -21,8 +21,8 @@
|
|||
These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required.
|
||||
- [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83
|
||||
- [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8
|
||||
- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac)
|
||||
- [OpenVR](https://github.com/ValveSoftware/openvr): 1.11.11 (Win32 only)
|
||||
- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Windows) / 0.5 (Mac)
|
||||
- [OpenVR](https://github.com/ValveSoftware/openvr): 1.11.11 (Windows, Linux)
|
||||
- [Polyvox](http://www.volumesoffun.com/): 0.2.1
|
||||
- [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3
|
||||
- [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3
|
||||
|
@ -78,14 +78,14 @@ Where /path/to/directory is the path to a directory where you wish the build fil
|
|||
BUILD_NUMBER
|
||||
|
||||
// The type of release.
|
||||
RELEASE_TYPE=PRODUCTION|PR
|
||||
RELEASE_BUILD=PRODUCTION|PR
|
||||
RELEASE_TYPE=PRODUCTION|PR|DEV
|
||||
|
||||
// TODO: What do these do?
|
||||
// Determine the build type
|
||||
PRODUCTION_BUILD=0|1
|
||||
PR_BUILD=0|1
|
||||
STABLE_BUILD=0|1
|
||||
|
||||
// TODO: What do these do?
|
||||
// Determine if to utilize testing or stable Metaverse URLs
|
||||
USE_STABLE_GLOBAL_SERVICES=1
|
||||
BUILD_GLOBAL_SERVICES=STABLE
|
||||
|
||||
|
@ -141,6 +141,8 @@ The following build options can be used when running CMake
|
|||
* BUILD_SERVER
|
||||
* BUILD_TESTS
|
||||
* BUILD_TOOLS
|
||||
* CLIENT_ONLY // Will package only the Interface
|
||||
* SERVER_ONLY // Will package only the Server
|
||||
|
||||
#### Developer Build Options
|
||||
|
||||
|
@ -150,4 +152,4 @@ The following build options can be used when running CMake
|
|||
#### Devices
|
||||
|
||||
You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Build Linux
|
||||
|
||||
*Last Updated on January 20, 2020*
|
||||
*Last Updated on April 11, 2020*
|
||||
|
||||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
|
||||
|
||||
|
|
23
BUILD_OSX.md
23
BUILD_OSX.md
|
@ -1,6 +1,6 @@
|
|||
# Build OSX
|
||||
|
||||
*Last Updated on April 30, 2019*
|
||||
*Last Updated on July 13, 2020*
|
||||
|
||||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only macOS specific instructions are found in this document.
|
||||
|
||||
|
@ -8,24 +8,29 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
|||
|
||||
[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some Vircadia dependencies very simple.
|
||||
|
||||
brew install cmake openssl
|
||||
brew install cmake openssl npm
|
||||
|
||||
### Python 3
|
||||
|
||||
Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/).
|
||||
Execute the `Update Shell Profile.command` script that is provided with the installer.
|
||||
|
||||
### OSX SDK
|
||||
|
||||
You will need the OSX SDK for building. The easiest way to get this is to install Xcode from the App Store.
|
||||
|
||||
### OpenSSL
|
||||
|
||||
Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
|
||||
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
|
||||
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via
|
||||
`export OPENSSL_ROOT_DIR=/usr/local/opt/openssl`
|
||||
or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake`
|
||||
|
||||
export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2l
|
||||
|
||||
Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.
|
||||
### Xcode
|
||||
|
||||
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
|
||||
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. You will need to select the Xcode installation in the terminal first if you have not done so already.
|
||||
|
||||
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
||||
|
||||
cmake .. -G Xcode
|
||||
|
||||
|
@ -34,3 +39,7 @@ If `cmake` complains about Python 3 being missing, you may need to update your C
|
|||
After running cmake, you will have the make files or Xcode project file necessary to build all of the components. Open the hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target drop down), and click Run.
|
||||
|
||||
If the build completes successfully, you will have built targets for all components located in the `build/${target_name}/Debug` directories.
|
||||
|
||||
### make
|
||||
|
||||
If you build with make rather than Xcode, you can append `-j4`for assigning more threads. The number indicates the number of threads, e.g. 4.
|
||||
|
|
|
@ -120,7 +120,7 @@ else()
|
|||
endif()
|
||||
|
||||
# Use default time server if none defined in environment
|
||||
set_from_env(TIMESERVER_URL TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp")
|
||||
set_from_env(TIMESERVER_URL TIMESERVER_URL "http://timestamp.comodoca.com?td=sha256")
|
||||
|
||||
set(HIFI_USE_OPTIMIZED_IK_OPTION OFF)
|
||||
set(BUILD_CLIENT_OPTION ON)
|
||||
|
|
|
@ -9,7 +9,7 @@ This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/P
|
|||
|
||||
### Packaging
|
||||
|
||||
To produce an installer, run the `package` target.
|
||||
To produce an installer, run the `package` target. However you will want to follow the steps specific to your platform below.
|
||||
|
||||
#### Windows
|
||||
|
||||
|
@ -62,7 +62,6 @@ To produce an executable installer on Windows, the following are required:
|
|||
1. Perform a clean cmake from a new terminal.
|
||||
1. Open the `vircadia.sln` solution with elevated (administrator) permissions on Visual Studio and select the **Release** configuration.
|
||||
1. Build the solution.
|
||||
1. Build CMakeTargets->INSTALL
|
||||
1. Build `packaged-server-console-npm-install` (found under **hidden/Server Console**)
|
||||
1. Build `packaged-server-console` (found under **Server Console**)
|
||||
This will add 2 folders to `build\server-console\` -
|
||||
|
|
|
@ -615,6 +615,10 @@ void Agent::setIsAvatar(bool isAvatar) {
|
|||
delete _avatarQueryTimer;
|
||||
_avatarQueryTimer = nullptr;
|
||||
|
||||
// Clear the skeleton model so that if agent is set to an avatar again the skeleton model is (re)loaded.
|
||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
scriptedAvatar->setSkeletonModelURL(QUrl());
|
||||
|
||||
// The avatar mixer never times out a connection (e.g., based on identity or data packets)
|
||||
// but rather keeps avatars in its list as long as "connected". As a result, clients timeout
|
||||
// when we stop sending identity, but then get woken up again by the mixer itself, which sends
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <AvatarLogging.h>
|
||||
#include <EntityItem.h>
|
||||
#include <EntityItemProperties.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
|
||||
ScriptableAvatar::ScriptableAvatar() {
|
||||
|
@ -221,7 +222,7 @@ void ScriptableAvatar::updateJointMappings() {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
DependencyManager::get<ResourceRequestObserver>()->update(
|
||||
_skeletonModelURL, -1, "AvatarData::updateJointMappings");
|
||||
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
|
||||
|
|
|
@ -370,16 +370,18 @@ void EntityServer::entityFilterAdded(EntityItemID id, bool success) {
|
|||
|
||||
void EntityServer::nodeAdded(SharedNodePointer node) {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
tree->knowAvatarID(node->getUUID());
|
||||
if (tree) {
|
||||
tree->knowAvatarID(node->getUUID());
|
||||
}
|
||||
OctreeServer::nodeAdded(node);
|
||||
}
|
||||
|
||||
void EntityServer::nodeKilled(SharedNodePointer node) {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
tree->withWriteLock([&] {
|
||||
if (tree) {
|
||||
tree->deleteDescendantsOfAvatar(node->getUUID());
|
||||
});
|
||||
tree->forgetAvatarID(node->getUUID());
|
||||
tree->forgetAvatarID(node->getUUID());
|
||||
}
|
||||
OctreeServer::nodeKilled(node);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Source: hifi-client-deps
|
||||
Version: 0.1
|
||||
Description: Collected dependencies for High Fidelity applications
|
||||
Build-Depends: hifi-deps, aristo (windows), glslang, liblo (windows), nlohmann-json, openvr (windows), quazip (!android), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), sranipal (windows), vulkanmemoryallocator
|
||||
Build-Depends: hifi-deps, aristo (windows), glslang, liblo (windows), nlohmann-json, openvr (linux|windows), quazip (!android), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), sranipal (windows), vulkanmemoryallocator
|
||||
|
|
|
@ -11,15 +11,23 @@ vcpkg_from_github(
|
|||
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||
|
||||
if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64")
|
||||
set(ARCH_PATH "win64")
|
||||
if(WIN32)
|
||||
set(ARCH_PATH "win64")
|
||||
else()
|
||||
set(ARCH_PATH "linux64")
|
||||
endif()
|
||||
elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86")
|
||||
set(ARCH_PATH "win32")
|
||||
if(WIN32)
|
||||
set(ARCH_PATH "win32")
|
||||
else()
|
||||
set(ARCH_PATH "linux32")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Package only supports x64 and x86 windows.")
|
||||
message(FATAL_ERROR "Package only supports x64 and x86 Windows and Linux.")
|
||||
endif()
|
||||
|
||||
if(VCPKG_CMAKE_SYSTEM_NAME)
|
||||
message(FATAL_ERROR "Package only supports windows desktop.")
|
||||
if(VCPKG_CMAKE_SYSTEM_NAME AND NOT (VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||
message(FATAL_ERROR "Package only supports Windows or Linux desktop.")
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY
|
||||
|
@ -28,18 +36,35 @@ file(MAKE_DIRECTORY
|
|||
${CURRENT_PACKAGES_DIR}/debug/lib
|
||||
${CURRENT_PACKAGES_DIR}/debug/bin
|
||||
)
|
||||
file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/lib)
|
||||
file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib)
|
||||
file(COPY
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb
|
||||
DESTINATION ${CURRENT_PACKAGES_DIR}/bin
|
||||
)
|
||||
file(COPY
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb
|
||||
DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/lib)
|
||||
file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib)
|
||||
file(COPY
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb
|
||||
DESTINATION ${CURRENT_PACKAGES_DIR}/bin
|
||||
)
|
||||
file(COPY
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb
|
||||
DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin
|
||||
)
|
||||
else()
|
||||
file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/libopenvr_api.so DESTINATION ${CURRENT_PACKAGES_DIR}/lib)
|
||||
file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/libopenvr_api.so DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib)
|
||||
file(COPY
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so.dbg
|
||||
DESTINATION ${CURRENT_PACKAGES_DIR}/bin
|
||||
)
|
||||
file(COPY
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so
|
||||
${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so.dbg
|
||||
DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin
|
||||
)
|
||||
endif()
|
||||
|
||||
file(COPY ${SOURCE_PATH}/headers DESTINATION ${CURRENT_PACKAGES_DIR})
|
||||
file(RENAME ${CURRENT_PACKAGES_DIR}/headers ${CURRENT_PACKAGES_DIR}/include)
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSE
|
|||
if (WIN32)
|
||||
vcpkg_download_distfile(
|
||||
SRANIPAL_SOURCE_ARCHIVE
|
||||
URLS "${EXTERNAL_BUILD_ASSETS}/seth/sranipal-1.1.0.1-windows.zip"
|
||||
SHA512 b09ce012abe4e3c71e8e69626bdd7823ff6576601a821ab365275f2764406a3e5f7b65fcf2eb1d0962eff31eb5958a148b00901f67c229dc6ace56eb5e6c9e1b
|
||||
FILENAME sranipal-1.1.0.1-windows.zip
|
||||
URLS "${EXTERNAL_BUILD_ASSETS}/seth/sranipal-1.1.0.1-2-windows.zip"
|
||||
SHA512 f1f68f6beef52ae5e034bc3f44932ae0800ee187b75d80e76ae7b17b8ddd7bc54c039ce5594d231035e3caf3a61fed36f38621a860b4fb20170cb0176d9c28f0
|
||||
FILENAME sranipal-1.1.0.1-2-windows.zip
|
||||
)
|
||||
|
||||
vcpkg_extract_source_archive(${SRANIPAL_SOURCE_ARCHIVE})
|
||||
|
|
|
@ -199,18 +199,15 @@
|
|||
|
||||
!system "$%TEMP%\tempinstaller.exe" = 2
|
||||
|
||||
; NOTE: We're not code signing right now, so we're going to disable that.
|
||||
; TODO: Get a code signing certificate so we can re-enable code signing.
|
||||
|
||||
; The Inner invocation has written an uninstaller binary for us.
|
||||
; We need to sign it if it's a production or PR build.
|
||||
; !if @PRODUCTION_BUILD@ == 1
|
||||
; !if @BYPASS_SIGNING@ == 1
|
||||
; !warning "BYPASS_SIGNING set - installer will not be signed"
|
||||
; !else
|
||||
; !system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
; !endif
|
||||
; !endif
|
||||
!if @PRODUCTION_BUILD@ == 1
|
||||
!if @BYPASS_SIGNING@ == 1
|
||||
!warning "BYPASS_SIGNING set - installer will not be signed"
|
||||
!else
|
||||
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://timestamp.comodoca.com?td=sha256 /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
!endif
|
||||
!endif
|
||||
|
||||
; Good. Now we can carry on writing the real installer.
|
||||
|
||||
|
@ -871,7 +868,7 @@ Function PostInstallOptionsPage
|
|||
Pop $LaunchConsoleNowCheckbox
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetInstallOption $LaunchConsoleNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $LaunchConsoleNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_UNCHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchConsoleNowCheckbox ${BST_UNCHECKED}
|
||||
|
@ -887,7 +884,7 @@ Function PostInstallOptionsPage
|
|||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetInstallOption $ConsoleStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $ConsoleStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_UNCHECKED}
|
||||
|
||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)"
|
||||
Pop $CleanInstallCheckbox
|
||||
|
@ -940,8 +937,8 @@ Function ReadInstallTypes
|
|||
StrCpy $Express "1"
|
||||
|
||||
StrCpy $DesktopClientState ${BST_CHECKED}
|
||||
StrCpy $ConsoleStartupState ${BST_CHECKED}
|
||||
StrCpy $LaunchConsoleNowState ${BST_CHECKED}
|
||||
StrCpy $ConsoleStartupState ${BST_UNCHECKED}
|
||||
StrCpy $LaunchConsoleNowState ${BST_UNCHECKED}
|
||||
StrCpy $LaunchClientNowState ${BST_CHECKED}
|
||||
StrCpy $CleanInstallState ${BST_UNCHECKED}
|
||||
StrCpy $DesktopConsoleState ${BST_UNCHECKED}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
"label": "Metaverse / Networking",
|
||||
"label": "Networking / Metaverse",
|
||||
"settings": [
|
||||
{
|
||||
"name": "access_token",
|
||||
|
@ -54,6 +54,55 @@
|
|||
"default": true,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "enable_metadata_exporter",
|
||||
"label": "Enable Metadata HTTP Availability",
|
||||
"help": "Allows your domain's metadata to be accessible on the public internet via direct HTTP connection to the domain server.",
|
||||
"default": true,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "metadata_exporter_port",
|
||||
"label": "Metadata Exporter HTTP Port",
|
||||
"help": "This is the port where the Metaverse exporter accepts connections. It listens both on IPv4 and IPv6 and can be accessed remotely, so you should make sure to restrict access with a firewall as needed.",
|
||||
"default": "9704",
|
||||
"type": "int",
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "authentication",
|
||||
"label": "Networking / WordPress OAuth2",
|
||||
"settings": [
|
||||
{
|
||||
"name": "enable_oauth2",
|
||||
"label": "Enable OAuth2 Authentication",
|
||||
"help": "Allow a WordPress-based (miniOrange) OAuth2 service to assign users to groups based on their role with the service.",
|
||||
"default": false,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "oauth2_url_path",
|
||||
"label": "Authentication URL",
|
||||
"help": "The URL that the Interface will use to login via OAuth2.",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "wordpress_url_base",
|
||||
"label": "WordPress API URL Base",
|
||||
"help": "The URL base that the domain server will use to make WordPress API calls. Typically \"https://oursite.com/wp-json/\". However, if using non-pretty permalinks or otherwise get a 404 error then try \"https://oursite.com/?rest_route=/\".",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "plugin_client_id",
|
||||
"label": "WordPress Plugin Client ID",
|
||||
"help": "This is the client ID from the WordPress plugin configuration.",
|
||||
"advanced": true,
|
||||
"backup": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -65,7 +114,7 @@
|
|||
{
|
||||
"name": "enable_prometheus_exporter",
|
||||
"label": "Enable Prometheus Exporter",
|
||||
"help": "Enable a Prometheus exporter to make it possible to gather the stats that are available at <a href='/'>Nodes</a> tab with a <a href='https://prometheus.io/'>Prometheus</a> server. This makes it possible to keep track of long-term domain statistics for graphing, troubleshooting, and performance monitoring.",
|
||||
"help": "Enable a Prometheus exporter to make it possible to gather stats about the mixers that are available in the <a href='/'>Nodes</a> tab with a <a href='https://prometheus.io/'>Prometheus</a> server. This makes it possible to keep track of long-term domain statistics for graphing, troubleshooting, and performance monitoring.",
|
||||
"default": false,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
|
@ -113,12 +162,40 @@
|
|||
"restart": false,
|
||||
"help": "This data will be queryable from your server. It may be collected by High Fidelity and used to share your domain with others.",
|
||||
"settings": [
|
||||
{
|
||||
"name": "world_name",
|
||||
"label": "Name",
|
||||
"advanced": true,
|
||||
"help": "The name of your domain (256 character limit)."
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"label": "Description",
|
||||
"advanced": true,
|
||||
"help": "A description of your domain (256 character limit)."
|
||||
},
|
||||
{
|
||||
"name": "thumbnail",
|
||||
"label": "World Thumbnail",
|
||||
"advanced": true,
|
||||
"help": "A link to the thumbnail that is publicly accessible from the internet."
|
||||
},
|
||||
{
|
||||
"name": "images",
|
||||
"label": "World Images",
|
||||
"advanced": true,
|
||||
"type": "table",
|
||||
"can_add_new_rows": true,
|
||||
"help": "URLs to images that visually describe your world to potential visitors.",
|
||||
"numbered": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "image",
|
||||
"label": "Image URL",
|
||||
"can_set": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "maturity",
|
||||
"label": "Maturity",
|
||||
|
@ -150,16 +227,22 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "hosts",
|
||||
"label": "Hosts",
|
||||
"name": "contact_info",
|
||||
"label": "World Administrative Contact",
|
||||
"advanced": true,
|
||||
"help": "Contact information to reach server administrators for assistance (256 character limit)."
|
||||
},
|
||||
{
|
||||
"name": "managers",
|
||||
"label": "World Managers / Administrators",
|
||||
"advanced": true,
|
||||
"type": "table",
|
||||
"can_add_new_rows": true,
|
||||
"help": "Usernames of hosts who can reliably show your domain to new visitors.",
|
||||
"help": "Usernames of managers that administrate the domain.",
|
||||
"numbered": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "host",
|
||||
"name": "manager",
|
||||
"label": "Username",
|
||||
"can_set": true
|
||||
}
|
||||
|
@ -210,6 +293,14 @@
|
|||
"help": "Must match the password entered above for change to be saved.",
|
||||
"value-hidden": true
|
||||
},
|
||||
{
|
||||
"name": "approved_safe_urls",
|
||||
"label": "Approved Script and QML URLs",
|
||||
"help": "These URLs will be sent to the Interface as safe URLs to allow through the whitelist if the Interface has this security option enabled.",
|
||||
"placeholder": "0",
|
||||
"default": "1",
|
||||
"advanced": false
|
||||
},
|
||||
{
|
||||
"name": "maximum_user_capacity",
|
||||
"label": "Maximum User Capacity",
|
||||
|
@ -247,7 +338,7 @@
|
|||
"name": "standard_permissions",
|
||||
"type": "table",
|
||||
"label": "Domain-Wide User Permissions",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"caption": "Standard Permissions",
|
||||
"can_add_new_rows": false,
|
||||
"groups": [
|
||||
|
@ -256,7 +347,7 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"span": 11
|
||||
}
|
||||
],
|
||||
|
@ -337,7 +428,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id_can_get_and_set_private_user_data",
|
||||
"label": "Can Get and Set Private User Data",
|
||||
"label": "Get and Set Private User Data",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
|
@ -381,6 +472,7 @@
|
|||
"name": "group_permissions",
|
||||
"type": "table",
|
||||
"caption": "Permissions for Users in Groups",
|
||||
"help": "For groups that are provided from WordPress you need to denote them by putting an \"@\" symbol in front of each item, e.g., \"@silver\".",
|
||||
"categorize_by_key": "permissions_id",
|
||||
"can_add_new_categories": true,
|
||||
"can_add_new_rows": false,
|
||||
|
@ -392,7 +484,7 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 11
|
||||
}
|
||||
],
|
||||
|
@ -498,7 +590,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id_can_get_and_set_private_user_data",
|
||||
"label": "Can Get and Set Private User Data",
|
||||
"label": "Get and Set Private User Data",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
|
@ -509,6 +601,7 @@
|
|||
"name": "group_forbiddens",
|
||||
"type": "table",
|
||||
"caption": "Permissions Denied to Users in Groups",
|
||||
"help": "For groups that are provided from WordPress you need to denote them by putting an \"@\" symbol in front of each item, e.g., \"@silver\".",
|
||||
"categorize_by_key": "permissions_id",
|
||||
"can_add_new_categories": true,
|
||||
"can_add_new_rows": false,
|
||||
|
@ -520,7 +613,7 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 11
|
||||
}
|
||||
],
|
||||
|
@ -623,7 +716,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id_can_get_and_set_private_user_data",
|
||||
"label": "Can Get and Set Private User Data",
|
||||
"label": "Get and Set Private User Data",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
|
@ -641,7 +734,7 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 11
|
||||
}
|
||||
],
|
||||
|
@ -722,7 +815,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id_can_get_and_set_private_user_data",
|
||||
"label": "Can Get and Set Private User Data",
|
||||
"label": "Get and Set Private User Data",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
|
@ -740,7 +833,7 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users from specific IPs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users from specific IPs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users from specific IPs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users from specific IPs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 11
|
||||
}
|
||||
],
|
||||
|
@ -821,7 +914,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id_can_get_and_set_private_user_data",
|
||||
"label": "Can Get and Set Private User Data",
|
||||
"label": "Get and Set Private User Data",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
|
@ -839,7 +932,7 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific MACs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific MACs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific MACs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific MACs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 11
|
||||
}
|
||||
],
|
||||
|
@ -920,7 +1013,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id_can_get_and_set_private_user_data",
|
||||
"label": "Can Get and Set Private User Data",
|
||||
"label": "Get and Set Private User Data",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
|
@ -938,7 +1031,7 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 11
|
||||
}
|
||||
],
|
||||
|
@ -1019,7 +1112,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id_can_get_and_set_private_user_data",
|
||||
"label": "Can Get and Set Private User Data",
|
||||
"label": "Get and Set Private User Data",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
|
|
26
domain-server/resources/metadata_exporter/index.html
Normal file
26
domain-server/resources/metadata_exporter/index.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!--
|
||||
//
|
||||
// index.html
|
||||
//
|
||||
// Created by kasenvr@gmail.com on 21 Jul 2020
|
||||
// Copyright 2020 Vircadia and contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Vircadia Metadata Exporter</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Vircadia Metadata Exporter</h1>
|
||||
|
||||
<p>If you can see this page, this means that your domain's metadata is available to be exported.</p>
|
||||
<p>
|
||||
<a href="/metadata">Metadata</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,3 +1,3 @@
|
|||
// Here you can put a script that will be run by an assignment-client (AC)
|
||||
// For examples, please go to https://github.com/highfidelity/hifi/tree/master/script-archive/acScripts
|
||||
// For examples, please go to https://github.com/kasenvr/project-athena/tree/master/script-archive/acScripts
|
||||
// The directory named acScripts contains assignment-client specific scripts you can try.
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 4.2 KiB |
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2015-08-24.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -94,6 +95,9 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
} else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) {
|
||||
QByteArray usernameSignature;
|
||||
|
||||
QString domainUsername;
|
||||
QStringList domainTokens;
|
||||
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// read username from packet
|
||||
packetStream >> username;
|
||||
|
@ -101,10 +105,25 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
if (message->getBytesLeftToRead() > 0) {
|
||||
// read user signature from packet
|
||||
packetStream >> usernameSignature;
|
||||
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// Read domain username from packet.
|
||||
packetStream >> domainUsername;
|
||||
domainUsername = domainUsername.toLower(); // Domain usernames are case-insensitive; internally lower-case.
|
||||
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// Read domain tokens from packet.
|
||||
|
||||
QString domainTokensString;
|
||||
packetStream >> domainTokensString;
|
||||
domainTokens = domainTokensString.split(":");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = processAgentConnectRequest(nodeConnection, username, usernameSignature);
|
||||
node = processAgentConnectRequest(nodeConnection, username, usernameSignature,
|
||||
domainUsername, domainTokens.value(0), domainTokens.value(1));
|
||||
}
|
||||
|
||||
if (node) {
|
||||
|
@ -142,7 +161,8 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
}
|
||||
}
|
||||
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
|
||||
QString verifiedDomainUserName, const QHostAddress& senderAddress,
|
||||
const QString& hardwareAddress, const QUuid& machineFingerprint) {
|
||||
NodePermissions userPerms;
|
||||
|
||||
|
@ -155,6 +175,27 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
#endif
|
||||
}
|
||||
|
||||
// If this user is a known member of a domain group, give them the implied permissions.
|
||||
// Do before processing verifiedUsername in case user is logged into the metaverse and is a member of a blacklist group.
|
||||
if (!verifiedDomainUserName.isEmpty()) {
|
||||
auto userGroups = _domainGroupMemberships[verifiedDomainUserName];
|
||||
foreach (QString userGroup, userGroups) {
|
||||
// A domain group is signified by a leading special character, "@".
|
||||
// Multiple domain groups may be specified in one domain server setting as a comma- and/or space-separated lists of
|
||||
// domain group names. For example, "@silver @Gold, @platinum".
|
||||
auto domainGroups = _server->_settingsManager.getDomainServerGroupNames()
|
||||
.filter(QRegularExpression("^(.*[\\s,])?" + QRegularExpression::escape(userGroup) + "([\\s,].*)?$",
|
||||
QRegularExpression::CaseInsensitiveOption));
|
||||
foreach(QString domainGroup, domainGroups) {
|
||||
userPerms |= _server->_settingsManager.getPermissionsForGroup(domainGroup, QUuid()); // No rank for domain groups.
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: domain user " << verifiedDomainUserName << "is in group:" << domainGroup
|
||||
<< "so:" << userPerms;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verifiedUsername.isEmpty()) {
|
||||
userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous);
|
||||
#ifdef WANT_DEBUG
|
||||
|
@ -256,6 +297,27 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
userPerms.setVerifiedUserName(verifiedUsername);
|
||||
}
|
||||
|
||||
// If this user is a known member of an domain group that is blacklisted, remove the implied permissions.
|
||||
if (!verifiedDomainUserName.isEmpty()) {
|
||||
auto userGroups = _domainGroupMemberships[verifiedDomainUserName];
|
||||
foreach(QString userGroup, userGroups) {
|
||||
// A domain group is signified by a leading special character, "@".
|
||||
// Multiple domain groups may be specified in one domain server setting as a comma- and/or space-separated lists of
|
||||
// domain group names. For example, "@silver @Gold, @platinum".
|
||||
auto domainGroups = _server->_settingsManager.getDomainServerBlacklistGroupNames()
|
||||
.filter(QRegularExpression("^(.*[\\s,])?" + QRegularExpression::escape(userGroup) + "([\\s,].*)?$",
|
||||
QRegularExpression::CaseInsensitiveOption));
|
||||
foreach(QString domainGroup, domainGroups) {
|
||||
userPerms &= ~_server->_settingsManager.getForbiddensForGroup(domainGroup, QUuid());
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: domain user is in blacklist group:" << domainGroup << "so:" << userPerms;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
userPerms.setVerifiedDomainUserName(verifiedDomainUserName);
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: final:" << userPerms;
|
||||
#endif
|
||||
|
@ -275,6 +337,7 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
// the id and the username in NodePermissions will often be the same, but id is set before
|
||||
// authentication and verifiedUsername is only set once they user's key has been confirmed.
|
||||
QString verifiedUsername = node->getPermissions().getVerifiedUserName();
|
||||
QString verifiedDomainUserName = node->getPermissions().getVerifiedDomainUserName();
|
||||
NodePermissions userPerms(NodePermissionsKey(verifiedUsername, 0));
|
||||
|
||||
if (node->getPermissions().isAssignment) {
|
||||
|
@ -309,7 +372,8 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
sendingAddress == QHostAddress::LocalHost);
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUserName,
|
||||
connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
|
||||
}
|
||||
|
||||
node->setPermissions(userPerms);
|
||||
|
@ -387,12 +451,19 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
|||
return newNode;
|
||||
}
|
||||
|
||||
const QString AUTHENTICATION_ENABLE_OAUTH2 = "authentication.enable_oauth2";
|
||||
const QString AUTHENTICATION_OAUTH2_URL_PATH = "authentication.oauth2_url_path";
|
||||
const QString AUTHENTICATION_WORDPRESS_URL_BASE = "authentication.wordpress_url_base";
|
||||
const QString AUTHENTICATION_PLUGIN_CLIENT_ID = "authentication.plugin_client_id";
|
||||
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
||||
const QString MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION = "security.maximum_user_capacity_redirect_location";
|
||||
|
||||
SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature) {
|
||||
const QByteArray& usernameSignature,
|
||||
const QString& domainUsername,
|
||||
const QString& domainAccessToken,
|
||||
const QString& domainRefreshToken) {
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
|
@ -419,7 +490,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
#ifdef WANT_DEBUG
|
||||
qDebug() << "stalling login because we have no username-signature:" << username;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
if (!domainHasLogin() || domainUsername.isEmpty()) {
|
||||
return SharedNodePointer();
|
||||
}
|
||||
} else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) {
|
||||
// they sent us a username and the signature verifies it
|
||||
getGroupMemberships(username);
|
||||
|
@ -430,16 +503,70 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
#ifdef WANT_DEBUG
|
||||
qDebug() << "stalling login because signature verification failed:" << username;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
if (!domainHasLogin() || domainUsername.isEmpty()) {
|
||||
return SharedNodePointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(),
|
||||
nodeConnection.hardwareAddress, nodeConnection.machineFingerprint);
|
||||
// The domain may have its own users and groups.
|
||||
QString verifiedDomainUsername;
|
||||
QStringList verifiedDomainUserGroups;
|
||||
if (domainHasLogin() && !domainUsername.isEmpty()) {
|
||||
|
||||
if (domainAccessToken.isEmpty()) {
|
||||
// User is attempting to prove their domain identity.
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "Stalling login because we have no domain OAuth2 tokens:" << domainUsername;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
|
||||
} else if (needToVerifyDomainUserIdentity(domainUsername, domainAccessToken, domainRefreshToken)) {
|
||||
// User's domain identity needs to be confirmed.
|
||||
requestDomainUser(domainUsername, domainAccessToken, domainRefreshToken);
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "Stalling login because we haven't authenticated user yet:" << domainUsername;
|
||||
#endif
|
||||
|
||||
} else if (verifyDomainUserIdentity(domainUsername, domainAccessToken, domainRefreshToken,
|
||||
nodeConnection.senderSockAddr)) {
|
||||
// User's domain identity is confirmed.
|
||||
verifiedDomainUsername = domainUsername;
|
||||
|
||||
} else {
|
||||
// User's domain identity didn't check out.
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "Stalling login because domain user verification failed:" << domainUsername;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUsername,
|
||||
nodeConnection.senderSockAddr.getAddress(), nodeConnection.hardwareAddress,
|
||||
nodeConnection.machineFingerprint);
|
||||
|
||||
if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.",
|
||||
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorized);
|
||||
if (domainHasLogin()) {
|
||||
QString domainAuthURL;
|
||||
auto domainAuthURLVariant = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_OAUTH2_URL_PATH);
|
||||
if (domainAuthURLVariant.canConvert<QString>()) {
|
||||
domainAuthURL = domainAuthURLVariant.toString();
|
||||
}
|
||||
QString domainAuthClientID;
|
||||
auto domainAuthClientIDVariant = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_PLUGIN_CLIENT_ID);
|
||||
if (domainAuthClientIDVariant.canConvert<QString>()) {
|
||||
domainAuthClientID = domainAuthClientIDVariant.toString();
|
||||
}
|
||||
|
||||
sendConnectionDeniedPacket("You lack the required domain permissions to connect to this domain.",
|
||||
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedDomain,
|
||||
domainAuthURL + "|" + domainAuthClientID);
|
||||
} else {
|
||||
sendConnectionDeniedPacket("You lack the required metaverse permissions to connect to this domain.",
|
||||
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedMetaverse);
|
||||
}
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "stalling login due to permissions:" << username;
|
||||
#endif
|
||||
|
@ -600,15 +727,15 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
|||
return true;
|
||||
|
||||
} else {
|
||||
// we only send back a LoginError if this wasn't an "optimistic" key
|
||||
// we only send back a LoginErrorMetaverse if this wasn't an "optimistic" key
|
||||
// (a key that we hoped would work but is probably stale)
|
||||
|
||||
if (!senderSockAddr.isNull() && !isOptimisticKey) {
|
||||
qDebug() << "Error decrypting username signature for" << username << "- denying connection.";
|
||||
qDebug() << "Error decrypting metaverse username signature for" << username << "- denying connection.";
|
||||
sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr,
|
||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
||||
DomainHandler::ConnectionRefusedReason::LoginErrorMetaverse);
|
||||
} else if (!senderSockAddr.isNull()) {
|
||||
qDebug() << "Error decrypting username signature for" << username << "with optimisitic key -"
|
||||
qDebug() << "Error decrypting metaverse username signature for" << username << "with optimistic key -"
|
||||
<< "re-requesting public key and delaying connection";
|
||||
}
|
||||
|
||||
|
@ -622,7 +749,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
|||
if (!senderSockAddr.isNull()) {
|
||||
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
|
||||
sendConnectionDeniedPacket("Couldn't convert data to RSA key.", senderSockAddr,
|
||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
||||
DomainHandler::ConnectionRefusedReason::LoginErrorMetaverse);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -635,6 +762,25 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool DomainGatekeeper::needToVerifyDomainUserIdentity(const QString& username, const QString& accessToken,
|
||||
const QString& refreshToken) {
|
||||
return !_verifiedDomainUserIdentities.contains(username)
|
||||
|| _verifiedDomainUserIdentities.value(username) != QPair<QString, QString>(accessToken, refreshToken);
|
||||
}
|
||||
|
||||
bool DomainGatekeeper::verifyDomainUserIdentity(const QString& username, const QString& accessToken,
|
||||
const QString& refreshToken, const HifiSockAddr& senderSockAddr) {
|
||||
if (_verifiedDomainUserIdentities.contains(username)
|
||||
&& _verifiedDomainUserIdentities.value(username) == QPair<QString, QString>(accessToken, refreshToken)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sendConnectionDeniedPacket("Error verifying domain user.", senderSockAddr,
|
||||
DomainHandler::ConnectionRefusedReason::LoginErrorDomain);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DomainGatekeeper::isWithinMaxCapacity() {
|
||||
// find out what our maximum capacity is
|
||||
QVariant maximumUserCapacityVariant =
|
||||
|
@ -907,7 +1053,6 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
|
|||
AccountManagerAuth::Required,
|
||||
QNetworkAccessManager::PostOperation, callbackParams,
|
||||
QJsonDocument(json).toJson());
|
||||
|
||||
}
|
||||
|
||||
QString extractUsernameFromGroupMembershipsReply(QNetworkReply* requestReply) {
|
||||
|
@ -962,6 +1107,7 @@ void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply* requestReply
|
|||
_inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply));
|
||||
}
|
||||
|
||||
|
||||
void DomainGatekeeper::getDomainOwnerFriendsList() {
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.callbackReceiver = this;
|
||||
|
@ -1010,6 +1156,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply* req
|
|||
qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply->error();
|
||||
}
|
||||
|
||||
// ####### TODO: Domain equivalent or addition
|
||||
void DomainGatekeeper::refreshGroupsCache() {
|
||||
// if agents are connected to this domain, refresh our cached information about groups and memberships in such.
|
||||
getDomainOwnerFriendsList();
|
||||
|
@ -1029,7 +1176,7 @@ void DomainGatekeeper::refreshGroupsCache() {
|
|||
|
||||
updateNodePermissions();
|
||||
|
||||
#if WANT_DEBUG
|
||||
#ifdef WANT_DEBUG
|
||||
_server->_settingsManager.debugDumpGroupsState();
|
||||
#endif
|
||||
}
|
||||
|
@ -1061,3 +1208,91 @@ Node::LocalID DomainGatekeeper::findOrCreateLocalID(const QUuid& uuid) {
|
|||
_localIDs.insert(newLocalID);
|
||||
return newLocalID;
|
||||
}
|
||||
|
||||
|
||||
bool DomainGatekeeper::domainHasLogin() {
|
||||
// The domain may have its own users and groups in a WordPress site.
|
||||
return _server->_settingsManager.valueForKeyPath(AUTHENTICATION_ENABLE_OAUTH2).toBool()
|
||||
&& !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_OAUTH2_URL_PATH).toString().isEmpty()
|
||||
&& !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_WORDPRESS_URL_BASE).toString().isEmpty()
|
||||
&& !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_PLUGIN_CLIENT_ID).toString().isEmpty();
|
||||
}
|
||||
|
||||
void DomainGatekeeper::requestDomainUser(const QString& username, const QString& accessToken, const QString& refreshToken) {
|
||||
|
||||
if (_inFlightDomainUserIdentityRequests.contains(username)) {
|
||||
// Domain identify request for this username is already in progress.
|
||||
return;
|
||||
}
|
||||
_inFlightDomainUserIdentityRequests.insert(username, QPair<QString, QString>(accessToken, refreshToken));
|
||||
|
||||
if (_verifiedDomainUserIdentities.contains(username)) {
|
||||
_verifiedDomainUserIdentities.remove(username);
|
||||
}
|
||||
|
||||
QString apiBase = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_WORDPRESS_URL_BASE).toString();
|
||||
if (!apiBase.endsWith("/")) {
|
||||
apiBase += "/";
|
||||
}
|
||||
|
||||
// Get data pertaining to "me", the user who generated the access token.
|
||||
const QString WORDPRESS_USER_ROUTE = "wp/v2/users/me";
|
||||
const QString WORDPRESS_USER_QUERY = "_fields=username,roles";
|
||||
QUrl domainUserURL = apiBase + WORDPRESS_USER_ROUTE + (apiBase.contains("?") ? "&" : "?") + WORDPRESS_USER_QUERY;
|
||||
|
||||
QNetworkRequest request;
|
||||
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
request.setRawHeader(QByteArray("Authorization"), QString("Bearer " + accessToken).toUtf8());
|
||||
|
||||
QByteArray formData; // No data to send.
|
||||
|
||||
request.setUrl(domainUserURL);
|
||||
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* requestReply = networkAccessManager.post(request, formData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &DomainGatekeeper::requestDomainUserFinished);
|
||||
}
|
||||
|
||||
void DomainGatekeeper::requestDomainUserFinished() {
|
||||
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
const QJsonObject& rootObject = jsonResponse.object();
|
||||
|
||||
auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (200 <= httpStatus && httpStatus < 300) {
|
||||
|
||||
QString username = rootObject.value("username").toString().toLower();
|
||||
if (_inFlightDomainUserIdentityRequests.contains(username)) {
|
||||
// Success! Verified user.
|
||||
_verifiedDomainUserIdentities.insert(username, _inFlightDomainUserIdentityRequests.value(username));
|
||||
_inFlightDomainUserIdentityRequests.remove(username);
|
||||
|
||||
// User user's WordPress roles as domain groups.
|
||||
QStringList domainUserGroups;
|
||||
auto userRoles = rootObject.value("roles").toArray();
|
||||
foreach (auto role, userRoles) {
|
||||
// Distinguish domain groups from metaverse groups by adding a leading special character.
|
||||
domainUserGroups.append(DOMAIN_GROUP_CHAR + role.toString().toLower());
|
||||
}
|
||||
_domainGroupMemberships[username] = domainUserGroups;
|
||||
|
||||
} else {
|
||||
// Failure.
|
||||
qDebug() << "Unexpected username in response for user details -" << username;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Failure.
|
||||
qDebug() << "Error in response for user details -" << httpStatus << requestReply->error()
|
||||
<< "-" << rootObject["error"].toString() << rootObject["error_description"].toString();
|
||||
|
||||
_inFlightDomainUserIdentityRequests.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2015-08-24.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -29,6 +30,8 @@
|
|||
#include "NodeConnectionData.h"
|
||||
#include "PendingAssignedNodeData.h"
|
||||
|
||||
const QString DOMAIN_GROUP_CHAR = "@";
|
||||
|
||||
class DomainServer;
|
||||
|
||||
class DomainGatekeeper : public QObject {
|
||||
|
@ -71,16 +74,28 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void handlePeerPingTimeout();
|
||||
|
||||
// Login and groups for domain, separate from metaverse.
|
||||
void requestDomainUserFinished();
|
||||
|
||||
private:
|
||||
SharedNodePointer processAssignmentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const PendingAssignedNodeData& pendingAssignment);
|
||||
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature);
|
||||
const QByteArray& usernameSignature,
|
||||
const QString& domainUsername,
|
||||
const QString& domainAccessToken,
|
||||
const QString& domainRefreshToken);
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
|
||||
|
||||
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
||||
bool needToVerifyDomainUserIdentity(const QString& username, const QString& accessToken, const QString& refreshToken);
|
||||
bool verifyDomainUserIdentity(const QString& username, const QString& accessToken, const QString& refreshToken,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
||||
bool isWithinMaxCapacity();
|
||||
|
||||
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
||||
|
@ -120,8 +135,9 @@ private:
|
|||
QSet<QString> _domainOwnerFriends; // keep track of friends of the domain owner
|
||||
QSet<QString> _inFlightGroupMembershipsRequests; // keep track of which we've already asked for
|
||||
|
||||
NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
|
||||
const QString& hardwareAddress, const QUuid& machineFingerprint);
|
||||
NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, QString verifiedDomainUsername,
|
||||
const QHostAddress& senderAddress, const QString& hardwareAddress,
|
||||
const QUuid& machineFingerprint);
|
||||
|
||||
void getGroupMemberships(const QString& username);
|
||||
// void getIsGroupMember(const QString& username, const QUuid groupID);
|
||||
|
@ -133,9 +149,18 @@ private:
|
|||
using LocalIDs = std::unordered_set<Node::LocalID>;
|
||||
LocalIDs _localIDs;
|
||||
UUIDToLocalID _uuidToLocalID;
|
||||
|
||||
Node::LocalID _currentLocalID;
|
||||
Node::LocalID _idIncrement;
|
||||
|
||||
// Login and groups for domain, separate from metaverse.
|
||||
bool domainHasLogin();
|
||||
void requestDomainUser(const QString& username, const QString& accessToken, const QString& refreshToken);
|
||||
|
||||
typedef QHash<QString, QPair<QString, QString>> DomainUserIdentities; // <domainUserName, <access_token, refresh_token>>
|
||||
DomainUserIdentities _inFlightDomainUserIdentityRequests; // Domain user identity requests currently in progress.
|
||||
DomainUserIdentities _verifiedDomainUserIdentities; // Verified domain users.
|
||||
|
||||
QHash<QString, QStringList> _domainGroupMemberships; // <domainUserName, [domainGroupName]>
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -4,20 +4,25 @@
|
|||
//
|
||||
// Created by Zach Pomerantz on 5/25/2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
#include "DomainMetadata.h"
|
||||
#include "HTTPConnection.h"
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <HifiConfigVariantMap.h>
|
||||
#include <LimitedNodeList.h>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "DomainServer.h"
|
||||
#include "DomainServerNodeData.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(domain_metadata_exporter, "hifi.domain_server.metadata_exporter")
|
||||
|
||||
const QString DomainMetadata::USERS = "users";
|
||||
const QString DomainMetadata::Users::NUM_TOTAL = "num_users";
|
||||
const QString DomainMetadata::Users::NUM_ANON = "num_anon_users";
|
||||
|
@ -29,18 +34,28 @@ const QString DomainMetadata::Users::HOSTNAMES = "user_hostnames";
|
|||
// }
|
||||
|
||||
const QString DomainMetadata::DESCRIPTORS = "descriptors";
|
||||
const QString DomainMetadata::Descriptors::NAME = "world_name";
|
||||
const QString DomainMetadata::Descriptors::DESCRIPTION = "description";
|
||||
const QString DomainMetadata::Descriptors::THUMBNAIL = "thumbnail";
|
||||
const QString DomainMetadata::Descriptors::IMAGES = "images";
|
||||
const QString DomainMetadata::Descriptors::CAPACITY = "capacity"; // parsed from security
|
||||
const QString DomainMetadata::Descriptors::RESTRICTION = "restriction"; // parsed from ACL
|
||||
const QString DomainMetadata::Descriptors::MATURITY = "maturity";
|
||||
const QString DomainMetadata::Descriptors::HOSTS = "hosts";
|
||||
const QString DomainMetadata::Descriptors::CONTACT = "contact_info";
|
||||
const QString DomainMetadata::Descriptors::MANAGERS = "managers";
|
||||
const QString DomainMetadata::Descriptors::TAGS = "tags";
|
||||
|
||||
// descriptors metadata will appear as (JSON):
|
||||
// { "description": String, // capped description
|
||||
// {
|
||||
// "world_name": String, // capped name
|
||||
// "description": String, // capped description
|
||||
// "thumbnail": String, // capped thumbnail URL
|
||||
// "images": [ String ], // capped list of image URLs
|
||||
// "capacity": Number,
|
||||
// "restriction": String, // enum of either open, hifi, or acl
|
||||
// "maturity": String, // enum corresponding to ESRB ratings
|
||||
// "hosts": [ String ], // capped list of usernames
|
||||
// "contact_info": [ String ], // capped list of usernames
|
||||
// "managers": [ String ], // capped list of usernames
|
||||
// "tags": [ String ], // capped list of tags
|
||||
// }
|
||||
|
||||
|
@ -54,17 +69,6 @@ DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) {
|
|||
_metadata[USERS] = QVariantMap {};
|
||||
_metadata[DESCRIPTORS] = QVariantMap {};
|
||||
|
||||
assert(dynamic_cast<DomainServer*>(domainServer));
|
||||
DomainServer* server = static_cast<DomainServer*>(domainServer);
|
||||
|
||||
// update the metadata when a user (dis)connects
|
||||
connect(server, &DomainServer::userConnected, this, &DomainMetadata::usersChanged);
|
||||
connect(server, &DomainServer::userDisconnected, this, &DomainMetadata::usersChanged);
|
||||
|
||||
// update the metadata when security changes
|
||||
connect(&server->_settingsManager, &DomainServerSettingsManager::updateNodePermissions,
|
||||
this, static_cast<void(DomainMetadata::*)()>(&DomainMetadata::securityChanged));
|
||||
|
||||
// initialize the descriptors
|
||||
securityChanged(false);
|
||||
descriptorsChanged();
|
||||
|
@ -88,12 +92,40 @@ void DomainMetadata::descriptorsChanged() {
|
|||
static const QString DESCRIPTORS_GROUP_KEYPATH = "descriptors";
|
||||
auto descriptorsMap = static_cast<DomainServer*>(parent())->_settingsManager.valueForKeyPath(DESCRIPTORS).toMap();
|
||||
|
||||
// copy simple descriptors (description/maturity)
|
||||
state[Descriptors::DESCRIPTION] = descriptorsMap[Descriptors::DESCRIPTION];
|
||||
state[Descriptors::MATURITY] = descriptorsMap[Descriptors::MATURITY];
|
||||
// copy simple descriptors
|
||||
if (!descriptorsMap[Descriptors::NAME].isNull()) {
|
||||
state[Descriptors::NAME] = descriptorsMap[Descriptors::NAME];
|
||||
} else {
|
||||
state[Descriptors::NAME] = "";
|
||||
}
|
||||
|
||||
// copy array descriptors (hosts/tags)
|
||||
state[Descriptors::HOSTS] = descriptorsMap[Descriptors::HOSTS].toList();
|
||||
if (!descriptorsMap[Descriptors::DESCRIPTION].isNull()) {
|
||||
state[Descriptors::DESCRIPTION] = descriptorsMap[Descriptors::DESCRIPTION];
|
||||
} else {
|
||||
state[Descriptors::DESCRIPTION] = "";
|
||||
}
|
||||
|
||||
if (!descriptorsMap[Descriptors::THUMBNAIL].isNull()) {
|
||||
state[Descriptors::THUMBNAIL] = descriptorsMap[Descriptors::THUMBNAIL];
|
||||
} else {
|
||||
state[Descriptors::THUMBNAIL] = "";
|
||||
}
|
||||
|
||||
if (!descriptorsMap[Descriptors::MATURITY].isNull()) {
|
||||
state[Descriptors::MATURITY] = descriptorsMap[Descriptors::MATURITY];
|
||||
} else {
|
||||
state[Descriptors::MATURITY] = "";
|
||||
}
|
||||
|
||||
if (!descriptorsMap[Descriptors::CONTACT].isNull()) {
|
||||
state[Descriptors::CONTACT] = descriptorsMap[Descriptors::CONTACT];
|
||||
} else {
|
||||
state[Descriptors::CONTACT] = "";
|
||||
}
|
||||
|
||||
// copy array descriptors
|
||||
state[Descriptors::IMAGES] = descriptorsMap[Descriptors::IMAGES].toList();
|
||||
state[Descriptors::MANAGERS] = descriptorsMap[Descriptors::MANAGERS].toList();
|
||||
state[Descriptors::TAGS] = descriptorsMap[Descriptors::TAGS].toList();
|
||||
|
||||
// parse capacity
|
||||
|
@ -198,7 +230,7 @@ void DomainMetadata::maybeUpdateUsers() {
|
|||
}
|
||||
|
||||
void DomainMetadata::sendDescriptors() {
|
||||
QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(get(DESCRIPTORS)).toJson(QJsonDocument::Compact)));
|
||||
QString domainUpdateJSON = QString("{\"domain\":{\"meta\":%1}").arg(QString(QJsonDocument(get(DESCRIPTORS)).toJson(QJsonDocument::Compact)));
|
||||
const QUuid& domainID = DependencyManager::get<LimitedNodeList>()->getSessionUUID();
|
||||
if (!domainID.isNull()) {
|
||||
static const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
|
||||
|
@ -215,3 +247,22 @@ void DomainMetadata::sendDescriptors() {
|
|||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool DomainMetadata::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
|
||||
QString domainMetadataJSON = QString("{\"domain\":{\"meta\":%1}, \"users\":%2}")
|
||||
.arg(QString(QJsonDocument(get(DESCRIPTORS)).toJson(QJsonDocument::Compact)))
|
||||
.arg(QString(QJsonDocument(get(USERS)).toJson(QJsonDocument::Compact)));
|
||||
const QString URI_METADATA = "/metadata";
|
||||
const QString EXPORTER_MIME_TYPE = "application/json";
|
||||
|
||||
if (url.path() == URI_METADATA) {
|
||||
connection->respond(HTTPConnection::StatusCode200, domainMetadataJSON.toUtf8(), qPrintable(EXPORTER_MIME_TYPE));
|
||||
return true;
|
||||
}
|
||||
|
||||
#if DEV_BUILD || PR_BUILD
|
||||
qCDebug(domain_metadata_exporter) << "Metadata request on URL " << url;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
|
||||
#include <QVariantMap>
|
||||
#include <QJsonObject>
|
||||
#include "HTTPManager.h"
|
||||
|
||||
class DomainMetadata : public QObject {
|
||||
class DomainMetadata : public QObject, public HTTPRequestHandler {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -33,25 +34,29 @@ public:
|
|||
static const QString DESCRIPTORS;
|
||||
class Descriptors {
|
||||
public:
|
||||
static const QString NAME;
|
||||
static const QString DESCRIPTION;
|
||||
static const QString THUMBNAIL;
|
||||
static const QString IMAGES;
|
||||
static const QString CAPACITY;
|
||||
static const QString RESTRICTION;
|
||||
static const QString MATURITY;
|
||||
static const QString HOSTS;
|
||||
static const QString CONTACT;
|
||||
static const QString MANAGERS;
|
||||
static const QString TAGS;
|
||||
};
|
||||
|
||||
DomainMetadata(QObject* domainServer);
|
||||
DomainMetadata() = delete;
|
||||
|
||||
~DomainMetadata() = default;
|
||||
// Get cached metadata
|
||||
QJsonObject get();
|
||||
QJsonObject get(const QString& group);
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;
|
||||
|
||||
public slots:
|
||||
void descriptorsChanged();
|
||||
void securityChanged(bool send);
|
||||
void securityChanged() { securityChanged(true); }
|
||||
void usersChanged();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -70,7 +70,6 @@ const QString DomainServer::REPLACEMENT_FILE_EXTENSION = ".replace";
|
|||
const int MIN_PORT = 1;
|
||||
const int MAX_PORT = 65535;
|
||||
|
||||
|
||||
int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
||||
|
||||
QString DomainServer::_iceServerAddr { NetworkingConstants::ICE_SERVER_DEFAULT_HOSTNAME };
|
||||
|
@ -122,7 +121,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
QUrl url{ MetaverseAPI::getCurrentMetaverseServerURL().toString() + metaversePath };
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
if (accessTokenVariant.isValid()) {
|
||||
|
@ -267,6 +266,13 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
|
||||
_metadata, &DomainMetadata::descriptorsChanged);
|
||||
|
||||
// update the metadata when a user (dis)connects
|
||||
connect(this, &DomainServer::userConnected, _metadata, &DomainMetadata::usersChanged);
|
||||
connect(this, &DomainServer::userDisconnected, _metadata, &DomainMetadata::usersChanged);
|
||||
|
||||
// update the metadata when security changes
|
||||
connect(&_settingsManager, &DomainServerSettingsManager::updateNodePermissions, [this] { _metadata->securityChanged(true); });
|
||||
|
||||
qDebug() << "domain-server is running";
|
||||
static const QString AC_SUBNET_WHITELIST_SETTING_PATH = "security.ac_subnet_whitelist";
|
||||
|
||||
|
@ -328,6 +334,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_nodePingMonitorTimer->start(NODE_PING_MONITOR_INTERVAL_MSECS);
|
||||
|
||||
initializeExporter();
|
||||
initializeMetadataExporter();
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
||||
|
@ -421,6 +428,11 @@ DomainServer::~DomainServer() {
|
|||
_contentManager->aboutToFinish();
|
||||
_contentManager->terminate();
|
||||
}
|
||||
|
||||
if (_httpMetadataExporterManager) {
|
||||
_httpMetadataExporterManager->close();
|
||||
delete _httpMetadataExporterManager;
|
||||
}
|
||||
|
||||
if (_httpExporterManager) {
|
||||
_httpExporterManager->close();
|
||||
|
@ -2458,7 +2470,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
url.setQuery("access_token=" + accessTokenVariant.toString());
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().put(req, doc.toJson());
|
||||
|
||||
|
@ -2559,7 +2571,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
|||
|
||||
QNetworkRequest tokenRequest(tokenRequestUrl);
|
||||
tokenRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit());
|
||||
|
@ -2871,7 +2883,7 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR
|
|||
|
||||
QNetworkRequest profileRequest(profileURL);
|
||||
profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
profileRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
profileRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
return NetworkAccessManager::getInstance().get(profileRequest);
|
||||
}
|
||||
|
||||
|
@ -3039,8 +3051,7 @@ void DomainServer::updateUpstreamNodes() {
|
|||
updateReplicationNodes(Upstream);
|
||||
}
|
||||
|
||||
void DomainServer::initializeExporter()
|
||||
{
|
||||
void DomainServer::initializeExporter() {
|
||||
static const QString ENABLE_EXPORTER = "monitoring.enable_prometheus_exporter";
|
||||
static const QString EXPORTER_PORT = "monitoring.prometheus_exporter_port";
|
||||
|
||||
|
@ -3056,7 +3067,39 @@ void DomainServer::initializeExporter()
|
|||
|
||||
if (isExporterEnabled && !_httpExporterManager) {
|
||||
qCInfo(domain_server) << "Starting Prometheus exporter on port " << exporterPort;
|
||||
_httpExporterManager = new HTTPManager(QHostAddress::Any, (quint16)exporterPort, QString("%1/resources/prometheus_exporter/").arg(QCoreApplication::applicationDirPath()), &_exporter);
|
||||
_httpExporterManager = new HTTPManager
|
||||
(
|
||||
QHostAddress::Any,
|
||||
(quint16)exporterPort,
|
||||
QString("%1/resources/prometheus_exporter/").arg(QCoreApplication::applicationDirPath()),
|
||||
&_exporter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::initializeMetadataExporter() {
|
||||
static const QString ENABLE_EXPORTER = "metaverse.enable_metadata_exporter";
|
||||
static const QString EXPORTER_PORT = "metaverse.metadata_exporter_port";
|
||||
|
||||
bool isMetadataExporterEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_EXPORTER).toBool();
|
||||
int metadataExporterPort = _settingsManager.valueOrDefaultValueForKeyPath(EXPORTER_PORT).toInt();
|
||||
|
||||
if (metadataExporterPort < MIN_PORT || metadataExporterPort > MAX_PORT) {
|
||||
qCWarning(domain_server) << "Metadata exporter port" << metadataExporterPort << "is out of range.";
|
||||
isMetadataExporterEnabled = false;
|
||||
}
|
||||
|
||||
qCDebug(domain_server) << "Setting up Metadata exporter.";
|
||||
|
||||
if (isMetadataExporterEnabled && !_httpMetadataExporterManager) {
|
||||
qCInfo(domain_server) << "Starting Metadata exporter on port" << metadataExporterPort;
|
||||
_httpMetadataExporterManager = new HTTPManager
|
||||
(
|
||||
QHostAddress::Any,
|
||||
(quint16)metadataExporterPort,
|
||||
QString("%1/resources/metadata_exporter/").arg(QCoreApplication::applicationDirPath()),
|
||||
_metadata
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ private slots:
|
|||
void updateDownstreamNodes();
|
||||
void updateUpstreamNodes();
|
||||
void initializeExporter();
|
||||
void initializeMetadataExporter();
|
||||
|
||||
void tokenGrantFinished();
|
||||
void profileRequestFinished();
|
||||
|
@ -240,6 +241,8 @@ private:
|
|||
|
||||
HTTPManager _httpManager;
|
||||
HTTPManager* _httpExporterManager { nullptr };
|
||||
HTTPManager* _httpMetadataExporterManager { nullptr };
|
||||
|
||||
std::unique_ptr<HTTPSManager> _httpsManager;
|
||||
|
||||
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
|
||||
|
|
|
@ -1966,6 +1966,10 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() {
|
|||
QStringList groupNames = getAllKnownGroupNames();
|
||||
foreach (QString groupName, groupNames) {
|
||||
QString lowerGroupName = groupName.toLower();
|
||||
if (lowerGroupName.startsWith(DOMAIN_GROUP_CHAR)) {
|
||||
// Ignore domain groups. (Assumption: metaverse group names can't start with a "@".)
|
||||
return;
|
||||
}
|
||||
if (_groupIDs.contains(lowerGroupName)) {
|
||||
// we already know about this one. recall setGroupID in case the group has been
|
||||
// added to another section (the same group is found in both groups and blacklists).
|
||||
|
@ -2185,6 +2189,24 @@ QList<QUuid> DomainServerSettingsManager::getBlacklistGroupIDs() {
|
|||
return result.toList();
|
||||
}
|
||||
|
||||
QStringList DomainServerSettingsManager::getDomainServerGroupNames() {
|
||||
// All names as listed in the domain server settings; both metaverse groups and domain groups
|
||||
QSet<QString> result;
|
||||
foreach(NodePermissionsKey groupKey, _groupPermissions.keys()) {
|
||||
result += _groupPermissions[groupKey]->getID();
|
||||
}
|
||||
return result.toList();
|
||||
}
|
||||
|
||||
QStringList DomainServerSettingsManager::getDomainServerBlacklistGroupNames() {
|
||||
// All names as listed in the domain server settings; not necessarily mnetaverse groups.
|
||||
QSet<QString> result;
|
||||
foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) {
|
||||
result += _groupForbiddens[groupKey]->getID();
|
||||
}
|
||||
return result.toList();
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::debugDumpGroupsState() {
|
||||
qDebug() << "--------- GROUPS ---------";
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
|
||||
#include <HifiConfigVariantMap.h>
|
||||
#include <HTTPManager.h>
|
||||
|
||||
#include <ReceivedMessage.h>
|
||||
#include "NodePermissions.h"
|
||||
|
||||
#include <Node.h>
|
||||
#include <ReceivedMessage.h>
|
||||
|
||||
#include "DomainGatekeeper.h"
|
||||
#include "NodePermissions.h"
|
||||
|
||||
const QString SETTINGS_PATHS_KEY = "paths";
|
||||
|
||||
|
@ -105,6 +105,9 @@ public:
|
|||
QList<QUuid> getGroupIDs();
|
||||
QList<QUuid> getBlacklistGroupIDs();
|
||||
|
||||
QStringList getDomainServerGroupNames();
|
||||
QStringList getDomainServerBlacklistGroupNames();
|
||||
|
||||
// these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api
|
||||
void clearGroupMemberships(const QString& name) { _groupMembership[name.toLower()].clear(); }
|
||||
void recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID);
|
||||
|
|
|
@ -10,87 +10,74 @@ import zipfile
|
|||
|
||||
print = functools.partial(print, flush=True)
|
||||
|
||||
ANDROID_PACKAGE_URL = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
|
||||
ANDROID_PACKAGE_URL = 'https://content.vircadia.com/eu-c-1/vircadia-public/dependencies/android/'
|
||||
|
||||
ANDROID_PACKAGES = {
|
||||
'qt' : {
|
||||
'file': 'qt-5.11.1_linux_armv8-libcpp_openssl_patched.tgz',
|
||||
'versionId': '3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN',
|
||||
'checksum': 'aa449d4bfa963f3bc9a9dfe558ba29df',
|
||||
},
|
||||
'bullet': {
|
||||
'file': 'bullet-2.88_armv8-libcpp.tgz',
|
||||
'versionId': 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
|
||||
'checksum': '81642779ccb110f8c7338e8739ac38a0',
|
||||
},
|
||||
'draco': {
|
||||
'file': 'draco_armv8-libcpp.tgz',
|
||||
'versionId': '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8',
|
||||
'checksum': '617a80d213a5ec69fbfa21a1f2f738cd',
|
||||
},
|
||||
'glad': {
|
||||
'file': 'glad_armv8-libcpp.zip',
|
||||
'versionId': 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY',
|
||||
'checksum': 'a8ee8584cf1ccd34766c7ddd9d5e5449',
|
||||
},
|
||||
'gvr': {
|
||||
'file': 'gvrsdk_v1.101.0.tgz',
|
||||
'versionId': 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r',
|
||||
'checksum': '57fd02baa069176ba18597a29b6b4fc7',
|
||||
},
|
||||
'nvtt': {
|
||||
'file': 'nvtt_armv8-libcpp.zip',
|
||||
'versionId': 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO',
|
||||
'checksum': 'eb46d0b683e66987190ed124aabf8910',
|
||||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so']
|
||||
},
|
||||
'oculus_1.22': {
|
||||
'file': 'ovr_sdk_mobile_1.22.zip',
|
||||
'versionId': 'InhomR5gwkzyiLAelH3X9k4nvV3iIpA_',
|
||||
'checksum': '1ac3c5b0521e5406f287f351015daff8',
|
||||
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
|
||||
'includeLibs': ['libvrapi.so']
|
||||
},
|
||||
'oculusPlatform': {
|
||||
'file': 'OVRPlatformSDK_v1.34.0.zip',
|
||||
'versionId': 'vbRUkkyzUAXfTGSEtuiUr_7.Fm5h5BZk',
|
||||
'checksum': '16e4c5f39520f122bc49cb6d5bb88289',
|
||||
'sharedLibFolder': 'Android/libs/arm64-v8a',
|
||||
'includeLibs': ['libovrplatformloader.so']
|
||||
},
|
||||
'openssl': {
|
||||
'file': 'openssl-1.1.0g_armv8.tgz',
|
||||
'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
|
||||
'checksum': 'cabb681fbccd79594f65fcc266e02f32'
|
||||
},
|
||||
'polyvox': {
|
||||
'file': 'polyvox_armv8-libcpp.tgz',
|
||||
'versionId': 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92',
|
||||
'checksum': 'dba88b3a098747af4bb169e9eb9af57e',
|
||||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
|
||||
},
|
||||
'tbb': {
|
||||
'file': 'tbb-2018_U1_armv8_libcpp.tgz',
|
||||
'versionId': 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB',
|
||||
'checksum': '20768f298f53b195e71b414b0ae240c4',
|
||||
'sharedLibFolder': 'lib/release',
|
||||
'includeLibs': ['libtbb.so', 'libtbbmalloc.so'],
|
||||
},
|
||||
'hifiAC': {
|
||||
'baseUrl': 'http://s3.amazonaws.com/hifi-public/dependencies/',
|
||||
'baseUrl': 'https://content.vircadia.com/eu-c-1/vircadia-public/dependencies/',
|
||||
'file': 'codecSDK-android_armv8-2.0.zip',
|
||||
'checksum': '1cbef929675818fc64c4101b72f84a6a'
|
||||
},
|
||||
'etc2comp': {
|
||||
'file': 'etc2comp-patched-armv8-libcpp.tgz',
|
||||
'versionId': 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
|
||||
'checksum': '14b02795d774457a33bbc60e00a786bc'
|
||||
},
|
||||
'breakpad': {
|
||||
'file': 'breakpad.tgz',
|
||||
'versionId': '8VrYXz7oyc.QBxNia0BVJOUBvrFO61jI',
|
||||
'checksum': 'ddcb23df336b08017042ba4786db1d9e',
|
||||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': {'libbreakpad_client.a'}
|
||||
|
@ -105,14 +92,12 @@ ANDROID_PLATFORM_PACKAGES = {
|
|||
'Darwin' : {
|
||||
'qt': {
|
||||
'file': 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz',
|
||||
'versionId': 'OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup',
|
||||
'checksum': 'c83cc477c08a892e00c71764dca051a0'
|
||||
},
|
||||
},
|
||||
'Windows' : {
|
||||
'qt': {
|
||||
'file': 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz',
|
||||
'versionId': 'JfWM0P_Mz5Qp0LwpzhrsRwN3fqlLSFeT',
|
||||
'checksum': '0582191cc55431aa4f660848a542883e'
|
||||
},
|
||||
}
|
||||
|
|
|
@ -121,14 +121,14 @@ if (APPLE)
|
|||
# configure CMake to use a custom Info.plist
|
||||
set_target_properties(${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in)
|
||||
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "High Fidelity")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "Vircadia")
|
||||
if (PRODUCTION_BUILD)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface)
|
||||
else ()
|
||||
if (DEV_BUILD)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface-dev)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface-dev)
|
||||
elseif (PR_BUILD)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface-pr)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface-pr)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -386,6 +386,10 @@ Item {
|
|||
visible: root.expanded
|
||||
text: "LOD: " + root.lodStatus;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded
|
||||
text: "Entity Updates: " + root.numEntityUpdates + " / " + root.numNeededEntityUpdates;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ MessageDialog {
|
|||
objectName: "ConnectionFailureDialog"
|
||||
|
||||
title: "No Connection"
|
||||
text: "Unable to connect to this domain. Click the 'GO TO' button on the toolbar to visit another domain."
|
||||
text: "Unable to connect to this domain. Click the 'EXPLORE' button on the toolbar to visit another domain."
|
||||
buttons: OriginalDialogs.StandardButton.Ok
|
||||
icon: OriginalDialogs.StandardIcon.Warning
|
||||
defaultButton: OriginalDialogs.StandardButton.NoButton;
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
//
|
||||
// Created by David Rowe on 3 Jun 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
//
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0
|
||||
import QtQuick 2.4
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
// Created by Clement on 7/18/16
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -44,6 +45,9 @@ Item {
|
|||
property bool lostFocus: false
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
// If not logging into domain, then we must be logging into the metaverse...
|
||||
readonly property bool isLoggingInToDomain: loginDialog.getDomainLoginRequested()
|
||||
readonly property string domainLoginDomain: loginDialog.getDomainLoginDomain()
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
@ -70,7 +74,12 @@ Item {
|
|||
}
|
||||
|
||||
function login() {
|
||||
loginDialog.login(emailField.text, passwordField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
loginDialog.login(emailField.text, passwordField.text);
|
||||
} else {
|
||||
loginDialog.loginDomain(emailField.text, passwordField.text);
|
||||
}
|
||||
|
||||
if (linkAccountBody.loginDialogPoppedUp) {
|
||||
var data;
|
||||
if (linkAccountBody.linkSteam) {
|
||||
|
@ -86,7 +95,7 @@ Item {
|
|||
}
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam,
|
||||
"withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus,
|
||||
"displayName":displayNameField.text });
|
||||
"displayName":displayNameField.text, "isLoggingInToDomain": linkAccountBody.isLoggingInToDomain, "domainLoginDomain": linkAccountBody.domainLoginDomain });
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
@ -97,14 +106,22 @@ Item {
|
|||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
errorContainer.height = (loginErrorMessageTextMetrics.width / displayNameField.width) * loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
var domainLoginText = "Log In to Domain\n" + domainLoginDomain;
|
||||
loginDialogText.text = (!isLoggingInToDomain) ? "Log In to Metaverse" : domainLoginText;
|
||||
loginButton.text = (!linkAccountBody.linkSteam && !linkAccountBody.linkOculus) ? "Log In" : "Link Account";
|
||||
loginButton.text = (!isLoggingInToDomain) ? "Log In to Metaverse" : "Log In to Domain";
|
||||
loginButton.color = hifi.buttons.blue;
|
||||
displayNameField.placeholderText = "Display Name (optional)";
|
||||
var savedDisplayName = Settings.getValue("Avatar/displayName", "");
|
||||
displayNameField.text = savedDisplayName;
|
||||
emailField.placeholderText = "Username or Email";
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
|
||||
emailField.placeholderText = (!isLoggingInToDomain) ? "Username or Email" : "Username";
|
||||
if (!isLoggingInToDomain) {
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
|
||||
if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) {
|
||||
loginButton.width = (passwordField.width - hifi.dimensions.contentSpacing.x) / 2;
|
||||
loginButton.anchors.right = displayNameField.right;
|
||||
|
@ -130,7 +147,7 @@ Item {
|
|||
Item {
|
||||
id: loginContainer
|
||||
width: displayNameField.width
|
||||
height: errorContainer.height + displayNameField.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y +
|
||||
height: errorContainer.height + loginDialogTextContainer.height + displayNameField.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y +
|
||||
keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
|
@ -144,9 +161,10 @@ Item {
|
|||
width: parent.width
|
||||
height: loginErrorMessageTextMetrics.height
|
||||
anchors {
|
||||
bottom: displayNameField.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
left: displayNameField.left;
|
||||
bottom: loginDialogTextContainer.top
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
left: loginDialogTextContainer.left
|
||||
right: loginDialogTextContainer.right
|
||||
}
|
||||
TextMetrics {
|
||||
id: loginErrorMessageTextMetrics
|
||||
|
@ -159,12 +177,45 @@ Item {
|
|||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ""
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: loginDialogTextContainer
|
||||
height: 56
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
Text {
|
||||
id: loginDialogText
|
||||
text: qsTr("Log In")
|
||||
lineHeight: 1
|
||||
color: "white"
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: 24
|
||||
font.bold: linkAccountBody.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: displayNameField
|
||||
|
@ -173,8 +224,8 @@ Item {
|
|||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: errorContainer.height
|
||||
top: loginDialogTextContainer.bottom
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
placeholderText: "Display Name (optional)"
|
||||
activeFocusOnPress: true
|
||||
|
@ -192,7 +243,11 @@ Item {
|
|||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
|
@ -231,7 +286,11 @@ Item {
|
|||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
|
@ -311,7 +370,11 @@ Item {
|
|||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
|
@ -320,12 +383,13 @@ Item {
|
|||
}
|
||||
HifiControlsUit.CheckBox {
|
||||
id: keepMeLoggedInCheckbox
|
||||
checked: Settings.getValue("keepMeLoggedIn", false);
|
||||
checked: !isLoggingInToDomain ? Settings.getValue("keepMeLoggedIn", false) : false; // ####### TODO
|
||||
text: qsTr("Keep Me Logged In");
|
||||
boxSize: 18;
|
||||
labelFontFamily: linkAccountBody.fontFamily
|
||||
labelFontSize: 18;
|
||||
color: hifi.colors.white;
|
||||
visible: !isLoggingInToDomain
|
||||
anchors {
|
||||
top: passwordField.bottom;
|
||||
topMargin: hifi.dimensions.contentSpacing.y;
|
||||
|
@ -333,14 +397,22 @@ Item {
|
|||
}
|
||||
onCheckedChanged: {
|
||||
Settings.setValue("keepMeLoggedIn", checked);
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", "");
|
||||
}
|
||||
} else {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", "");
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
|
||||
if (!isLoggingInToDomain) {
|
||||
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
|
@ -392,7 +464,7 @@ Item {
|
|||
HifiStylesUit.ShortcutText {
|
||||
id: cantAccessText
|
||||
z: 10
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain
|
||||
anchors {
|
||||
top: loginButton.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
|
@ -402,7 +474,7 @@ Item {
|
|||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
|
||||
text: "<a href='metaverse.vircadia.com/users/password/new'> Can't access your account?</a>"
|
||||
text: "<a href='https://metaverse.vircadia.com/users/password/new'> Can't access your account?</a>"
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
@ -486,15 +558,16 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: signUpContainer
|
||||
width: loginContainer.width
|
||||
height: signUpTextMetrics.height
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain
|
||||
anchors {
|
||||
left: loginContainer.left
|
||||
top: loginContainer.bottom
|
||||
topMargin: 0.15 * parent.height
|
||||
topMargin: 0.05 * parent.height
|
||||
}
|
||||
TextMetrics {
|
||||
id: signUpTextMetrics
|
||||
|
@ -527,7 +600,7 @@ Item {
|
|||
leftMargin: hifi.dimensions.contentSpacing.x
|
||||
}
|
||||
|
||||
text: "<a href='metaverse.vircadia.com/users/register'>Sign Up</a>"
|
||||
text: "<a href='https://metaverse.vircadia.com/users/register'>Sign Up</a>"
|
||||
|
||||
linkColor: hifi.colors.blueAccent
|
||||
onLinkActivated: {
|
||||
|
@ -542,37 +615,54 @@ Item {
|
|||
"errorString": "" });
|
||||
}
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: dismissButtonTextMetrics
|
||||
font: loginErrorMessage.font
|
||||
text: dismissButton.text
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: dismissButton
|
||||
width: dismissButtonTextMetrics.width
|
||||
height: d.minHeightButton
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
margins: 3 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
color: hifi.buttons.noneBorderlessWhite
|
||||
text: qsTr("No thanks, take me in-world! >")
|
||||
fontCapitalization: Font.MixedCase
|
||||
fontFamily: linkAccountBody.fontFamily
|
||||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus;
|
||||
onClicked: {
|
||||
if (linkAccountBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user dismissed login screen"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
loginDialog.dismissLoginDialog();
|
||||
|
||||
Text {
|
||||
id: signUpTextSecond
|
||||
text: qsTr("or")
|
||||
anchors {
|
||||
left: signUpShortcutText.right
|
||||
leftMargin: hifi.dimensions.contentSpacing.x
|
||||
}
|
||||
lineHeight: 1
|
||||
color: "white"
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus;
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: dismissButtonTextMetrics
|
||||
font: loginErrorMessage.font
|
||||
text: dismissButton.text
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: dismissButton
|
||||
width: loginButton.width
|
||||
height: d.minHeightButton
|
||||
anchors {
|
||||
top: signUpText.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: loginButton.left
|
||||
}
|
||||
text: qsTr("Use without account, log in anonymously")
|
||||
fontCapitalization: Font.MixedCase
|
||||
fontFamily: linkAccountBody.fontFamily
|
||||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus;
|
||||
onClicked: {
|
||||
if (linkAccountBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user dismissed login screen"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
loginDialog.dismissLoginDialog();
|
||||
}
|
||||
root.tryDestroy();
|
||||
}
|
||||
root.tryDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
// Created by Wayne Chen on 10/18/18
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -31,6 +32,8 @@ Item {
|
|||
property bool linkSteam: linkSteam
|
||||
property bool linkOculus: linkOculus
|
||||
property bool createOculus: createOculus
|
||||
property bool isLoggingInToDomain: isLoggingInToDomain
|
||||
property string domainLoginDomain: domainLoginDomain
|
||||
property string displayName: ""
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
|
@ -106,6 +109,9 @@ Item {
|
|||
loggingInGlyph.visible = true;
|
||||
loggingInText.text = "Logging in to Oculus";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
} else if (loggingInBody.isLoggingInToDomain) {
|
||||
loggingInText.text = "Logging in to " + domainLoginDomain;
|
||||
loggingInText.anchors.centerIn = loggingInHeader;
|
||||
} else {
|
||||
loggingInText.text = "Logging in";
|
||||
loggingInText.anchors.centerIn = loggingInHeader;
|
||||
|
|
|
@ -437,6 +437,10 @@ Item {
|
|||
visible: root.expanded
|
||||
text: "LOD: " + root.lodStatus;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded
|
||||
text: "Entity Updates: " + root.numEntityUpdates + " / " + root.numNeededEntityUpdates;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ Item {
|
|||
if (webViewCoreUserAgent !== undefined) {
|
||||
webViewCore.profile.httpUserAgent = webViewCoreUserAgent
|
||||
} else {
|
||||
webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
|
||||
webViewCore.profile.httpUserAgent += " (VircadiaInterface)";
|
||||
}
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
|
|
|
@ -19,7 +19,7 @@ Item {
|
|||
buttons: OriginalDialogs.StandardButton.Ok,
|
||||
defaultButton: OriginalDialogs.StandardButton.NoButton,
|
||||
title: "No Connection",
|
||||
text: "Unable to connect to this domain. Click the 'GO TO' button on the toolbar to visit another domain."
|
||||
text: "Unable to connect to this domain. Click the 'EXPLORE' button on the toolbar to visit another domain."
|
||||
});
|
||||
object.selected.connect(function(button) {
|
||||
if (button === OriginalDialogs.StandardButton.Ok) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
// Created by Vlad Stelmahovsky on 03/22/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -325,178 +326,14 @@ Rectangle {
|
|||
size: 16;
|
||||
|
||||
text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to talk.") :
|
||||
qsTr("Press and hold grip triggers on both of your controllers to talk.");
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: secondSeparator;
|
||||
anchors.top: pttTextContainer.visible ? pttTextContainer.bottom : switchesContainer.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: inputDeviceHeader
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2;
|
||||
height: 36;
|
||||
anchors.top: secondSeparator.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
||||
HiFiGlyphs {
|
||||
width: margins.sizeCheckBox;
|
||||
text: hifi.glyphs.mic;
|
||||
color: hifi.colors.white;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: -size/4; //the glyph has empty space at left about 25%
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 30;
|
||||
}
|
||||
RalewayRegular {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: margins.sizeText + margins.sizeLevel;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: margins.sizeCheckBox;
|
||||
size: 22;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Choose input device");
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: inputView;
|
||||
width: rightMostInputLevelPos;
|
||||
anchors.top: inputDeviceHeader.bottom;
|
||||
anchors.topMargin: 10;
|
||||
x: margins.paddings
|
||||
interactive: false;
|
||||
height: contentHeight;
|
||||
|
||||
clip: true;
|
||||
model: AudioScriptingInterface.devices.input;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos - margins.paddings*2
|
||||
height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ?
|
||||
(margins.sizeCheckBox > checkBoxInput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxInput.implicitHeight + 4) : 0
|
||||
visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)
|
||||
AudioControls.CheckBox {
|
||||
id: checkBoxInput
|
||||
anchors.left: parent.left
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - inputLevel.width
|
||||
clip: true
|
||||
checkable: !checked
|
||||
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
|
||||
boxSize: margins.sizeCheckBox / 2
|
||||
isRound: true
|
||||
text: devicename
|
||||
fontSize: 16;
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
stereoInput.checked = false;
|
||||
AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo
|
||||
AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
AudioControls.InputPeak {
|
||||
id: inputLevel
|
||||
anchors.right: parent.right
|
||||
peak: model.peak;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR)) &&
|
||||
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioControls.LoopbackAudio {
|
||||
id: loopbackAudio
|
||||
x: margins.paddings
|
||||
anchors.top: inputView.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
||||
visible: (bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR);
|
||||
anchors { left: parent.left; leftMargin: margins.paddings }
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: thirdSeparator;
|
||||
anchors.top: loopbackAudio.visible ? loopbackAudio.bottom : inputView.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: outputDeviceHeader;
|
||||
anchors.topMargin: 10;
|
||||
anchors.top: thirdSeparator.bottom;
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2
|
||||
height: 36
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: -size/4 //the glyph has empty space at left about 25%
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: margins.sizeCheckBox
|
||||
text: hifi.glyphs.unmuted;
|
||||
color: hifi.colors.white;
|
||||
size: 36;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
width: margins.sizeText + margins.sizeLevel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: margins.sizeCheckBox
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 22;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Choose output device");
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: outputView
|
||||
width: parent.width - margins.paddings*2
|
||||
x: margins.paddings;
|
||||
interactive: false;
|
||||
height: contentHeight;
|
||||
anchors.top: outputDeviceHeader.bottom;
|
||||
anchors.topMargin: 10;
|
||||
clip: true;
|
||||
model: AudioScriptingInterface.devices.output;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos
|
||||
height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ?
|
||||
(margins.sizeCheckBox > checkBoxOutput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxOutput.implicitHeight + 4) : 0
|
||||
visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)
|
||||
|
||||
AudioControls.CheckBox {
|
||||
id: checkBoxOutput
|
||||
width: parent.width
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
boxSize: margins.sizeCheckBox / 2
|
||||
isRound: true
|
||||
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
|
||||
checkable: !checked
|
||||
text: devicename
|
||||
fontSize: 16
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
qsTr("Press and hold grip triggers on both controllers to talk.");
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: avatarGainContainer
|
||||
x: margins.paddings;
|
||||
anchors.top: outputView.bottom;
|
||||
anchors.top: pttTextContainer.bottom;
|
||||
anchors.topMargin: 10;
|
||||
width: parent.width - margins.paddings*2
|
||||
height: avatarGainSliderTextMetrics.height
|
||||
|
@ -677,12 +514,174 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
AudioControls.PlaySampleSound {
|
||||
id: playSampleSound
|
||||
x: margins.paddings
|
||||
Separator {
|
||||
id: secondSeparator;
|
||||
anchors.top: systemInjectorGainContainer.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Item {
|
||||
id: inputDeviceHeader
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2;
|
||||
height: 36;
|
||||
anchors.top: secondSeparator.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
||||
HiFiGlyphs {
|
||||
width: margins.sizeCheckBox;
|
||||
text: hifi.glyphs.mic;
|
||||
color: hifi.colors.white;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: -size/4; //the glyph has empty space at left about 25%
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 30;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: margins.sizeText + margins.sizeLevel;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: margins.sizeCheckBox;
|
||||
size: 22;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Choose input device");
|
||||
}
|
||||
}
|
||||
|
||||
AudioControls.LoopbackAudio {
|
||||
id: loopbackAudio
|
||||
x: margins.paddings
|
||||
anchors.top: inputDeviceHeader.bottom;
|
||||
anchors.topMargin: 10;
|
||||
visible: (bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR);
|
||||
anchors { left: parent.left; leftMargin: margins.paddings }
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: inputView;
|
||||
width: rightMostInputLevelPos;
|
||||
anchors.top: loopbackAudio.visible ? loopbackAudio.bottom : inputDeviceHeader.bottom;
|
||||
anchors.topMargin: 10;
|
||||
x: margins.paddings
|
||||
interactive: false;
|
||||
height: contentHeight;
|
||||
|
||||
clip: true;
|
||||
model: AudioScriptingInterface.devices.input;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos - margins.paddings*2
|
||||
height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ?
|
||||
(margins.sizeCheckBox > checkBoxInput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxInput.implicitHeight + 4) : 0
|
||||
visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)
|
||||
AudioControls.CheckBox {
|
||||
id: checkBoxInput
|
||||
anchors.left: parent.left
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - inputLevel.width
|
||||
clip: true
|
||||
checkable: !checked
|
||||
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
|
||||
boxSize: margins.sizeCheckBox / 2
|
||||
isRound: true
|
||||
text: devicename
|
||||
fontSize: 16;
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
stereoInput.checked = false;
|
||||
AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo
|
||||
AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
AudioControls.InputPeak {
|
||||
id: inputLevel
|
||||
anchors.right: parent.right
|
||||
peak: model.peak;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR)) &&
|
||||
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: thirdSeparator;
|
||||
anchors.top: inputView.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: outputDeviceHeader;
|
||||
anchors.topMargin: 10;
|
||||
anchors.top: thirdSeparator.bottom;
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2
|
||||
height: 36
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: -size/4 //the glyph has empty space at left about 25%
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: margins.sizeCheckBox
|
||||
text: hifi.glyphs.unmuted;
|
||||
color: hifi.colors.white;
|
||||
size: 36;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
width: margins.sizeText + margins.sizeLevel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: margins.sizeCheckBox
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 22;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Choose output device");
|
||||
}
|
||||
}
|
||||
|
||||
AudioControls.PlaySampleSound {
|
||||
id: playSampleSound
|
||||
x: margins.paddings
|
||||
anchors.top: outputDeviceHeader.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: outputView
|
||||
width: parent.width - margins.paddings*2
|
||||
x: margins.paddings;
|
||||
interactive: false;
|
||||
height: contentHeight + 10;
|
||||
anchors.top: playSampleSound.bottom;
|
||||
anchors.topMargin: 10;
|
||||
clip: true;
|
||||
model: AudioScriptingInterface.devices.output;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos
|
||||
height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ?
|
||||
(margins.sizeCheckBox > checkBoxOutput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxOutput.implicitHeight + 4) : 0
|
||||
visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)
|
||||
AudioControls.CheckBox {
|
||||
id: checkBoxOutput
|
||||
width: parent.width
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
boxSize: margins.sizeCheckBox / 2
|
||||
isRound: true
|
||||
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
|
||||
checkable: !checked
|
||||
text: devicename
|
||||
fontSize: 16
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -336,8 +336,8 @@ Item {
|
|||
height: parent.height
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
minimumValue: 0.25
|
||||
maximumValue: 1.0
|
||||
stepSize: 0.02
|
||||
maximumValue: 2.0
|
||||
stepSize: 0.05
|
||||
value: Render.viewportResolutionScale
|
||||
live: true
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Created by Dante Ruiz on 6/1/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -96,7 +97,8 @@ Item {
|
|||
|
||||
HifiControls.ImageMessageBox {
|
||||
id: imageMessageBox
|
||||
anchors.fill: parent
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 444
|
||||
z: 2000
|
||||
imageWidth: 442
|
||||
imageHeight: 670
|
||||
|
@ -179,7 +181,7 @@ Item {
|
|||
HifiControls.CheckBox {
|
||||
id: checkBox
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
text: "show all input devices"
|
||||
text: "Show all input devices"
|
||||
|
||||
onClicked: {
|
||||
box.model = inputPlugins();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Created by Dante Ruiz on 6/5/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -71,6 +72,7 @@ Flickable {
|
|||
|
||||
property int state: buttonState.disabled
|
||||
property var lastConfiguration: null
|
||||
property bool isConfiguring: false
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
|
@ -360,9 +362,9 @@ Flickable {
|
|||
RalewayRegular {
|
||||
id: info
|
||||
|
||||
text: "See Recommended Tracker Placement"
|
||||
text: "See Recommended Placement"
|
||||
color: hifi.colors.blueHighlight
|
||||
size: 10
|
||||
size: 12
|
||||
anchors {
|
||||
left: additional.right
|
||||
leftMargin: 10
|
||||
|
@ -415,7 +417,6 @@ Flickable {
|
|||
id: feetBox
|
||||
width: 15
|
||||
height: 15
|
||||
boxRadius: 7
|
||||
|
||||
onClicked: {
|
||||
if (!checked) {
|
||||
|
@ -446,7 +447,6 @@ Flickable {
|
|||
id: hipBox
|
||||
width: 15
|
||||
height: 15
|
||||
boxRadius: 7
|
||||
|
||||
onClicked: {
|
||||
if (checked) {
|
||||
|
@ -486,7 +486,6 @@ Flickable {
|
|||
id: chestBox
|
||||
width: 15
|
||||
height: 15
|
||||
boxRadius: 7
|
||||
|
||||
onClicked: {
|
||||
if (checked) {
|
||||
|
@ -524,7 +523,6 @@ Flickable {
|
|||
id: shoulderBox
|
||||
width: 15
|
||||
height: 15
|
||||
boxRadius: 7
|
||||
|
||||
onClicked: {
|
||||
if (checked) {
|
||||
|
@ -823,7 +821,6 @@ Flickable {
|
|||
id: viveInDesktop
|
||||
width: 15
|
||||
height: 15
|
||||
boxRadius: 7
|
||||
|
||||
anchors.top: advanceSettings.bottom
|
||||
anchors.topMargin: 5
|
||||
|
@ -840,9 +837,71 @@ Flickable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
HifiControls.CheckBox {
|
||||
id: eyeTracking
|
||||
width: 15
|
||||
height: 15
|
||||
|
||||
anchors.top: viveInDesktop.bottom
|
||||
anchors.topMargin: 5
|
||||
anchors.left: openVrConfiguration.left
|
||||
anchors.leftMargin: leftMargin + 10
|
||||
|
||||
onClicked: {
|
||||
sendConfigurationSettings();
|
||||
}
|
||||
}
|
||||
|
||||
RalewayBold {
|
||||
id: eyeTrackingLabel
|
||||
size: 12
|
||||
text: "Use eye tracking (if available)."
|
||||
color: hifi.colors.lightGrayText
|
||||
anchors {
|
||||
left: eyeTracking.right
|
||||
leftMargin: 5
|
||||
verticalCenter: eyeTracking.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: privacyPolicy
|
||||
text: "Privacy Policy"
|
||||
color: hifi.colors.blueHighlight
|
||||
size: 12
|
||||
anchors {
|
||||
left: eyeTrackingLabel.right
|
||||
leftMargin: 10
|
||||
verticalCenter: eyeTrackingLabel.verticalCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: privacyPolicyUnderline
|
||||
color: hifi.colors.blueHighlight
|
||||
width: privacyPolicy.width
|
||||
height: 1
|
||||
anchors {
|
||||
top: privacyPolicy.bottom
|
||||
topMargin: 1
|
||||
left: privacyPolicy.left
|
||||
}
|
||||
visible: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: true
|
||||
onEntered: privacyPolicyUnderline.visible = true;
|
||||
onExited: privacyPolicyUnderline.visible = false;
|
||||
onClicked: HiFiAbout.openUrl("https://vircadia.com/privacy-policy");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Row {
|
||||
id: outOfRangeDataStrategyRow
|
||||
anchors.top: viveInDesktop.bottom
|
||||
anchors.top: eyeTracking.bottom
|
||||
anchors.topMargin: 5
|
||||
anchors.left: openVrConfiguration.left
|
||||
anchors.leftMargin: leftMargin + 10
|
||||
|
@ -966,6 +1025,8 @@ Flickable {
|
|||
}
|
||||
|
||||
function displayConfiguration() {
|
||||
isConfiguring = true;
|
||||
|
||||
var settings = InputConfiguration.configurationSettings(openVrConfiguration.pluginName);
|
||||
var configurationType = settings["trackerConfiguration"];
|
||||
displayTrackerConfiguration(configurationType);
|
||||
|
@ -982,6 +1043,7 @@ Flickable {
|
|||
var viveController = settings["handController"];
|
||||
var desktopMode = settings["desktopMode"];
|
||||
var hmdDesktopPosition = settings["hmdDesktopTracking"];
|
||||
var eyeTrackingEnabled = settings["eyeTrackingEnabled"];
|
||||
|
||||
armCircumference.realValue = settings.armCircumference;
|
||||
shoulderWidth.realValue = settings.shoulderWidth;
|
||||
|
@ -1004,6 +1066,7 @@ Flickable {
|
|||
|
||||
viveInDesktop.checked = desktopMode;
|
||||
hmdInDesktop.checked = hmdDesktopPosition;
|
||||
eyeTracking.checked = eyeTrackingEnabled;
|
||||
outOfRangeDataStrategyComboBox.currentIndex = outOfRangeDataStrategyComboBox.model.indexOf(settings.outOfRangeDataStrategy);
|
||||
|
||||
initializeButtonState();
|
||||
|
@ -1014,6 +1077,8 @@ Flickable {
|
|||
};
|
||||
|
||||
UserActivityLogger.logAction("mocap_ui_open_dialog", data);
|
||||
|
||||
isConfiguring = false;
|
||||
}
|
||||
|
||||
function displayTrackerConfiguration(type) {
|
||||
|
@ -1170,6 +1235,7 @@ Flickable {
|
|||
"shoulderWidth": shoulderWidth.realValue,
|
||||
"desktopMode": viveInDesktop.checked,
|
||||
"hmdDesktopTracking": hmdInDesktop.checked,
|
||||
"eyeTrackingEnabled": eyeTracking.checked,
|
||||
"outOfRangeDataStrategy": outOfRangeDataStrategyComboBox.model[outOfRangeDataStrategyComboBox.currentIndex]
|
||||
}
|
||||
|
||||
|
@ -1177,6 +1243,10 @@ Flickable {
|
|||
}
|
||||
|
||||
function sendConfigurationSettings() {
|
||||
if (isConfiguring) {
|
||||
// Ignore control value changes during dialog initialization.
|
||||
return;
|
||||
}
|
||||
var settings = composeConfigurationSettings();
|
||||
InputConfiguration.setConfigurationSettings(settings, openVrConfiguration.pluginName);
|
||||
updateCalibrationButton();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Andrzej Kapolka on 5/10/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -65,6 +66,7 @@
|
|||
#include <Trace.h>
|
||||
#include <ResourceScriptingInterface.h>
|
||||
#include <AccountManager.h>
|
||||
#include <DomainAccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <AnimDebugDraw.h>
|
||||
#include <BuildInfo.h>
|
||||
|
@ -852,6 +854,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
#else
|
||||
DependencyManager::set<AccountManager>(true, std::bind(&Application::getUserAgent, qApp));
|
||||
#endif
|
||||
DependencyManager::set<DomainAccountManager>();
|
||||
DependencyManager::set<StatTracker>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT, defaultScriptsOverrideOption);
|
||||
DependencyManager::set<Preferences>();
|
||||
|
@ -1348,6 +1351,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
#endif
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
|
||||
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, dialogsManager.data(),
|
||||
&DialogsManager::showDomainLoginDialog);
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this,
|
||||
&Application::updateWindowTitle);
|
||||
// ####### TODO: Connect any other signals from domainAccountManager.
|
||||
|
||||
// use our MyAvatar position and quat for address manager path
|
||||
addressManager->setPositionGetter([] {
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
@ -1571,7 +1581,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// Do not show login dialog if requested not to on the command line
|
||||
QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY);
|
||||
int index = arguments().indexOf(hifiNoLoginCommandLineKey);
|
||||
if (index != -1) {
|
||||
if (index != -1 || _disableLoginScreen) {
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
return;
|
||||
}
|
||||
|
@ -2614,7 +2624,7 @@ QString Application::getUserAgent() {
|
|||
return userAgent;
|
||||
}
|
||||
|
||||
QString userAgent = "Mozilla/5.0 (HighFidelityInterface/" + BuildInfo::VERSION + "; "
|
||||
QString userAgent = NetworkingConstants::VIRCADIA_USER_AGENT + "/" + BuildInfo::VERSION + "; "
|
||||
+ QSysInfo::productType() + " " + QSysInfo::productVersion() + ")";
|
||||
|
||||
auto formatPluginName = [](QString name) -> QString { return name.trimmed().replace(" ", "-"); };
|
||||
|
@ -2801,6 +2811,7 @@ void Application::cleanupBeforeQuit() {
|
|||
if (!keepMeLoggedIn) {
|
||||
DependencyManager::get<AccountManager>()->removeAccountFromFile();
|
||||
}
|
||||
// ####### TODO
|
||||
|
||||
_displayPlugin.reset();
|
||||
PluginManager::getInstance()->shutdown();
|
||||
|
@ -3150,6 +3161,7 @@ extern void setupPreferences();
|
|||
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false);
|
||||
#endif
|
||||
|
||||
// ####### TODO
|
||||
void Application::showLoginScreen() {
|
||||
#if !defined(DISABLE_QML)
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -3857,6 +3869,11 @@ void Application::showHelp() {
|
|||
//InfoView::show(INFO_HELP_PATH, false, queryString.toString());
|
||||
}
|
||||
|
||||
void Application::gotoTutorial() {
|
||||
const QString TUTORIAL_ADDRESS = "file:///~/serverless/tutorial.json";
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(TUTORIAL_ADDRESS);
|
||||
}
|
||||
|
||||
void Application::resizeEvent(QResizeEvent* event) {
|
||||
resizeGL();
|
||||
}
|
||||
|
@ -3934,12 +3951,15 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers;
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
|
||||
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
|
||||
QString addressLookupString;
|
||||
if (urlIndex != -1) {
|
||||
QUrl url(arguments().value(urlIndex + 1));
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption urlOption("url", "", "value");
|
||||
parser.addOption(urlOption);
|
||||
parser.parse(arguments());
|
||||
if (parser.isSet(urlOption)) {
|
||||
QUrl url = QUrl(parser.value(urlOption));
|
||||
if (url.scheme() == URL_SCHEME_HIFIAPP) {
|
||||
Setting::Handle<QVariant>("startUpApp").set(url.path());
|
||||
} else {
|
||||
|
@ -4065,7 +4085,7 @@ std::map<QString, QString> Application::prepareServerlessDomainContents(QUrl dom
|
|||
bool success = tmpTree->readFromByteArray(domainURL.toString(), data);
|
||||
if (success) {
|
||||
tmpTree->reaverageOctreeElements();
|
||||
tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0);
|
||||
tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), "domain", 0, 0, 0);
|
||||
}
|
||||
std::map<QString, QString> namedPaths = tmpTree->getNamedPaths();
|
||||
|
||||
|
@ -5537,7 +5557,7 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse
|
|||
|
||||
// FIXME: readFromURL() can take over the main event loop which may cause problems, especially if downloading the JSON
|
||||
// from the Web.
|
||||
success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId);
|
||||
success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId, true);
|
||||
if (success) {
|
||||
_entityClipboard->reaverageOctreeElements();
|
||||
}
|
||||
|
@ -5545,8 +5565,8 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse
|
|||
return success;
|
||||
}
|
||||
|
||||
QVector<EntityItemID> Application::pasteEntities(float x, float y, float z) {
|
||||
return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z);
|
||||
QVector<EntityItemID> Application::pasteEntities(const QString& entityHostType, float x, float y, float z) {
|
||||
return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), entityHostType, x, y, z);
|
||||
}
|
||||
|
||||
void Application::init() {
|
||||
|
@ -7061,19 +7081,23 @@ void Application::updateWindowTitle() const {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
|
||||
bool isMetaverseLoggedIn = accountManager->isLoggedIn();
|
||||
bool isDomainLoggedIn = domainAccountManager->isLoggedIn();
|
||||
QString authedDomain = domainAccountManager->getAuthedDomain();
|
||||
|
||||
QString buildVersion = " - Vircadia - "
|
||||
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
|
||||
+ " " + applicationVersion();
|
||||
|
||||
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
|
||||
|
||||
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
|
||||
nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||
QString username = accountManager->getAccountInfo().getUsername();
|
||||
|
||||
setCrashAnnotation("sentry[user][username]", username.toStdString());
|
||||
QString metaverseUsername = accountManager->getAccountInfo().getUsername();
|
||||
QString domainUsername = domainAccountManager->getUsername();
|
||||
|
||||
setCrashAnnotation("sentry[user][username]", metaverseUsername.toStdString());
|
||||
|
||||
QString currentPlaceName;
|
||||
if (isServerlessMode()) {
|
||||
|
@ -7089,8 +7113,22 @@ void Application::updateWindowTitle() const {
|
|||
}
|
||||
}
|
||||
|
||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||
+ currentPlaceName + connectionStatus + loginStatus + buildVersion;
|
||||
QString metaverseDetails;
|
||||
if (isMetaverseLoggedIn) {
|
||||
metaverseDetails = "Metaverse: Logged in as " + metaverseUsername;
|
||||
} else {
|
||||
metaverseDetails = "Metaverse: Not Logged In";
|
||||
}
|
||||
|
||||
QString domainDetails;
|
||||
if (currentPlaceName == authedDomain && isDomainLoggedIn) {
|
||||
domainDetails = "Domain: Logged in as " + domainUsername;
|
||||
} else {
|
||||
domainDetails = "Domain: Not Logged In";
|
||||
}
|
||||
|
||||
QString title = QString() + currentPlaceName + connectionStatus + " (" + metaverseDetails + ") (" + domainDetails + ")"
|
||||
+ buildVersion;
|
||||
|
||||
#ifndef WIN32
|
||||
// crashes with vs2013/win32
|
||||
|
@ -7650,7 +7688,7 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
int requestNumber = ++_avatarAttachmentRequest;
|
||||
connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() {
|
||||
|
|
|
@ -375,7 +375,7 @@ signals:
|
|||
void awayStateWhenFocusLostInVRChanged(bool enabled);
|
||||
|
||||
public slots:
|
||||
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
||||
QVector<EntityItemID> pasteEntities(const QString& entityHostType, float x, float y, float z);
|
||||
bool exportEntities(const QString& filename, const QVector<QUuid>& entityIDs, const glm::vec3* givenOffset = nullptr);
|
||||
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||
bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1);
|
||||
|
@ -425,6 +425,7 @@ public slots:
|
|||
#endif
|
||||
|
||||
static void showHelp();
|
||||
static void gotoTutorial();
|
||||
|
||||
void cycleCamera();
|
||||
void cameraModeChanged();
|
||||
|
@ -732,6 +733,7 @@ private:
|
|||
GraphicsEngine _graphicsEngine;
|
||||
void updateRenderArgs(float deltaTime);
|
||||
|
||||
bool _disableLoginScreen { true };
|
||||
|
||||
Overlays _overlays;
|
||||
ApplicationOverlay _applicationOverlay;
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
#include "Application.h"
|
||||
#include "ui/DialogsManager.h"
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <DomainHandler.h>
|
||||
#include <DomainAccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
|
@ -34,6 +36,10 @@ void ConnectionMonitor::init() {
|
|||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer);
|
||||
connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState);
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
connect(accountManager.data(), &AccountManager::loginComplete, this, &ConnectionMonitor::startTimer);
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, &ConnectionMonitor::startTimer);
|
||||
|
||||
_timer.setSingleShot(true);
|
||||
if (!domainHandler.isConnected()) {
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// For happ(ier) development of QML, use these two things:
|
||||
// This forces QML files to be pulled from the source as you edit it: set environment variable HIFI_USE_SOURCE_TREE_RESOURCES=1
|
||||
// Use this to live reload: DependencyManager::get<OffscreenUi>()->clearCache();
|
||||
|
||||
#include "Menu.h"
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
|
@ -84,6 +88,13 @@ Menu::Menu() {
|
|||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
}
|
||||
|
||||
auto domainLogin = addActionToQMenuAndActionHash(fileMenu, "Domain: Log In");
|
||||
connect(domainLogin, &QAction::triggered, [] {
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->setDomainLoginState();
|
||||
dialogsManager->showDomainLoginDialog();
|
||||
});
|
||||
|
||||
// File > Quit
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole);
|
||||
|
||||
|
@ -821,6 +832,8 @@ Menu::Menu() {
|
|||
|
||||
addActionToQMenuAndActionHash(helpMenu, "Controls Reference", 0, qApp, SLOT(showHelp()));
|
||||
|
||||
addActionToQMenuAndActionHash(helpMenu, "Tutorial", 0, qApp, SLOT(gotoTutorial()));
|
||||
|
||||
helpMenu->addSeparator();
|
||||
|
||||
// Help > Release Notes
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "ModelPropertiesDialog.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
static const int MAX_TEXTURE_SIZE = 1024;
|
||||
static const int MAX_TEXTURE_SIZE = 8192;
|
||||
|
||||
void copyDirectoryContent(QDir& from, QDir& to) {
|
||||
for (auto entry : from.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot |
|
||||
|
|
|
@ -27,7 +27,7 @@ void PerformanceManager::setupPerformancePresetSettings(bool evaluatePlatformTie
|
|||
// If evaluatePlatformTier, evalute the Platform Tier and assign the matching Performance profile by default.
|
||||
// A bunch of Performance, Simulation and Render settings will be set to a matching default value from this
|
||||
|
||||
// Here is the mapping between pelatformTIer and performance profile
|
||||
// Here is the mapping between platformTier and performance profile
|
||||
const std::array<PerformanceManager::PerformancePreset, platform::Profiler::NumTiers> platformToPerformancePresetMap = { {
|
||||
PerformanceManager::PerformancePreset::MID, // platform::Profiler::UNKNOWN
|
||||
PerformanceManager::PerformancePreset::LOW, // platform::Profiler::LOW
|
||||
|
|
|
@ -722,7 +722,7 @@ public:
|
|||
* @function MyAvatar.restoreHandAnimation
|
||||
* @param isLeft {boolean} Set to true if using the left hand
|
||||
* @example <caption> Override left hand animation for three seconds. </caption>
|
||||
* var ANIM_URL = "https://apidocs.projectathena.dev/models/ClapHands_Standing.fbx";
|
||||
* var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx";
|
||||
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreHandAnimation();
|
||||
|
@ -780,7 +780,7 @@ public:
|
|||
* hanging at its sides when it is not moving, the avatar will stand and clap its hands. Note that just as it did before, as soon as the avatar
|
||||
* starts to move, the animation will smoothly blend into the walk animation used by the "walkFwd" animation role.</caption>
|
||||
* // An animation of the avatar clapping its hands while standing. Restore default after 30s.
|
||||
* var ANIM_URL = "https://apidocs.projectathena.dev/models/ClapHands_Standing.fbx";
|
||||
* var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx";
|
||||
* MyAvatar.overrideRoleAnimation("idleStand", ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreRoleAnimation();
|
||||
|
|
|
@ -407,13 +407,13 @@ void MyCharacterController::clearDetailedMotionStates() {
|
|||
}
|
||||
|
||||
void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
_detailedMotionStates[i]->forceActive();
|
||||
for (auto& detailedMotionState : _detailedMotionStates) {
|
||||
detailedMotionState->forceActive();
|
||||
}
|
||||
if (_pendingFlags & PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION) {
|
||||
_pendingFlags &= ~PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
transaction.objectsToRemove.push_back(_detailedMotionStates[i]);
|
||||
for (auto& detailedMotionState : _detailedMotionStates) {
|
||||
transaction.objectsToRemove.push_back(detailedMotionState);
|
||||
}
|
||||
// NOTE: the DetailedMotionStates are deleted after being added to PhysicsEngine::Transaction::_objectsToRemove
|
||||
// See AvatarManager::handleProcessedPhysicsTransaction()
|
||||
|
|
|
@ -116,10 +116,10 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const {
|
|||
|
||||
int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||
int32_t bytesRead = Avatar::parseDataFromBuffer(buffer);
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
for (auto& detailedMotionState : _detailedMotionStates) {
|
||||
// NOTE: we activate _detailedMotionStates is because they are KINEMATIC
|
||||
// and Bullet will automagically call DetailedMotionState::getWorldTransform() when active.
|
||||
_detailedMotionStates[i]->forceActive();
|
||||
detailedMotionState->forceActive();
|
||||
}
|
||||
if (_moving && _motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
|
||||
|
|
|
@ -60,10 +60,11 @@ bool ClipboardScriptingInterface::importEntities(
|
|||
return retVal;
|
||||
}
|
||||
|
||||
QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position) {
|
||||
QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position, const QString& entityHostType) {
|
||||
QVector<EntityItemID> retVal;
|
||||
BLOCKING_INVOKE_METHOD(qApp, "pasteEntities",
|
||||
Q_RETURN_ARG(QVector<EntityItemID>, retVal),
|
||||
Q_ARG(const QString&, entityHostType),
|
||||
Q_ARG(float, position.x),
|
||||
Q_ARG(float, position.y),
|
||||
Q_ARG(float, position.z));
|
||||
|
|
|
@ -117,10 +117,11 @@ public:
|
|||
* Pastes the contents of the clipboard into the domain.
|
||||
* @function Clipboard.pasteEntities
|
||||
* @param {Vec3} position - The position to paste the clipboard contents at.
|
||||
* @param {Entities.EntityHostType} [entityHostType="domain"] - The type of entities to create.
|
||||
* @returns {Uuid[]} The IDs of the new entities that were created as a result of the paste operation. If entities couldn't
|
||||
* be created then an empty array is returned.
|
||||
*/
|
||||
Q_INVOKABLE QVector<EntityItemID> pasteEntities(glm::vec3 position);
|
||||
Q_INVOKABLE QVector<EntityItemID> pasteEntities(glm::vec3 position, const QString& entityHostType = "domain");
|
||||
};
|
||||
|
||||
#endif // hifi_ClipboardScriptingInterface_h
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "Menu.h"
|
||||
#include "OffscreenUi.h"
|
||||
#include "commerce/QmlCommerce.h"
|
||||
#include "NetworkingConstants.h"
|
||||
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";
|
||||
|
@ -411,6 +412,10 @@ QString WindowScriptingInterface::checkVersion() {
|
|||
return QCoreApplication::applicationVersion();
|
||||
}
|
||||
|
||||
QString WindowScriptingInterface::getUserAgent() {
|
||||
return NetworkingConstants::VIRCADIA_USER_AGENT;
|
||||
}
|
||||
|
||||
QString WindowScriptingInterface::protocolSignature() {
|
||||
return protocolVersionsSignatureBase64();
|
||||
}
|
||||
|
|
|
@ -295,6 +295,13 @@ public slots:
|
|||
*/
|
||||
QString checkVersion();
|
||||
|
||||
/**jsdoc
|
||||
* Gets Interface's user agent.
|
||||
* @function Window.getUserAgent
|
||||
* @returns {string} Interface's user agent.
|
||||
*/
|
||||
QString getUserAgent();
|
||||
|
||||
/**jsdoc
|
||||
* Gets the signature for Interface's protocol version.
|
||||
* @function Window.protocolSignature
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Clement on 1/18/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -109,11 +110,34 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) {
|
|||
}
|
||||
}
|
||||
|
||||
void DialogsManager::setMetaverseLoginState() {
|
||||
// We're only turning off the domain login trigger but the actual domain auth URL is still saved.
|
||||
// So we can continue the domain login if desired.
|
||||
_isDomainLogin = false;
|
||||
}
|
||||
|
||||
void DialogsManager::setDomainLoginState() {
|
||||
_isDomainLogin = true;
|
||||
}
|
||||
|
||||
void DialogsManager::setDomainLogin(bool isDomainLogin, const QString& domain) {
|
||||
_isDomainLogin = isDomainLogin;
|
||||
if (!domain.isEmpty()) {
|
||||
_domainLoginDomain = domain;
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsManager::toggleLoginDialog() {
|
||||
setDomainLogin(false);
|
||||
LoginDialog::toggleAction();
|
||||
}
|
||||
|
||||
void DialogsManager::showLoginDialog() {
|
||||
|
||||
// ####### TODO: May be called from script via DialogsManagerScriptingInterface. Need to handle the case that it's already
|
||||
// displayed and may be the domain login version.
|
||||
|
||||
setDomainLogin(false);
|
||||
LoginDialog::showWithSelection();
|
||||
}
|
||||
|
||||
|
@ -121,10 +145,22 @@ void DialogsManager::hideLoginDialog() {
|
|||
LoginDialog::hide();
|
||||
}
|
||||
|
||||
|
||||
void DialogsManager::showDomainLoginDialog(const QString& domain) {
|
||||
setDomainLogin(true, domain);
|
||||
LoginDialog::showWithSelection();
|
||||
}
|
||||
|
||||
// #######: TODO: Domain version of toggleLoginDialog()?
|
||||
|
||||
// #######: TODO: Domain version of hideLoginDialog()?
|
||||
|
||||
|
||||
void DialogsManager::showUpdateDialog() {
|
||||
UpdateDialog::show();
|
||||
}
|
||||
|
||||
|
||||
void DialogsManager::octreeStatsDetails() {
|
||||
if (!_octreeStatsDialog) {
|
||||
_octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats());
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Clement on 1/18/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -40,6 +41,10 @@ public:
|
|||
QPointer<TestingDialog> getTestingDialog() const { return _testingDialog; }
|
||||
void emitAddressBarShown(bool visible) { emit addressBarShown(visible); }
|
||||
void setAddressBarVisible(bool addressBarVisible);
|
||||
void setMetaverseLoginState();
|
||||
void setDomainLoginState();
|
||||
bool getIsDomainLogin() { return _isDomainLogin; }
|
||||
QString getDomainLoginDomain() { return _domainLoginDomain; }
|
||||
|
||||
public slots:
|
||||
void showAddressBar();
|
||||
|
@ -49,6 +54,7 @@ public slots:
|
|||
void toggleLoginDialog();
|
||||
void showLoginDialog();
|
||||
void hideLoginDialog();
|
||||
void showDomainLoginDialog(const QString& domain = "");
|
||||
void octreeStatsDetails();
|
||||
void lodTools();
|
||||
void hmdTools(bool showTools);
|
||||
|
@ -82,6 +88,10 @@ private:
|
|||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
bool _dialogCreatedWhileShown { false };
|
||||
bool _addressBarVisible { false };
|
||||
|
||||
void setDomainLogin(bool isDomainLogin, const QString& domain = "");
|
||||
bool _isDomainLogin { false };
|
||||
QString _domainLoginDomain;
|
||||
};
|
||||
|
||||
#endif // hifi_DialogsManager_h
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/04/14
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -24,7 +25,9 @@
|
|||
#include <UserActivityLogger.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "DomainAccountManager.h"
|
||||
#include "DependencyManager.h"
|
||||
#include "DialogsManager.h"
|
||||
#include "Menu.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -38,12 +41,17 @@ const QUrl LOGIN_DIALOG = PathUtils::qmlUrl("OverlayLoginDialog.qml");
|
|||
|
||||
LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
// the login hasn't been dismissed yet if the user isn't logged in and is encouraged to login.
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
connect(accountManager.data(), &AccountManager::loginComplete,
|
||||
this, &LoginDialog::handleLoginCompleted);
|
||||
connect(accountManager.data(), &AccountManager::loginFailed,
|
||||
this, &LoginDialog::handleLoginFailed);
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete,
|
||||
this, &LoginDialog::handleLoginCompleted);
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginFailed,
|
||||
this, &LoginDialog::handleLoginFailed);
|
||||
connect(qApp, &Application::loginDialogFocusEnabled, this, &LoginDialog::focusEnabled);
|
||||
connect(qApp, &Application::loginDialogFocusDisabled, this, &LoginDialog::focusDisabled);
|
||||
connect(this, SIGNAL(dismissedLoginDialog()), qApp, SLOT(onDismissedLoginDialog()));
|
||||
|
@ -91,14 +99,16 @@ void LoginDialog::toggleAction() {
|
|||
|
||||
if (accountManager->isLoggedIn()) {
|
||||
// change the menu item to logout
|
||||
loginAction->setText("Logout " + accountManager->getAccountInfo().getUsername());
|
||||
loginAction->setText("Metaverse: Logout " + accountManager->getAccountInfo().getUsername());
|
||||
connection = connect(loginAction, &QAction::triggered, accountManager.data(), &AccountManager::logout);
|
||||
} else {
|
||||
// change the menu item to login
|
||||
loginAction->setText("Log In / Sign Up");
|
||||
loginAction->setText("Metaverse: Log In / Sign Up");
|
||||
connection = connect(loginAction, &QAction::triggered, [] {
|
||||
// if not in login state, show.
|
||||
if (!qApp->getLoginDialogPoppedUp()) {
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->setMetaverseLoginState();
|
||||
LoginDialog::showWithSelection();
|
||||
}
|
||||
});
|
||||
|
@ -131,10 +141,15 @@ void LoginDialog::dismissLoginDialog() {
|
|||
}
|
||||
|
||||
void LoginDialog::login(const QString& username, const QString& password) const {
|
||||
qDebug() << "Attempting to login " << username;
|
||||
qDebug() << "Attempting to login" << username;
|
||||
DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
|
||||
}
|
||||
|
||||
void LoginDialog::loginDomain(const QString& username, const QString& password) const {
|
||||
qDebug() << "Attempting to login" << username << "into a domain";
|
||||
DependencyManager::get<DomainAccountManager>()->requestAccessToken(username, password);
|
||||
}
|
||||
|
||||
void LoginDialog::loginThroughOculus() {
|
||||
qDebug() << "Attempting to login through Oculus";
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
|
@ -410,3 +425,11 @@ void LoginDialog::signupFailed(QNetworkReply* reply) {
|
|||
emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
bool LoginDialog::getDomainLoginRequested() const {
|
||||
return DependencyManager::get<DialogsManager>()->getIsDomainLogin();
|
||||
}
|
||||
|
||||
QString LoginDialog::getDomainLoginDomain() const {
|
||||
return DependencyManager::get<DialogsManager>()->getDomainLoginDomain();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/04/14
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -71,6 +72,7 @@ protected slots:
|
|||
Q_INVOKABLE QString oculusUserID() const;
|
||||
|
||||
Q_INVOKABLE void login(const QString& username, const QString& password) const;
|
||||
Q_INVOKABLE void loginDomain(const QString& username, const QString& password) const;
|
||||
Q_INVOKABLE void loginThroughSteam();
|
||||
Q_INVOKABLE void linkSteam();
|
||||
Q_INVOKABLE void createAccountFromSteam(QString username = QString());
|
||||
|
@ -81,6 +83,10 @@ protected slots:
|
|||
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
|
||||
|
||||
Q_INVOKABLE bool getLoginDialogPoppedUp() const;
|
||||
|
||||
Q_INVOKABLE bool getDomainLoginRequested() const;
|
||||
Q_INVOKABLE QString getDomainLoginDomain() const;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LoginDialog_h
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <ThreadHelpers.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" };
|
||||
|
@ -225,7 +226,7 @@ void ModelHandler::update() {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.head(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
}
|
||||
|
@ -278,7 +279,7 @@ void ModelHandler::queryNewFiles(QString marker) {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
|
||||
|
|
|
@ -458,6 +458,8 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
|
||||
// LOD Details
|
||||
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
||||
STAT_UPDATE(numEntityUpdates, DependencyManager::get<EntityTreeRenderer>()->getPrevNumEntityUpdates());
|
||||
STAT_UPDATE(numNeededEntityUpdates, DependencyManager::get<EntityTreeRenderer>()->getPrevTotalNeededEntityUpdates());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -248,6 +248,10 @@ private: \
|
|||
* <em>Read-only.</em>
|
||||
* @property {string} lodStatus - Description of the current LOD.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} numEntityUpdates - The number of entity updates that happened last frame.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} numNeededEntityUpdates - The total number of entity updates scheduled for last frame.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} timingStats - Details of the average time (ms) spent in and number of calls made to different parts of
|
||||
* the code. Provided only if <code>timingExpanded</code> is <code>true</code>. Only the top 10 items are provided if
|
||||
* Developer > Timing > Performance Timer > Only Display Top 10 is enabled.
|
||||
|
@ -543,6 +547,8 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, lodAngle, 0)
|
||||
STATS_PROPERTY(int, lodTargetFramerate, 0)
|
||||
STATS_PROPERTY(QString, lodStatus, QString())
|
||||
STATS_PROPERTY(int, numEntityUpdates, 0)
|
||||
STATS_PROPERTY(int, numNeededEntityUpdates, 0)
|
||||
STATS_PROPERTY(QString, timingStats, QString())
|
||||
STATS_PROPERTY(QString, gameUpdateStats, QString())
|
||||
STATS_PROPERTY(int, serverElements, 0)
|
||||
|
@ -1211,6 +1217,20 @@ signals:
|
|||
*/
|
||||
void lodStatusChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>numEntityUpdates</code> property changes.
|
||||
* @function Stats.numEntityUpdatesChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void numEntityUpdatesChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>numNeededEntityUpdates</code> property changes.
|
||||
* @function Stats.numNeededEntityUpdatesChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void numNeededEntityUpdatesChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>timingStats</code> property changes.
|
||||
* @function Stats.timingStatsChanged
|
||||
|
|
|
@ -143,13 +143,13 @@ set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR})
|
|||
include(CPackComponent)
|
||||
|
||||
set(CPACK_PACKAGE_NAME "HQ Launcher")
|
||||
set(CPACK_PACKAGE_VENDOR "High Fidelity")
|
||||
set(CPACK_PACKAGE_VENDOR "Vircadia")
|
||||
set(CPACK_PACKAGE_VERSION ${BUILD_VERSION})
|
||||
set(CPACK_PACKAGE_FILE_NAME "HQ Launcher")
|
||||
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
|
||||
set(DMG_SUBFOLDER_NAME "High Fidelity")
|
||||
set(DMG_SUBFOLDER_NAME "Vircadia")
|
||||
set(ESCAPED_DMG_SUBFOLDER_NAME "")
|
||||
set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc")
|
||||
|
||||
|
|
|
@ -276,12 +276,12 @@ if (APPLE)
|
|||
include(CPackComponent)
|
||||
|
||||
set(CPACK_PACKAGE_NAME "HQ Launcher")
|
||||
set(CPACK_PACKAGE_VENDOR "High Fidelity")
|
||||
set(CPACK_PACKAGE_VENDOR "Vircadia")
|
||||
set(CPACK_PACKAGE_FILE_NAME "HQ Launcher")
|
||||
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
|
||||
set(DMG_SUBFOLDER_NAME "High Fidelity")
|
||||
set(DMG_SUBFOLDER_NAME "Vircadia")
|
||||
set(ESCAPED_DMG_SUBFOLDER_NAME "")
|
||||
set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc")
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import QtQuick 2.3
|
|||
import QtQuick 2.1
|
||||
|
||||
Text {
|
||||
text: "High Fidelity"
|
||||
text: "Vircadia"
|
||||
font.bold: true
|
||||
font.family: "Graphik Semibold"
|
||||
font.pixelSize: 17
|
||||
|
|
|
@ -21,7 +21,7 @@ Launcher::Launcher(int& argc, char**argv) : QGuiApplication(argc, argv) {
|
|||
_launcherWindow->rootContext()->setContextProperty("LauncherState", _launcherState.get());
|
||||
_launcherWindow->rootContext()->setContextProperty("PathUtils", new PathUtils());
|
||||
_launcherWindow->rootContext()->setContextProperty("Platform", platform);
|
||||
_launcherWindow->setTitle("High Fidelity");
|
||||
_launcherWindow->setTitle("Vircadia");
|
||||
_launcherWindow->setFlags(Qt::FramelessWindowHint | Qt::Window);
|
||||
_launcherWindow->setLauncherStatePtr(_launcherState);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <QStandardPaths>
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QTime>
|
||||
|
||||
|
@ -253,7 +253,7 @@ void LauncherInstaller::createApplicationRegistryKeys() {
|
|||
success = insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath);
|
||||
success = insertRegistryKey(REGISTRY_PATH, "DisplayVersion", std::string(LAUNCHER_BUILD_VERSION));
|
||||
success = insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe);
|
||||
success = insertRegistryKey(REGISTRY_PATH, "Publisher", "High Fidelity");
|
||||
success = insertRegistryKey(REGISTRY_PATH, "Publisher", "Vircadia");
|
||||
|
||||
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ bool hasSuffix(const std::string& path, const std::string& suffix) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QCoreApplication::setOrganizationName("High Fidelity");
|
||||
QCoreApplication::setOrganizationName("Vircadia");
|
||||
QCoreApplication::setApplicationName("HQ Launcher");
|
||||
|
||||
Q_INIT_RESOURCE(resources);
|
||||
|
|
|
@ -310,7 +310,7 @@ BOOL LauncherManager::getAndCreatePaths(PathType type, CString& outPath) {
|
|||
outPath += DIRECTORY_NAME_INTERFACE;
|
||||
} else if (type == PathType::Content_Directory) {
|
||||
outPath += DIRECTORY_NAME_CONTENT;
|
||||
}
|
||||
}
|
||||
return (CreateDirectory(outPath, NULL) || ERROR_ALREADY_EXISTS == GetLastError());
|
||||
}
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ BOOL LauncherManager::createConfigJSON() {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain,
|
||||
LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn, CString& organizationBuildTag) {
|
||||
CString configPath;
|
||||
getAndCreatePaths(PathType::Interface_Directory, configPath);
|
||||
|
@ -388,7 +388,7 @@ LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, C
|
|||
}
|
||||
Json::Value config;
|
||||
configFile >> config;
|
||||
if (config["version"].isString() &&
|
||||
if (config["version"].isString() &&
|
||||
config["domain"].isString() &&
|
||||
config["content"].isString()) {
|
||||
loggedIn = config["loggedIn"].asBool();
|
||||
|
@ -446,7 +446,7 @@ LauncherUtils::ResponseError LauncherManager::readOrganizationJSON(const CString
|
|||
CString url = _T("/organizations/") + hash + _T(".json");
|
||||
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(),
|
||||
true, L"orgs.highfidelity.com", url,
|
||||
contentTypeJson, CStringA(),
|
||||
contentTypeJson, CStringA(),
|
||||
response, false);
|
||||
if (error != LauncherUtils::ResponseError::NoError) {
|
||||
return error;
|
||||
|
@ -557,7 +557,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
addToLog(_T("Already running most recent build. Launching interface.exe"));
|
||||
} else {
|
||||
addToLog(_T("Updating the launcher was not allowed --noUpdate"));
|
||||
}
|
||||
}
|
||||
if (isInstalled) {
|
||||
addToLog(_T("Installed version: ") + currentVersion);
|
||||
if (!newInterfaceVersion) {
|
||||
|
@ -576,7 +576,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
}
|
||||
}
|
||||
_shouldWait = FALSE;
|
||||
|
||||
|
||||
} else {
|
||||
setFailed(true);
|
||||
CString msg;
|
||||
|
@ -587,7 +587,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
}
|
||||
}
|
||||
|
||||
LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username,
|
||||
LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username,
|
||||
const CString& password) {
|
||||
CStringA post = "grant_type=password&username=";
|
||||
post += username;
|
||||
|
@ -599,9 +599,9 @@ LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const
|
|||
CString response;
|
||||
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(),
|
||||
true,
|
||||
L"metaverse.highfidelity.com",
|
||||
L"metaverse.highfidelity.com",
|
||||
L"/oauth/token",
|
||||
contentTypeText, post,
|
||||
contentTypeText, post,
|
||||
response, true);
|
||||
if (error != LauncherUtils::ResponseError::NoError) {
|
||||
return error;
|
||||
|
@ -629,7 +629,7 @@ BOOL LauncherManager::createApplicationRegistryKeys(int size) {
|
|||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath);
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayVersion", LauncherUtils::cStringToStd(_latestVersion));
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe);
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "High Fidelity");
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "Vircadia");
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "InstallDate", LauncherUtils::cStringToStd(CTime::GetCurrentTime().Format("%Y%m%d")));
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "EstimatedSize", (DWORD)size);
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "NoModify", (DWORD)1);
|
||||
|
@ -686,9 +686,9 @@ BOOL LauncherManager::extractApplication() {
|
|||
updateProgress(ProcessType::UnzipApplication, max(progress, 0.0f));
|
||||
};
|
||||
_currentProcess = ProcessType::UnzipApplication;
|
||||
BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipApplication,
|
||||
BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipApplication,
|
||||
LauncherUtils::cStringToStd(_applicationZipPath),
|
||||
LauncherUtils::cStringToStd(installPath),
|
||||
LauncherUtils::cStringToStd(installPath),
|
||||
onExtractFinished, onProgress);
|
||||
if (success) {
|
||||
addToLog(_T("Created thread for unzipping application."));
|
||||
|
@ -737,7 +737,7 @@ void LauncherManager::restartNewLauncher() {
|
|||
continueAction = ContinueActionOnStart::ContinueUpdate;
|
||||
} else if (_keepLoggingIn) {
|
||||
continueAction = ContinueActionOnStart::ContinueLogIn;
|
||||
}
|
||||
}
|
||||
CStringW params;
|
||||
params.Format(_T(" --restart --noUpdate --continueAction %s"), getContinueActionParam(continueAction));
|
||||
LauncherUtils::launchApplication(_tempLauncherPath, params.GetBuffer());
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
static const int MAX_TARGET_MARKERS = 30;
|
||||
static const float JOINT_CHAIN_INTERP_TIME = 0.5f;
|
||||
|
||||
static QTime debounceJointWarningsClock;
|
||||
static const int JOINT_WARNING_DEBOUNCE_TIME = 30000; // 30 seconds
|
||||
|
||||
static void lookupJointInfo(const AnimInverseKinematics::JointChainInfo& jointChainInfo,
|
||||
int indexA, int indexB,
|
||||
const AnimInverseKinematics::JointInfo** jointInfoA,
|
||||
|
@ -91,6 +94,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const IKTargetVar& orig) :
|
|||
}
|
||||
|
||||
AnimInverseKinematics::AnimInverseKinematics(const QString& id) : AnimNode(AnimNode::Type::InverseKinematics, id) {
|
||||
debounceJointWarningsClock.start();
|
||||
}
|
||||
|
||||
AnimInverseKinematics::~AnimInverseKinematics() {
|
||||
|
@ -158,6 +162,14 @@ void AnimInverseKinematics::setTargetVars(const QString& jointName, const QStrin
|
|||
}
|
||||
}
|
||||
|
||||
bool debounceJointWarnings() {
|
||||
if (debounceJointWarningsClock.elapsed() >= JOINT_WARNING_DEBOUNCE_TIME) {
|
||||
debounceJointWarningsClock.restart();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses) {
|
||||
|
||||
_hipsTargetIndex = -1;
|
||||
|
@ -172,7 +184,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
|||
if (jointIndex >= 0) {
|
||||
// this targetVar has a valid joint --> cache the indices
|
||||
targetVar.jointIndex = jointIndex;
|
||||
} else {
|
||||
} else if (debounceJointWarnings()) {
|
||||
qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ void AutoUpdater::getLatestVersionData() {
|
|||
QNetworkRequest latestVersionRequest(buildsURL);
|
||||
|
||||
latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(latestVersionRequest);
|
||||
connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
|
@ -62,7 +63,7 @@ void JSBaker::loadScript() {
|
|||
// setup the request to follow re-directs and always hit the network
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
networkRequest.setUrl(_jsURL);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <PathUtils.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <hfm/ModelFormatRegistry.h>
|
||||
|
@ -159,7 +160,7 @@ void ModelBaker::saveSourceModel() {
|
|||
// setup the request to follow re-directs and always hit the network
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
networkRequest.setUrl(_modelURL);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <image/TextureProcessing.h>
|
||||
#include <ktx/KTX.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextureMeta.h>
|
||||
|
||||
|
@ -99,7 +100,7 @@ void TextureBaker::loadTexture() {
|
|||
// setup the request to follow re-directs and always hit the network
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
networkRequest.setUrl(_textureURL);
|
||||
|
||||
|
|
|
@ -425,6 +425,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
}
|
||||
|
||||
float expectedUpdateCost = _avgRenderableUpdateCost * _renderablesToUpdate.size();
|
||||
_prevTotalNeededEntityUpdates = _renderablesToUpdate.size();
|
||||
if (expectedUpdateCost < MAX_UPDATE_RENDERABLES_TIME_BUDGET) {
|
||||
// we expect to update all renderables within available time budget
|
||||
PROFILE_RANGE_EX(simulation_physics, "UpdateRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
||||
|
@ -433,7 +434,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
assert(renderable); // only valid renderables are added to _renderablesToUpdate
|
||||
renderable->updateInScene(scene, transaction);
|
||||
}
|
||||
size_t numRenderables = _renderablesToUpdate.size() + 1; // add one to avoid divide by zero
|
||||
_prevNumEntityUpdates = _renderablesToUpdate.size();
|
||||
size_t numRenderables = _prevNumEntityUpdates + 1; // add one to avoid divide by zero
|
||||
_renderablesToUpdate.clear();
|
||||
|
||||
// compute average per-renderable update cost
|
||||
|
@ -494,7 +496,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
}
|
||||
|
||||
// compute average per-renderable update cost
|
||||
size_t numUpdated = sortedRenderables.size() - _renderablesToUpdate.size() + 1; // add one to avoid divide by zero
|
||||
_prevNumEntityUpdates = sortedRenderables.size() - _renderablesToUpdate.size();
|
||||
size_t numUpdated = _prevNumEntityUpdates + 1; // add one to avoid divide by zero
|
||||
float cost = (float)(usecTimestampNow() - updateStart) / (float)(numUpdated);
|
||||
const float BLEND = 0.1f;
|
||||
_avgRenderableUpdateCost = (1.0f - BLEND) * _avgRenderableUpdateCost + BLEND * cost;
|
||||
|
|
|
@ -136,6 +136,9 @@ public:
|
|||
static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName);
|
||||
static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName);
|
||||
|
||||
int getPrevNumEntityUpdates() const { return _prevNumEntityUpdates; }
|
||||
int getPrevTotalNeededEntityUpdates() const { return _prevTotalNeededEntityUpdates; }
|
||||
|
||||
signals:
|
||||
void enterEntity(const EntityItemID& entityItemID);
|
||||
void leaveEntity(const EntityItemID& entityItemID);
|
||||
|
@ -249,6 +252,8 @@ private:
|
|||
|
||||
ReadWriteLockable _changedEntitiesGuard;
|
||||
std::unordered_set<EntityItemID> _changedEntities;
|
||||
int _prevNumEntityUpdates { 0 };
|
||||
int _prevTotalNeededEntityUpdates { 0 };
|
||||
|
||||
std::unordered_set<EntityRendererPointer> _renderablesToUpdate;
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer> _entitiesInScene;
|
||||
|
|
|
@ -219,13 +219,6 @@ void EntityRenderer::render(RenderArgs* args) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!_renderUpdateQueued && needsRenderUpdate()) {
|
||||
// FIXME find a way to spread out the calls to needsRenderUpdate so that only a given subset of the
|
||||
// items checks every frame, like 1/N of the tree ever N frames
|
||||
_renderUpdateQueued = true;
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
if (_visible && (args->_renderMode != RenderArgs::RenderMode::DEFAULT_RENDER_MODE || !_cauterized)) {
|
||||
doRender(args);
|
||||
}
|
||||
|
@ -344,11 +337,6 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans
|
|||
}
|
||||
_updateTime = usecTimestampNow();
|
||||
|
||||
// FIXME is this excessive?
|
||||
if (!needsRenderUpdate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
doRenderUpdateSynchronous(scene, transaction, _entity);
|
||||
transaction.updateItem<PayloadProxyInterface>(_renderItemID, [this](PayloadProxyInterface& self) {
|
||||
if (!isValidRenderItem()) {
|
||||
|
@ -356,7 +344,6 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans
|
|||
}
|
||||
// Happens on the render thread. Classes should use
|
||||
doRenderUpdateAsynchronous(_entity);
|
||||
_renderUpdateQueued = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -457,7 +444,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
|
|||
}
|
||||
|
||||
void EntityRenderer::onAddToScene(const EntityItemPointer& entity) {
|
||||
QObject::connect(this, &EntityRenderer::requestRenderUpdate, this, [this] {
|
||||
QObject::connect(this, &EntityRenderer::requestRenderUpdate, this, [this] {
|
||||
auto renderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
if (renderer) {
|
||||
renderer->onEntityChanged(_entity->getID());
|
||||
|
@ -466,7 +453,10 @@ void EntityRenderer::onAddToScene(const EntityItemPointer& entity) {
|
|||
_changeHandlerId = entity->registerChangeHandler([](const EntityItemID& changedEntity) {
|
||||
auto renderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
if (renderer) {
|
||||
renderer->onEntityChanged(changedEntity);
|
||||
auto renderable = renderer->renderableForEntityId(changedEntity);
|
||||
if (renderable && renderable->needsRenderUpdate()) {
|
||||
renderer->onEntityChanged(changedEntity);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -148,8 +148,6 @@ protected:
|
|||
QVector<QUuid> _renderWithZones;
|
||||
bool _cauterized { false };
|
||||
bool _moving { false };
|
||||
// Only touched on the rendering thread
|
||||
bool _renderUpdateQueued{ false };
|
||||
Transform _renderTransform;
|
||||
|
||||
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
|
||||
|
@ -191,10 +189,7 @@ protected:
|
|||
using Parent::needsRenderUpdateFromEntity;
|
||||
// Returns true if the item in question needs to have updateInScene called because of changes in the entity
|
||||
virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const override final {
|
||||
if (Parent::needsRenderUpdateFromEntity(entity)) {
|
||||
return true;
|
||||
}
|
||||
return needsRenderUpdateFromTypedEntity(_typedEntity);
|
||||
return Parent::needsRenderUpdateFromEntity(entity) || needsRenderUpdateFromTypedEntity(_typedEntity);
|
||||
}
|
||||
|
||||
virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) override final {
|
||||
|
|
|
@ -39,6 +39,16 @@ bool GizmoEntityRenderer::isTransparent() const {
|
|||
return Parent::isTransparent() || ringTransparent;
|
||||
}
|
||||
|
||||
void GizmoEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void GizmoEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
bool dirty = false;
|
||||
RingGizmoPropertyGroup ringProperties = entity->getRingProperties();
|
||||
|
@ -186,15 +196,6 @@ void GizmoEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
|
||||
withWriteLock([&] {
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Item::Bound GizmoEntityRenderer::getBound() {
|
||||
|
|
|
@ -29,6 +29,7 @@ protected:
|
|||
bool isTransparent() const override;
|
||||
|
||||
private:
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity);
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
|
|
|
@ -41,10 +41,9 @@ void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
_dimensions = entity->getScaledDimensions();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -30,11 +30,9 @@ bool ImageEntityRenderer::isTransparent() const {
|
|||
}
|
||||
|
||||
bool ImageEntityRenderer::needsRenderUpdate() const {
|
||||
bool textureLoadedChanged = resultWithReadLock<bool>([&] {
|
||||
return (!_textureIsLoaded && _texture && _texture->isLoaded());
|
||||
});
|
||||
|
||||
if (textureLoadedChanged) {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
return !_textureIsLoaded;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -63,15 +61,15 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
_pulseProperties = entity->getPulseProperties();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
|
||||
if (!_textureIsLoaded && _texture && _texture->isLoaded()) {
|
||||
_textureIsLoaded = true;
|
||||
if (!_textureIsLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
_textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
|
||||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
|
|
|
@ -1337,7 +1337,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
_model->setCullWithParent(_cullWithParent);
|
||||
_model->setRenderWithZones(_renderWithZones);
|
||||
emit requestRenderUpdate();
|
||||
if(didVisualGeometryRequestSucceed) {
|
||||
if (didVisualGeometryRequestSucceed) {
|
||||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
|
||||
modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, _model);
|
||||
}
|
||||
|
|
|
@ -64,6 +64,16 @@ ParticleEffectEntityRenderer::ParticleEffectEntityRenderer(const EntityItemPoint
|
|||
});
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityRenderer::needsRenderUpdate() const {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
return !_textureLoaded;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
auto newParticleProperties = entity->getParticleProperties();
|
||||
if (!newParticleProperties.valid()) {
|
||||
|
@ -102,6 +112,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_textureLoaded = true;
|
||||
entity->setVisuallyReady(true);
|
||||
});
|
||||
} else {
|
||||
|
@ -111,20 +122,29 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
if (textureNeedsUpdate) {
|
||||
withWriteLock([&] {
|
||||
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
|
||||
_textureLoaded = false;
|
||||
entity->setVisuallyReady(false);
|
||||
});
|
||||
}
|
||||
|
||||
if (_networkTexture) {
|
||||
if (!_textureLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
bool textureLoaded = resultWithReadLock<bool>([&] {
|
||||
return _networkTexture && (_networkTexture->isLoaded() || _networkTexture->isFailed());
|
||||
});
|
||||
if (textureLoaded) {
|
||||
withWriteLock([&] {
|
||||
entity->setVisuallyReady(_networkTexture->isFailed() || _networkTexture->isLoaded());
|
||||
entity->setVisuallyReady(true);
|
||||
_textureLoaded = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] {
|
||||
withWriteLock([&] {
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
});
|
||||
});
|
||||
|
@ -199,7 +219,7 @@ float importanceSample3DDimension(float startDim) {
|
|||
return dimension;
|
||||
}
|
||||
|
||||
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
|
||||
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(const Transform& baseTransform, const particle::Properties& particleProperties,
|
||||
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource,
|
||||
const TriangleInfo& triangleInfo) {
|
||||
CpuParticle particle;
|
||||
|
@ -217,7 +237,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
|
|||
const auto& polarFinish = particleProperties.polar.finish;
|
||||
|
||||
particle.seed = randFloatInRange(-1.0f, 1.0f);
|
||||
particle.expiration = now + (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND);
|
||||
particle.expiration = (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND);
|
||||
|
||||
particle.relativePosition = glm::vec3(0.0f);
|
||||
particle.basePosition = baseTransform.getTranslation();
|
||||
|
@ -403,7 +423,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
computeTriangles(geometryResource->getHFMModel());
|
||||
}
|
||||
// emit particle
|
||||
_cpuParticles.push_back(createParticle(now, modelTransform, particleProperties, shapeType, geometryResource, _triangleInfo));
|
||||
_cpuParticles.push_back(createParticle(modelTransform, particleProperties, shapeType, geometryResource, _triangleInfo));
|
||||
_timeUntilNextEmit = emitInterval;
|
||||
if (emitInterval < timeRemaining) {
|
||||
timeRemaining -= emitInterval;
|
||||
|
@ -415,7 +435,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
}
|
||||
|
||||
// Kill any particles that have expired or are over the max size
|
||||
while (_cpuParticles.size() > particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration <= now)) {
|
||||
while (_cpuParticles.size() > particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration == 0)) {
|
||||
_cpuParticles.pop_front();
|
||||
}
|
||||
|
||||
|
@ -428,6 +448,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
}
|
||||
particle.basePosition = modelTransform.getTranslation();
|
||||
}
|
||||
particle.expiration = particle.expiration >= interval ? particle.expiration - interval : 0;
|
||||
particle.integrate(deltaTime);
|
||||
}
|
||||
_prevEmitterShouldTrail = particleProperties.emission.shouldTrail;
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
ParticleEffectEntityRenderer(const EntityItemPointer& entity);
|
||||
|
||||
protected:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
|
@ -88,7 +89,7 @@ private:
|
|||
glm::mat4 transform;
|
||||
} _triangleInfo;
|
||||
|
||||
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
|
||||
static CpuParticle createParticle(const Transform& baseTransform, const particle::Properties& particleProperties,
|
||||
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource,
|
||||
const TriangleInfo& triangleInfo);
|
||||
void stepSimulation();
|
||||
|
@ -111,6 +112,7 @@ private:
|
|||
GeometryResource::Pointer _geometryResource;
|
||||
|
||||
NetworkTexturePointer _networkTexture;
|
||||
bool _textureLoaded { false };
|
||||
ScenePointer _scene;
|
||||
};
|
||||
|
||||
|
|
|
@ -204,9 +204,8 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
|
|||
bool geometryChanged = uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged;
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] () {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] {
|
||||
withWriteLock([&] {
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
|
||||
if (geometryChanged) {
|
||||
|
|
|
@ -70,20 +70,18 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
auto entity = getEntity();
|
||||
_position = entity->getWorldPosition();
|
||||
_dimensions = entity->getUnscaledDimensions(); // get unscaled to avoid scaling twice
|
||||
_orientation = entity->getWorldOrientation();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform(); // contains parent scale, if this entity scales with its parent
|
||||
if (_shape == entity::Sphere) {
|
||||
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||
}
|
||||
|
||||
_renderTransform.postScale(_dimensions);
|
||||
});;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -121,11 +119,16 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
|
|||
materialChanged = true;
|
||||
}
|
||||
|
||||
if (materialChanged) {
|
||||
auto materials = _materials.find("0");
|
||||
if (materials != _materials.end()) {
|
||||
auto materials = _materials.find("0");
|
||||
if (materials != _materials.end()) {
|
||||
if (materialChanged) {
|
||||
materials->second.setNeedsUpdate(true);
|
||||
}
|
||||
|
||||
if (materials->second.shouldUpdate()) {
|
||||
RenderPipelines::updateMultiMaterial(materials->second);
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ private:
|
|||
PulsePropertyGroup _pulseProperties;
|
||||
std::shared_ptr<graphics::ProceduralMaterial> _material { std::make_shared<graphics::ProceduralMaterial>() };
|
||||
glm::vec3 _color { NAN };
|
||||
float _alpha;
|
||||
float _alpha { NAN };
|
||||
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _dimensions;
|
||||
|
|
|
@ -102,10 +102,9 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
|
||||
void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
_dimensions = entity->getScaledDimensions();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(_dimensions);
|
||||
});
|
||||
|
|
|
@ -236,11 +236,10 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
glm::vec2 windowSize = getWindowSize(entity);
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.setScale(1.0f);
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
|
|
|
@ -262,14 +262,6 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
entity->setVisuallyReady(visuallyReady);
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
if (entity->getShapeType() == SHAPE_TYPE_SPHERE) {
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ItemKey ZoneEntityRenderer::getKey() {
|
||||
return ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()).build();
|
||||
}
|
||||
|
@ -306,8 +298,6 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
return true;
|
||||
}
|
||||
|
||||
// FIXME: do we need to trigger an update when shapeType changes? see doRenderUpdateAsynchronousTyped
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ protected:
|
|||
virtual void doRender(RenderArgs* args) override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
private:
|
||||
void updateKeyZoneItemFromEntity(const TypedEntityPointer& entity);
|
||||
|
|
|
@ -617,10 +617,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
_lastEdited = lastEditedFromBufferAdjusted;
|
||||
_lastEditedFromRemote = now;
|
||||
_lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer;
|
||||
|
||||
// TODO: only send this notification if something ACTUALLY changed (hint, we haven't yet parsed
|
||||
// the properties out of the bitstream (see below))
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
}
|
||||
|
||||
// last updated is stored as ByteCountCoded delta from lastEdited
|
||||
|
@ -1005,6 +1001,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
element->getTree()->trackIncomingEntityLastEdited(lastEditedFromBufferAdjusted, bytesRead);
|
||||
}
|
||||
|
||||
if (somethingChanged) {
|
||||
somethingChangedNotification();
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
@ -1573,14 +1572,14 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
qCDebug(entities) << "EntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
#endif
|
||||
setLastEdited(now);
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
setLastEdited(properties._lastEdited);
|
||||
if (getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
|
||||
// anything that sets the transform or velocity must update _lastSimulated which is used
|
||||
// for kinematic extrapolation (e.g. we want to extrapolate forward from this moment
|
||||
// when position and/or velocity was changed).
|
||||
_lastSimulated = now;
|
||||
}
|
||||
somethingChangedNotification();
|
||||
}
|
||||
|
||||
// timestamps
|
||||
|
@ -1832,6 +1831,7 @@ void EntityItem::setPosition(const glm::vec3& value) {
|
|||
void EntityItem::setParentID(const QUuid& value) {
|
||||
QUuid oldParentID = getParentID();
|
||||
if (oldParentID != value) {
|
||||
_needsRenderUpdate = true;
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree && !oldParentID.isNull()) {
|
||||
tree->removeFromChildrenOfAvatars(getThisPointer());
|
||||
|
@ -3000,10 +3000,15 @@ bool EntityItem::getCauterized() const {
|
|||
}
|
||||
|
||||
void EntityItem::setCauterized(bool value) {
|
||||
bool needsRenderUpdate = false;
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _cauterized != value;
|
||||
needsRenderUpdate = _cauterized != value;
|
||||
_needsRenderUpdate |= needsRenderUpdate;
|
||||
_cauterized = value;
|
||||
});
|
||||
if (needsRenderUpdate) {
|
||||
somethingChangedNotification();
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityItem::getIgnorePickIntersection() const {
|
||||
|
@ -3042,10 +3047,15 @@ bool EntityItem::getCullWithParent() const {
|
|||
}
|
||||
|
||||
void EntityItem::setCullWithParent(bool value) {
|
||||
bool needsRenderUpdate = false;
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _cullWithParent != value;
|
||||
needsRenderUpdate = _cullWithParent != value;
|
||||
_needsRenderUpdate |= needsRenderUpdate;
|
||||
_cullWithParent = value;
|
||||
});
|
||||
if (needsRenderUpdate) {
|
||||
somethingChangedNotification();
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityItem::isChildOfMyAvatar() const {
|
||||
|
|
|
@ -579,8 +579,8 @@ public:
|
|||
|
||||
bool stillHasMyGrab() const;
|
||||
|
||||
bool needsRenderUpdate() const { return resultWithReadLock<bool>([&] { return _needsRenderUpdate; }); }
|
||||
void setNeedsRenderUpdate(bool needsRenderUpdate) { withWriteLock([&] { _needsRenderUpdate = needsRenderUpdate; }); }
|
||||
bool needsRenderUpdate() const { return _needsRenderUpdate; }
|
||||
void setNeedsRenderUpdate(bool needsRenderUpdate) { _needsRenderUpdate = needsRenderUpdate; }
|
||||
|
||||
void setRenderWithZones(const QVector<QUuid>& renderWithZones);
|
||||
QVector<QUuid> getRenderWithZones() const;
|
||||
|
|
|
@ -2399,7 +2399,9 @@ signals:
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when an avatar enters an entity, but only if the entity has an entity method exposed for this event.
|
||||
* Triggered when an avatar enters an entity.
|
||||
* Note: At the initial loading of the script, if the avatar is already present inside the entity, it might be too late
|
||||
* to catch this event when the script runs, so it won't trigger. The {@link Entities.preload|preload} signal can be used to handle those cases.
|
||||
* <p>See also, {@link Entities|Entity Methods} and {@link Script.addEventHandler}.</p>
|
||||
* @function Entities.enterEntity
|
||||
* @param {Uuid} entityID - The ID of the entity that the avatar entered.
|
||||
|
@ -2408,7 +2410,7 @@ signals:
|
|||
void enterEntity(const EntityItemID& entityItemID);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when an avatar leaves an entity, but only if the entity has an entity method exposed for this event.
|
||||
* Triggered when an avatar leaves an entity.
|
||||
* <p>See also, {@link Entities|Entity Methods} and {@link Script.addEventHandler}.</p>
|
||||
* @function Entities.leaveEntity
|
||||
* @param {Uuid} entityID - The ID of the entity that the avatar left.
|
||||
|
|
|
@ -537,7 +537,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
return true;
|
||||
}
|
||||
|
||||
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone) {
|
||||
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone, const bool isImport) {
|
||||
EntityItemProperties props = properties;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -548,7 +548,8 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
|
|||
|
||||
if (properties.getEntityHostType() == entity::HostType::DOMAIN && getIsClient() &&
|
||||
!nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() &&
|
||||
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) {
|
||||
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() &&
|
||||
!_serverlessDomain && !isClone && !isImport) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -2233,15 +2234,24 @@ void EntityTree::fixupNeedsParentFixups() {
|
|||
}
|
||||
|
||||
entity->postParentFixup();
|
||||
} else if (getIsServer() || _avatarIDs.contains(entity->getParentID())) {
|
||||
// this is a child of an avatar, which the entity server will never have
|
||||
// a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves.
|
||||
if (!_childrenOfAvatars.contains(entity->getParentID())) {
|
||||
_childrenOfAvatars[entity->getParentID()] = QSet<EntityItemID>();
|
||||
} else {
|
||||
bool needsUpdate = getIsServer();
|
||||
if (!needsUpdate) {
|
||||
std::lock_guard<std::mutex> lock(_avatarIDsLock);
|
||||
needsUpdate = _avatarIDs.contains(entity->getParentID());
|
||||
}
|
||||
|
||||
if (needsUpdate) {
|
||||
std::lock_guard<std::mutex> lock(_childrenOfAvatarsLock);
|
||||
// this is a child of an avatar, which the entity server will never have
|
||||
// a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves.
|
||||
if (!_childrenOfAvatars.contains(entity->getParentID())) {
|
||||
_childrenOfAvatars[entity->getParentID()] = QSet<EntityItemID>();
|
||||
}
|
||||
_childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID();
|
||||
doMove = true;
|
||||
iter.remove(); // and pull it out of the list
|
||||
}
|
||||
_childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID();
|
||||
doMove = true;
|
||||
iter.remove(); // and pull it out of the list
|
||||
}
|
||||
|
||||
if (queryAACubeSuccess && doMove) {
|
||||
|
@ -2261,8 +2271,19 @@ void EntityTree::fixupNeedsParentFixups() {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) {
|
||||
QHash<QUuid, QSet<EntityItemID>>::const_iterator itr = _childrenOfAvatars.constFind(avatarID);
|
||||
void EntityTree::knowAvatarID(const QUuid& avatarID) {
|
||||
std::lock_guard<std::mutex> lock(_avatarIDsLock);
|
||||
_avatarIDs += avatarID;
|
||||
}
|
||||
|
||||
void EntityTree::forgetAvatarID(const QUuid& avatarID) {
|
||||
std::lock_guard<std::mutex> lock(_avatarIDsLock);
|
||||
_avatarIDs -= avatarID;
|
||||
}
|
||||
|
||||
void EntityTree::deleteDescendantsOfAvatar(const QUuid& avatarID) {
|
||||
std::lock_guard<std::mutex> lock(_childrenOfAvatarsLock);
|
||||
auto itr = _childrenOfAvatars.find(avatarID);
|
||||
if (itr != _childrenOfAvatars.end()) {
|
||||
if (!itr.value().empty()) {
|
||||
std::vector<EntityItemID> ids;
|
||||
|
@ -2280,8 +2301,10 @@ void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) {
|
|||
|
||||
void EntityTree::removeFromChildrenOfAvatars(EntityItemPointer entity) {
|
||||
QUuid avatarID = entity->getParentID();
|
||||
if (_childrenOfAvatars.contains(avatarID)) {
|
||||
_childrenOfAvatars[avatarID].remove(entity->getID());
|
||||
std::lock_guard<std::mutex> lock(_childrenOfAvatarsLock);
|
||||
auto itr = _childrenOfAvatars.find(avatarID);
|
||||
if (itr != _childrenOfAvatars.end()) {
|
||||
itr.value().remove(entity->getID());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2657,11 +2680,12 @@ QByteArray EntityTree::remapActionDataIDs(QByteArray actionData, QHash<EntityIte
|
|||
}
|
||||
|
||||
QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSender, EntityTreePointer localTree,
|
||||
float x, float y, float z) {
|
||||
const QString& entityHostType, float x, float y, float z) {
|
||||
SendEntitiesOperationArgs args;
|
||||
args.ourTree = this;
|
||||
args.otherTree = localTree;
|
||||
args.root = glm::vec3(x, y, z);
|
||||
args.entityHostType = entityHostType;
|
||||
// If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we
|
||||
// use new identifiers. We need to keep a map so that we can map parent identifiers correctly.
|
||||
QHash<EntityItemID, EntityItemID> map;
|
||||
|
@ -2751,6 +2775,11 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void
|
|||
EntityItemID newID = getMapped(oldID);
|
||||
EntityItemProperties properties = item->getProperties();
|
||||
|
||||
properties.setEntityHostTypeFromString(args->entityHostType);
|
||||
if (properties.getEntityHostType() == entity::HostType::AVATAR) {
|
||||
properties.setOwningAvatarID(AVATAR_SELF_ID);
|
||||
}
|
||||
|
||||
EntityItemID oldParentID = properties.getParentID();
|
||||
if (oldParentID.isInvalidID()) { // no parent
|
||||
properties.setPosition(properties.getPosition() + args->root);
|
||||
|
@ -2764,6 +2793,17 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void
|
|||
}
|
||||
}
|
||||
|
||||
QVector<QUuid> oldRenderWithZones = properties.getRenderWithZones();
|
||||
if (!oldRenderWithZones.isEmpty()) {
|
||||
QVector<QUuid> newRenderWithZones;
|
||||
for (QUuid oldRenderWithZoneID : oldRenderWithZones) {
|
||||
if (args->ourTree->findEntityByEntityItemID(oldRenderWithZoneID)) {
|
||||
newRenderWithZones.append(getMapped(oldRenderWithZoneID));
|
||||
}
|
||||
}
|
||||
properties.setRenderWithZones(newRenderWithZones);
|
||||
}
|
||||
|
||||
properties.setXNNeighborID(getMapped(properties.getXNNeighborID()));
|
||||
properties.setXPNeighborID(getMapped(properties.getXPNeighborID()));
|
||||
properties.setYNNeighborID(getMapped(properties.getYNNeighborID()));
|
||||
|
@ -2926,7 +2966,7 @@ void convertGrabUserDataToProperties(EntityItemProperties& properties) {
|
|||
}
|
||||
|
||||
|
||||
bool EntityTree::readFromMap(QVariantMap& map) {
|
||||
bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) {
|
||||
// These are needed to deal with older content (before adding inheritance modes)
|
||||
int contentVersion = map["Version"].toInt();
|
||||
|
||||
|
@ -3096,7 +3136,7 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
}
|
||||
}
|
||||
|
||||
EntityItemPointer entity = addEntity(entityItemID, properties);
|
||||
EntityItemPointer entity = addEntity(entityItemID, properties, isImport);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();
|
||||
success = false;
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
class SendEntitiesOperationArgs {
|
||||
public:
|
||||
glm::vec3 root;
|
||||
QString entityHostType;
|
||||
EntityTree* ourTree;
|
||||
EntityTreePointer otherTree;
|
||||
QHash<EntityItemID, EntityItemID>* map;
|
||||
|
@ -117,7 +118,7 @@ public:
|
|||
// The newer API...
|
||||
void postAddEntity(EntityItemPointer entityItem);
|
||||
|
||||
EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false);
|
||||
EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false, const bool isImport = false);
|
||||
|
||||
// use this method if you only know the entityID
|
||||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||
|
@ -177,7 +178,7 @@ public:
|
|||
static QByteArray remapActionDataIDs(QByteArray actionData, QHash<EntityItemID, EntityItemID>& map);
|
||||
|
||||
QVector<EntityItemID> sendEntities(EntityEditPacketSender* packetSender, EntityTreePointer localTree,
|
||||
float x, float y, float z);
|
||||
const QString& entityHostType, float x, float y, float z);
|
||||
|
||||
void entityChanged(EntityItemPointer entity);
|
||||
|
||||
|
@ -195,7 +196,7 @@ public:
|
|||
|
||||
virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues,
|
||||
bool skipThoseWithBadParents) override;
|
||||
virtual bool readFromMap(QVariantMap& entityDescription) override;
|
||||
virtual bool readFromMap(QVariantMap& entityDescription, const bool isImport = false) override;
|
||||
virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) override;
|
||||
|
||||
|
||||
|
@ -239,9 +240,9 @@ public:
|
|||
Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name) const;
|
||||
Q_INVOKABLE QStringList getJointNames(const QUuid& entityID) const;
|
||||
|
||||
void knowAvatarID(QUuid avatarID) { _avatarIDs += avatarID; }
|
||||
void forgetAvatarID(QUuid avatarID) { _avatarIDs -= avatarID; }
|
||||
void deleteDescendantsOfAvatar(QUuid avatarID);
|
||||
void knowAvatarID(const QUuid& avatarID);
|
||||
void forgetAvatarID(const QUuid& avatarID);
|
||||
void deleteDescendantsOfAvatar(const QUuid& avatarID);
|
||||
void removeFromChildrenOfAvatars(EntityItemPointer entity);
|
||||
|
||||
void addToNeedsParentFixupList(EntityItemPointer entity);
|
||||
|
@ -364,8 +365,10 @@ protected:
|
|||
QVector<EntityItemWeakPointer> _needsParentFixup; // entites with a parentID but no (yet) known parent instance
|
||||
mutable QReadWriteLock _needsParentFixupLock;
|
||||
|
||||
std::mutex _avatarIDsLock;
|
||||
// we maintain a list of avatarIDs to notice when an entity is a child of one.
|
||||
QSet<QUuid> _avatarIDs; // IDs of avatars connected to entity server
|
||||
std::mutex _childrenOfAvatarsLock;
|
||||
QHash<QUuid, QSet<EntityItemID>> _childrenOfAvatars; // which entities are children of which avatars
|
||||
|
||||
float _maxTmpEntityLifetime { DEFAULT_MAX_TMP_ENTITY_LIFETIME };
|
||||
|
|
|
@ -39,8 +39,8 @@ EntityItemProperties GizmoEntityItem::getProperties(const EntityPropertyFlags& d
|
|||
return properties;
|
||||
}
|
||||
|
||||
bool GizmoEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
bool GizmoEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gizmoType, setGizmoType);
|
||||
withWriteLock([&] {
|
||||
|
@ -49,16 +49,6 @@ bool GizmoEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
_needsRenderUpdate |= ringPropertiesChanged;
|
||||
});
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - getLastEdited();
|
||||
qCDebug(entities) << "GizmoEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
}
|
||||
setLastEdited(properties.getLastEdited());
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
// methods for getting/setting all properties of an entity
|
||||
EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
|
||||
bool setProperties(const EntityItemProperties& properties) override;
|
||||
bool setSubClassProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@ EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& de
|
|||
return properties;
|
||||
}
|
||||
|
||||
bool GridEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
bool GridEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
|
@ -61,16 +61,6 @@ bool GridEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(majorGridEvery, setMajorGridEvery);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(minorGridEvery, setMinorGridEvery);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - getLastEdited();
|
||||
qCDebug(entities) << "GridEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
}
|
||||
setLastEdited(properties.getLastEdited());
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
// methods for getting/setting all properties of an entity
|
||||
EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
|
||||
bool setProperties(const EntityItemProperties& properties) override;
|
||||
bool setSubClassProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
|
|
|
@ -45,8 +45,8 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d
|
|||
return properties;
|
||||
}
|
||||
|
||||
bool ImageEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
bool ImageEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
|
@ -62,16 +62,6 @@ bool ImageEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keepAspectRatio, setKeepAspectRatio);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(subImage, setSubImage);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - getLastEdited();
|
||||
qCDebug(entities) << "ImageEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
}
|
||||
setLastEdited(properties.getLastEdited());
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
// methods for getting/setting all properties of an entity
|
||||
EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
|
||||
bool setProperties(const EntityItemProperties& properties) override;
|
||||
bool setSubClassProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
|
|
|
@ -55,21 +55,6 @@ void LightEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
|||
}
|
||||
}
|
||||
|
||||
void LightEntityItem::locationChanged(bool tellPhysics, bool tellChildren) {
|
||||
EntityItem::locationChanged(tellPhysics, tellChildren);
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate = true;
|
||||
});
|
||||
}
|
||||
|
||||
void LightEntityItem::dimensionsChanged() {
|
||||
EntityItem::dimensionsChanged();
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
EntityItemProperties LightEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
|
||||
|
||||
|
@ -134,23 +119,8 @@ void LightEntityItem::setCutoff(float value) {
|
|||
}
|
||||
}
|
||||
|
||||
bool LightEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - getLastEdited();
|
||||
qCDebug(entities) << "LightEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
}
|
||||
setLastEdited(properties.getLastEdited());
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
bool LightEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setSubClassProperties(properties); // set the properties in our base class
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight);
|
||||
|
@ -162,7 +132,6 @@ bool LightEntityItem::setSubClassProperties(const EntityItemProperties& properti
|
|||
return somethingChanged;
|
||||
}
|
||||
|
||||
|
||||
int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue