From d20b036ac7db086af89a1c07f329a49e03b6455c Mon Sep 17 00:00:00 2001 From: kasenvr <52365539+kasenvr@users.noreply.github.com> Date: Wed, 2 Dec 2020 22:52:42 -0500 Subject: [PATCH 01/86] Add basic Linux server packaging instructions. --- INSTALL.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 994725ac28..a0966e3fc5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,6 +1,6 @@ # Creating an Installer -*Last Updated on August 24, 2020* +*Last Updated on December 2, 2020* Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. @@ -9,13 +9,13 @@ During generation, CMake should produce an `install` target and a `package` targ The `install` target will copy the Vircadia targets and their dependencies to your `CMAKE_INSTALL_PREFIX`. This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt` -### Packaging +## Packaging To produce an installer, run the `package` target. However you will want to follow the steps specific to your platform below. -#### Windows +### Windows -##### Prerequisites +#### Prerequisites To produce an executable installer on Windows, the following are required: @@ -63,11 +63,11 @@ To produce an executable installer on Windows, the following are required: 1. [Node.JS and NPM]() 1. Install version 10.15.0 LTS -##### Code Signing (optional) +#### Code Signing (optional) For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PASSPHRASE` environment variables to be present during CMake runtime and globally as we proceed to package the installer. -##### Creating the Installer +#### Creating the Installer 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. @@ -79,7 +79,14 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS 1. Build CMakeTargets->PACKAGE The installer is now available in `build\_CPack_Packages\win64\NSIS` -#### OS X +#### FAQ + +1. **Problem:** Failure to open a file. ```File: failed opening file "\FOLDERSHARE\XYZSRelease\...\Credits.rtf" Error in script "C:\TFS\XYZProject\Releases\NullsoftInstaller\XYZWin7Installer.nsi" on line 77 -- aborting creation process``` + 1. **Cause:** The complete path (current directory + relative path) has to be < 260 characters to any of the relevant files. + 1. **Solution:** Move your build and packaging folder as high up in the drive as possible to prevent an overage. + +### OS X + 1. [npm]() Install version 12.16.3 LTS @@ -91,8 +98,54 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS 1. Perform a Release build of `package` Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop -### FAQ +### Linux -1. **Problem:** Failure to open a file. ```File: failed opening file "\FOLDERSHARE\XYZSRelease\...\Credits.rtf" Error in script "C:\TFS\XYZProject\Releases\NullsoftInstaller\XYZWin7Installer.nsi" on line 77 -- aborting creation process``` - 1. **Cause:** The complete path (current directory + relative path) has to be < 260 characters to any of the relevant files. - 1. **Solution:** Move your build and packaging folder as high up in the drive as possible to prevent an overage. +#### Server + +##### Ubuntu | .deb + +1. Ensure you are using an Ubuntu 18.04 system. +1. Set up Vircadia Builder to compile the server. + ``` + git clone https://github.com/kasenvr/vircadia-builder.git + cd vircadia-builder + chmod +x vircadia-builder + ./vircadia-builder + ``` +1. Build the server. + ``` + ./vircadia-builder --build server + ``` +1. Navigate to the `pkg-scripts` directory. + ``` + cd ../Vircadia/source/pkg-scripts/ + ``` +1. Generate the .deb package. + ``` + DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server + ``` +1. If successful, the generated .deb package will be in the `pkg-scripts` folder. + +##### Amazon Linux 2 | .rpm + +1. Ensure you are using an Amazon Linux 2 system. +1. Set up Vircadia Builder to compile the server. + ``` + git clone https://github.com/kasenvr/vircadia-builder.git + cd vircadia-builder + chmod +x vircadia-builder + ./vircadia-builder + ``` +1. Build the server. + ``` + ./vircadia-builder --build server + ``` +1. Navigate to the `pkg-scripts` directory. + ``` + cd ../Vircadia/source/pkg-scripts/ + ``` +1. Generate the .rpm package. + ``` + ./make-rpm-server + ``` +1. If successful, the generated .rpm package will be in the `pkg-scripts` folder. From 0ab82f686340fb0f1a5ea55fdb49ee8d0a584f99 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Mon, 21 Dec 2020 04:24:01 -0500 Subject: [PATCH 02/86] Add examples to env variables. --- BUILD.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/BUILD.md b/BUILD.md index 2d94d1b5b1..9b31c4895b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,6 +1,6 @@ # General Build Information -*Last Updated on August 26, 2020* +*Last Updated on December 21, 2020* ### OS Specific Build Guides @@ -11,7 +11,7 @@ ### Dependencies - [git](https://git-scm.com/downloads): >= 1.6 -- [cmake](https://cmake.org/download/): 3.9 +- [CMake](https://cmake.org/download/): 3.9 - [Python](https://www.python.org/downloads/): 3.6 or higher - [Node.JS](https://nodejs.org/en/): >= 12.13.1 LTS - Used to build the Screen Sharing executable. @@ -41,6 +41,7 @@ These are not placed in your normal build tree when doing an out of source build Vircadia uses CMake to generate build files and project files for your platform. #### Qt + CMake will download Qt 5.12.3 using vcpkg. To override this (i.e. use an installed Qt configuration - you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt **lib/cmake** folder. @@ -72,9 +73,9 @@ Where /path/to/directory is the path to a directory where you wish the build fil // The identifying tag of the release. CMAKE_BACKTRACE_TOKEN - // The release version. + // The release version. (e.g. 2021.4.2-Osiris) RELEASE_NUMBER - // The build commit. + // Typically, this is the build commit short hash. (e.g. f1576ab) BUILD_NUMBER // The type of release. @@ -108,7 +109,7 @@ If CMake gives you the same error message repeatedly after the build fails, try ##### Generating a release/debug only vcpkg build -In order to generate a release or debug only vcpkg package, you could use the use the `VCPKG_BUILD_TYPE` define in your cmake generate command. Building a release only vcpkg can drastically decrease the total build time. +In order to generate a release or debug only vcpkg package, you could use the use the `VCPKG_BUILD_TYPE` define in your CMake generate command. Building a release only vcpkg can drastically decrease the total build time. For release only vcpkg: From 3ed9572f83a43f6dd05027c3a65b8b814b6ee903 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Jan 2021 20:58:26 +0000 Subject: [PATCH 03/86] Bump electron from 7.2.4 to 9.4.0 in /screenshare Bumps [electron](https://github.com/electron/electron) from 7.2.4 to 9.4.0. - [Release notes](https://github.com/electron/electron/releases) - [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md) - [Commits](https://github.com/electron/electron/compare/v7.2.4...v9.4.0) Signed-off-by: dependabot[bot] --- screenshare/package-lock.json | 12 ++++++------ screenshare/package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/screenshare/package-lock.json b/screenshare/package-lock.json index 4d0fea0805..cf1f382ce0 100644 --- a/screenshare/package-lock.json +++ b/screenshare/package-lock.json @@ -71,9 +71,9 @@ } }, "@types/node": { - "version": "12.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz", - "integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==", + "version": "12.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.15.tgz", + "integrity": "sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==", "dev": true }, "ansi-styles": { @@ -365,9 +365,9 @@ "dev": true }, "electron": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-7.2.4.tgz", - "integrity": "sha512-Z+R692uTzXgP8AHrabE+kkrMlQJ6pnAYoINenwj9QSqaD2YbO8IuXU9DMCcUY0+VpA91ee09wFZJNUKYPMnCKg==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz", + "integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==", "dev": true, "requires": { "@electron/get": "^1.0.1", diff --git a/screenshare/package.json b/screenshare/package.json index e92e20a073..07d461f252 100644 --- a/screenshare/package.json +++ b/screenshare/package.json @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/highfidelity/hifi#readme", "devDependencies": { - "electron": "^7.2.4", + "electron": "^9.4.0", "electron-packager": "^14.0.6" }, "dependencies": { From d2875c2c4fa0e430e81f4d017e515a1786daee67 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Tue, 9 Feb 2021 00:44:01 -0500 Subject: [PATCH 04/86] Add manual mode to noise reduction + add noise reduction threshold --- interface/src/scripting/Audio.cpp | 50 ++++++++++++++++++++++ interface/src/scripting/Audio.h | 26 +++++++++++ libraries/audio-client/src/AudioClient.cpp | 32 +++++++++++++- libraries/audio-client/src/AudioClient.h | 12 ++++++ 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 43958188e3..36045bd8ec 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -25,6 +25,8 @@ QString Audio::DESKTOP { "Desktop" }; QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; +Setting::Handle enableNoiseReductionAutomaticSetting { QStringList { Audio::AUDIO, "NoiseReductionAutomatic" }, false }; +Setting::Handle setNoiseReductionThresholdSetting { QStringList { Audio::AUDIO, "NoiseReductionThreshold" }, 0.2f }; Setting::Handle enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true }; Setting::Handle enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true }; @@ -40,6 +42,8 @@ Audio::Audio() : _devices(_contextIsHMD) { auto client = DependencyManager::get().data(); connect(client, &AudioClient::muteToggled, this, &Audio::setMuted); connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction); + connect(client, &AudioClient::noiseReductionAutomaticChanged, this, &Audio::enableNoiseReductionAutomatic); + connect(client, &AudioClient::noiseReductionThresholdChanged, this, &Audio::setNoiseReductionThreshold); connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted); connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation); connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); @@ -47,6 +51,8 @@ Audio::Audio() : _devices(_contextIsHMD) { connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk); enableNoiseReduction(enableNoiseReductionSetting.get()); + enableNoiseReductionAutomatic(enableNoiseReductionAutomaticSetting.get()); + setNoiseReductionThreshold(setNoiseReductionThresholdSetting.get()); enableWarnWhenMuted(enableWarnWhenMutedSetting.get()); enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get()); onContextChanged(); @@ -286,6 +292,50 @@ void Audio::enableNoiseReduction(bool enable) { } } +bool Audio::noiseReductionAutomatic() const { + return resultWithReadLock([&] { + return _noiseReductionAutomatic; + }); +} + +void Audio::enableNoiseReductionAutomatic(bool enable) { + bool changed = false; + withWriteLock([&] { + if (_noiseReductionAutomatic != enable) { + _noiseReductionAutomatic = enable; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setNoiseReductionAutomatic", Q_ARG(bool, enable), Q_ARG(bool, false)); + enableNoiseReductionAutomaticSetting.set(enable); + changed = true; + } + }); + if (changed) { + emit noiseReductionAutomaticChanged(enable); + } +} + +float Audio::getNoiseReductionThreshold() const { + return resultWithReadLock([&] { + return _noiseReductionThreshold; + }); +} + +void Audio::setNoiseReductionThreshold(float threshold) { + bool changed = false; + withWriteLock([&] { + if (_noiseReductionThreshold != threshold) { + _noiseReductionThreshold = threshold; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setNoiseReductionThreshold", Q_ARG(float, threshold), Q_ARG(bool, false)); + setNoiseReductionThresholdSetting.set(threshold); + changed = true; + } + }); + if (changed) { + emit noiseReductionAutomaticChanged(threshold); + } +} + bool Audio::warnWhenMutedEnabled() const { return resultWithReadLock([&] { return _enableWarnWhenMuted; diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 74d85f0107..3addd6d5a4 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -92,6 +92,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged) + Q_PROPERTY(bool noiseReductionAutomatic READ noiseReductionAutomatic WRITE enableNoiseReductionAutomatic NOTIFY noiseReductionAutomaticChanged) + Q_PROPERTY(float noiseReductionThreshold READ getNoiseReductionThreshold WRITE setNoiseReductionThreshold NOTIFY noiseReductionThresholdChanged) Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged) Q_PROPERTY(bool acousticEchoCancellation READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged) @@ -124,6 +126,8 @@ public: bool isMuted() const; bool noiseReductionEnabled() const; + bool noiseReductionAutomatic() const; + float getNoiseReductionThreshold() const; bool warnWhenMutedEnabled() const; bool acousticEchoCancellationEnabled() const; float getInputVolume() const; @@ -398,6 +402,24 @@ signals: * @returns {Signal} */ void noiseReductionChanged(bool isEnabled); + + /**jsdoc + * Triggered when the audio input noise reduction mode is changed. + * @function Audio.noiseReductionAutomaticChanged + * @param {boolean} isEnabled - true if audio input noise reduction automatic mode is enabled, otherwise false. + * This means the mode is set to 'manual'. + * @returns {Signal} + */ + void noiseReductionAutomaticChanged(bool isEnabled); + + /**jsdoc + * Triggered when audio input noise reduction threshold is changed. + * @function Audio.noiseReductionThresholdChanged + * @param {number} threshold - The threshold for the audio input noise reduction, range 0.0 (open the gate completely) – 1.0 + * (close the gate completely). + * @returns {Signal} + */ + void noiseReductionThresholdChanged(float threshold); /**jsdoc * Triggered when "warn when muted" is enabled or disabled. @@ -512,6 +534,8 @@ public slots: private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); + void enableNoiseReductionAutomatic(bool enable); + void setNoiseReductionThreshold(float threshold); void enableWarnWhenMuted(bool enable); void enableAcousticEchoCancellation(bool enable); void setInputVolume(float volume); @@ -533,8 +557,10 @@ private: float _localInjectorGain { 0.0f }; // in dB float _systemInjectorGain { 0.0f }; // in dB float _pttOutputGainDesktop { 0.0f }; // in dB + float _noiseReductionThreshold { 0.2f }; // Match default value of AudioClient::_noiseReductionThreshold. bool _isClipping { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. + bool _noiseReductionAutomatic { true }; // Match default value of AudioClient::_noiseReductionAutomatic. bool _enableWarnWhenMuted { true }; bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled bool _contextIsHMD { false }; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2600097c3b..a3d2568b6d 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1342,6 +1342,13 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } } +float AudioClient::loudnessToLevel(float loudness) { + float level = loudness * (1 / 32768.0f); // level in [0, 1] + level = 6.02059991f * fastLog2f(level); // convert to dBFS + level = (level + 48.0f) * (1 / 42.0f); // map [-48, -6] dBFS to [0, 1] + return glm::clamp(level, 0.0f, 1.0f); +} + void AudioClient::handleAudioInput(QByteArray& audioBuffer) { if (!_audioPaused) { @@ -1352,9 +1359,14 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE; int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); - if (_isNoiseGateEnabled) { + if (_isNoiseGateEnabled && _isNoiseReductionAutomatic) { // The audio gate includes DC removal audioGateOpen = _audioGate->render(samples, samples, numFrames); + } else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic && + loudnessToLevel(_lastSmoothedRawInputLoudness) >= _noiseReductionThreshold) { + audioGateOpen = _audioGate->removeDC(samples, samples, numFrames); + } else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic) { + audioGateOpen = false; } else { audioGateOpen = _audioGate->removeDC(samples, samples, numFrames); } @@ -1750,6 +1762,24 @@ void AudioClient::setNoiseReduction(bool enable, bool emitSignal) { } } +void AudioClient::setNoiseReductionAutomatic(bool enable, bool emitSignal) { + if (_isNoiseReductionAutomatic != enable) { + _isNoiseReductionAutomatic = enable; + if (emitSignal) { + emit noiseReductionAutomaticChanged(_isNoiseReductionAutomatic); + } + } +} + +void AudioClient::setNoiseReductionThreshold(float threshold, bool emitSignal) { + if (_noiseReductionThreshold != threshold) { + _noiseReductionThreshold = threshold; + if (emitSignal) { + emit noiseReductionThresholdChanged(_noiseReductionThreshold); + } + } +} + void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) { if (_warnWhenMuted != enable) { _warnWhenMuted = enable; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 09abb2c356..f101d68a0e 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -217,6 +217,12 @@ public slots: void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true); bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; } + + void setNoiseReductionAutomatic(bool isNoiseGateAutomatic, bool emitSignal = true); + bool isNoiseReductionAutomatic() const { return _isNoiseReductionAutomatic; } + + void setNoiseReductionThreshold(float noiseReductionThreshold, bool emitSignal = true); + float noiseReductionThreshold() const { return _noiseReductionThreshold; } void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true); bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; } @@ -265,6 +271,8 @@ signals: void inputVolumeChanged(float volume); void muteToggled(bool muted); void noiseReductionChanged(bool noiseReductionEnabled); + void noiseReductionAutomaticChanged(bool noiseReductionAutomatic); + void noiseReductionThresholdChanged(bool noiseReductionThreshold); void warnWhenMutedChanged(bool warnWhenMutedEnabled); void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled); void mutedByMixer(); @@ -310,6 +318,8 @@ private: friend class CheckDevicesThread; friend class LocalInjectorsThread; + float loudnessToLevel(float loudness); + // background tasks void checkDevices(); void checkPeakValues(); @@ -397,6 +407,8 @@ private: bool _shouldEchoLocally{ false }; bool _shouldEchoToServer{ false }; bool _isNoiseGateEnabled{ true }; + bool _isNoiseReductionAutomatic{ true }; + float _noiseReductionThreshold{ 0.2f }; bool _warnWhenMuted; bool _isAECEnabled{ true }; From 5a40007758f409e719134c19630565505fbe8ab1 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Tue, 9 Feb 2021 00:46:46 -0500 Subject: [PATCH 05/86] Update default threshold value. --- interface/src/scripting/Audio.h | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 3addd6d5a4..cd1a2002ed 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -557,7 +557,7 @@ private: float _localInjectorGain { 0.0f }; // in dB float _systemInjectorGain { 0.0f }; // in dB float _pttOutputGainDesktop { 0.0f }; // in dB - float _noiseReductionThreshold { 0.2f }; // Match default value of AudioClient::_noiseReductionThreshold. + float _noiseReductionThreshold { 0.1f }; // Match default value of AudioClient::_noiseReductionThreshold. bool _isClipping { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _noiseReductionAutomatic { true }; // Match default value of AudioClient::_noiseReductionAutomatic. diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index f101d68a0e..a5de9bd4ca 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -408,7 +408,7 @@ private: bool _shouldEchoToServer{ false }; bool _isNoiseGateEnabled{ true }; bool _isNoiseReductionAutomatic{ true }; - float _noiseReductionThreshold{ 0.2f }; + float _noiseReductionThreshold{ 0.1f }; bool _warnWhenMuted; bool _isAECEnabled{ true }; From b6e7466804fc71728e2262fc5bc395344ea4d576 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 9 Feb 2021 17:14:13 -0800 Subject: [PATCH 06/86] triggerHapticPulse supports arbitrary device index instead of hands --- .../controllers/src/controllers/InputDevice.h | 2 +- .../src/controllers/ScriptingInterface.cpp | 16 +++++----- .../src/controllers/ScriptingInterface.h | 32 ++++++++++++------- .../src/controllers/UserInputMapper.cpp | 8 ++--- .../src/controllers/UserInputMapper.h | 4 +-- .../TouchscreenVirtualPadDevice.cpp | 2 +- .../TouchscreenVirtualPadDevice.h | 2 +- plugins/hifiSdl2/src/Joystick.cpp | 2 +- plugins/hifiSdl2/src/Joystick.h | 2 +- .../oculus/src/OculusControllerManager.cpp | 8 ++++- plugins/oculus/src/OculusControllerManager.h | 2 +- plugins/openvr/src/ViveControllerManager.cpp | 8 ++++- plugins/openvr/src/ViveControllerManager.h | 2 +- 13 files changed, 56 insertions(+), 34 deletions(-) diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 95d0524a4a..dc8e811f0c 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -118,7 +118,7 @@ public: const QString& getName() const { return _name; } // By default, Input Devices do not support haptics - virtual bool triggerHapticPulse(float strength, float duration, controller::Hand hand) { return false; } + virtual bool triggerHapticPulse(float strength, float duration, uint16_t index) { return false; } // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index fd32b2eb43..e9a831859d 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -134,13 +134,13 @@ namespace controller { return DependencyManager::get()->getActionNames(); } - bool ScriptingInterface::triggerHapticPulse(float strength, float duration, controller::Hand hand) const { - return DependencyManager::get()->triggerHapticPulse(strength, duration, hand); + bool ScriptingInterface::triggerHapticPulse(float strength, float duration, uint16_t index) const { + return DependencyManager::get()->triggerHapticPulse(strength, duration, index); } - bool ScriptingInterface::triggerShortHapticPulse(float strength, controller::Hand hand) const { + bool ScriptingInterface::triggerShortHapticPulse(float strength, uint16_t index) const { const float SHORT_HAPTIC_DURATION_MS = 250.0f; - return DependencyManager::get()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, hand); + return DependencyManager::get()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, index); } void ScriptingInterface::startInputRecording() { @@ -189,13 +189,13 @@ namespace controller { emit inputDeviceRunningChanged(deviceName, isRunning); } - bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand) const { - return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, duration, hand); + bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, uint16_t index) const { + return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, duration, index); } - bool ScriptingInterface::triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand) const { + bool ScriptingInterface::triggerShortHapticPulseOnDevice(unsigned int device, float strength, uint16_t index) const { const float SHORT_HAPTIC_DURATION_MS = 250.0f; - return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, hand); + return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, index); } void ScriptingInterface::updateMaps() { diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index a1875c7fe8..076146a79d 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -212,22 +212,28 @@ namespace controller { * @function Controller.triggerHapticPulse * @param {number} strength - The strength of the haptic pulse, range 0.01.0. * @param {number} duration - The duration of the haptic pulse, in milliseconds. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on devices on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. * @example Trigger a haptic pulse on the right hand. * var HAPTIC_STRENGTH = 0.5; * var HAPTIC_DURATION = 10; * var RIGHT_HAND = 1; * Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND); */ - Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, controller::Hand hand = BOTH) const; + Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, uint16_t index = 2) const; /**jsdoc * Triggers a 250ms haptic pulse on connected and enabled devices that have the capability. * @function Controller.triggerShortHapticPulse * @param {number} strength - The strength of the haptic pulse, range 0.01.0. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on devices on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. */ - Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const; + Q_INVOKABLE bool triggerShortHapticPulse(float strength, uint16_t index = 2) const; /**jsdoc * Triggers a haptic pulse on a particular device if connected and enabled and it has the capability. @@ -235,7 +241,10 @@ namespace controller { * @param {number} deviceID - The ID of the device to trigger the haptic pulse on. * @param {number} strength - The strength of the haptic pulse, range 0.01.0. * @param {number} duration - The duration of the haptic pulse, in milliseconds. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on this device on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. * @example Trigger a haptic pulse on an Oculus Touch controller. * var HAPTIC_STRENGTH = 0.5; * var deviceID = Controller.findDevice("OculusTouch"); @@ -243,19 +252,20 @@ namespace controller { * var RIGHT_HAND = 1; * Controller.triggerHapticPulseOnDevice(deviceID, HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND); */ - Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, - controller::Hand hand = BOTH) const; + Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, + uint16_t index = 2) const; /**jsdoc * Triggers a 250ms haptic pulse on a particular device if connected and enabled and it has the capability. * @function Controller.triggerShortHapticPulseOnDevice * @param {number} deviceID - The ID of the device to trigger the haptic pulse on. * @param {number} strength - The strength of the haptic pulse, range 0.01.0. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on this device on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. */ - Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand = BOTH) - const; - + Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, uint16_t index = 2) const; /**jsdoc * Creates a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 604a4f9c73..6333792b3f 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -365,19 +365,19 @@ Pose UserInputMapper::getPoseState(Action action) const { } -bool UserInputMapper::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool UserInputMapper::triggerHapticPulse(float strength, float duration, uint16_t index) { Locker locker(_lock); bool toReturn = false; for (const auto& device : _registeredDevices) { - toReturn = device.second->triggerHapticPulse(strength, duration, hand) || toReturn; + toReturn = device.second->triggerHapticPulse(strength, duration, index) || toReturn; } return toReturn; } -bool UserInputMapper::triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand) { +bool UserInputMapper::triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, uint16_t index) { Locker locker(_lock); if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { - return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, hand); + return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, index); } return false; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 79fcf6e64c..ee8b34193f 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -91,8 +91,8 @@ namespace controller { void setActionState(Action action, float value, bool valid = true); void deltaActionState(Action action, float delta, bool valid = true); void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; } - bool triggerHapticPulse(float strength, float duration, controller::Hand hand); - bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand); + bool triggerHapticPulse(float strength, float duration, uint16_t index); + bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, uint16_t index); static Input makeStandardInput(controller::StandardButtonChannel button); static Input makeStandardInput(controller::StandardAxisChannel axis); diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index 6bb743bad0..6ba2d0eeea 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -199,7 +199,7 @@ void TouchscreenVirtualPadDevice::InputDevice::update(float deltaTime, const con _axisStateMap.clear(); } -bool TouchscreenVirtualPadDevice::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool TouchscreenVirtualPadDevice::InputDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { auto& virtualPadManager = VirtualPad::Manager::instance(); virtualPadManager.requestHapticFeedback((int) duration); return true; diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h index 8c276fb66b..1fa46f8a24 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h @@ -71,7 +71,7 @@ protected: virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + virtual bool triggerHapticPulse(float strength, float duration, uint16_t index) override; virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; diff --git a/plugins/hifiSdl2/src/Joystick.cpp b/plugins/hifiSdl2/src/Joystick.cpp index ad0b459544..55a07e0ab9 100644 --- a/plugins/hifiSdl2/src/Joystick.cpp +++ b/plugins/hifiSdl2/src/Joystick.cpp @@ -72,7 +72,7 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { } } -bool Joystick::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool Joystick::triggerHapticPulse(float strength, float duration, uint16_t index) { if (SDL_HapticRumblePlay(_sdlHaptic, strength, duration) != 0) { return false; } diff --git a/plugins/hifiSdl2/src/Joystick.h b/plugins/hifiSdl2/src/Joystick.h index ae90470974..a839676468 100644 --- a/plugins/hifiSdl2/src/Joystick.h +++ b/plugins/hifiSdl2/src/Joystick.h @@ -39,7 +39,7 @@ public: virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; Joystick() : InputDevice("GamePad") {} ~Joystick(); diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp index c1431fa4b2..4e22b7a236 100644 --- a/plugins/oculus/src/OculusControllerManager.cpp +++ b/plugins/oculus/src/OculusControllerManager.cpp @@ -369,7 +369,13 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const pose = pose.transform(controllerToAvatar); } -bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { + if (index > 2) { + return false; + } + + controller::Hand hand = (controller::Hand)index; + Locker locker(_lock); bool toReturn = true; ovr::withSession([&](ovrSession session) { diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index 7d1d176a56..6a3a10f18a 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -76,7 +76,7 @@ private: void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; private: void stopHapticPulse(bool leftHand); diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 3629698e11..4483955fc5 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -1559,7 +1559,13 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = pose.transform(controllerToAvatar); } -bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { + if (index > 2) { + return false; + } + + controller::Hand hand = (controller::Hand)index; + Locker locker(_lock); if (hand == controller::BOTH || hand == controller::LEFT) { if (strength == 0.0f) { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 956b2b5eaf..1b8c2a2ec5 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -112,7 +112,7 @@ private: QString getDefaultMappingConfig() const override; void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; void hapticsHelper(float deltaTime, bool leftHand); void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration); void calibrate(const controller::InputCalibrationData& inputCalibration); From 085bf8423c4ec2bafc8cdbd621a4b37b5537f1cd Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 11 Feb 2021 05:55:08 -0500 Subject: [PATCH 07/86] Update APIs and add Noise Reduction section to Audio app. --- interface/resources/qml/hifi/audio/Audio.qml | 220 +++++++++++++++---- interface/src/scripting/Audio.cpp | 2 +- interface/src/scripting/Audio.h | 16 +- 3 files changed, 195 insertions(+), 43 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 31f3ee44df..9f08f1bd3b 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -104,6 +104,11 @@ Rectangle { AudioScriptingInterface.setSystemInjectorGain(sliderValue); } } + function updateNoiseReductionThresholdFromQML(sliderValue) { + if (AudioScriptingInterface.getNoiseReductionThreshold() != sliderValue) { + AudioScriptingInterface.setNoiseReductionThreshold(sliderValue); + } + } Component.onCompleted: { enablePeakValues(); @@ -164,7 +169,7 @@ Rectangle { x: 2 * margins.paddings; width: parent.width; // switch heights + 2 * top margins - height: (root.switchHeight) * 6 + 48; + height: (bar.currentIndex === 0) ? (root.switchHeight) * 2 + 48 : (root.switchHeight) * 4 + 48; anchors.top: firstSeparator.bottom; anchors.topMargin: 10; @@ -175,6 +180,7 @@ Rectangle { height: parent.height; anchors.top: parent.top anchors.left: parent.left; + HifiControlsUit.Switch { id: muteMic; height: root.switchHeight; @@ -193,45 +199,11 @@ Rectangle { } } - HifiControlsUit.Switch { - id: noiseReductionSwitch; - height: root.switchHeight; - switchWidth: root.switchWidth; - anchors.top: muteMic.bottom; - anchors.topMargin: 24 - anchors.left: parent.left - labelTextOn: "Noise Reduction"; - labelTextSize: 16; - backgroundOnColor: "#E3E3E3"; - checked: AudioScriptingInterface.noiseReduction; - onCheckedChanged: { - AudioScriptingInterface.noiseReduction = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding - } - } - - HifiControlsUit.Switch { - id: acousticEchoCancellationSwitch; - height: root.switchHeight; - switchWidth: root.switchWidth; - anchors.top: noiseReductionSwitch.bottom - anchors.topMargin: 24 - anchors.left: parent.left - labelTextOn: "Echo Cancellation"; - labelTextSize: 16; - backgroundOnColor: "#E3E3E3"; - checked: AudioScriptingInterface.acousticEchoCancellation; - onCheckedChanged: { - AudioScriptingInterface.acousticEchoCancellation = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.acousticEchoCancellation; }); - } - } - HifiControlsUit.Switch { id: pttSwitch height: root.switchHeight; switchWidth: root.switchWidth; - anchors.top: acousticEchoCancellationSwitch.bottom; + anchors.top: muteMic.bottom; anchors.topMargin: 24 anchors.left: parent.left labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk"); @@ -254,6 +226,7 @@ Rectangle { height: parent.height; anchors.top: parent.top anchors.left: switchContainer.right; + HifiControlsUit.Switch { id: warnMutedSwitch height: root.switchHeight; @@ -271,7 +244,6 @@ Rectangle { } } - HifiControlsUit.Switch { id: audioLevelSwitch height: root.switchHeight; @@ -519,13 +491,181 @@ Rectangle { anchors.top: systemInjectorGainContainer.bottom; anchors.topMargin: 10; } + + Item { + id: noiseReductionHeader + 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("Noise Reduction"); + } + } + + Item { + id: noiseReductionSwitches; + x: 2 * margins.paddings; + width: parent.width; + // switch heights + 2 * top margins + height: (root.switchHeight) * 5 + 48; + anchors.top: noiseReductionHeader.bottom; + anchors.topMargin: 0; + + Item { + id: noiseReductionSwitchContainer; + x: margins.paddings; + width: parent.width / 2; + height: parent.height; + anchors.top: parent.top; + anchors.left: parent.left; + + HifiControlsUit.Switch { + id: acousticEchoCancellationSwitch; + height: root.switchHeight; + switchWidth: root.switchWidth; + anchors.top: noiseReductionSwitchContainer.top + anchors.topMargin: 24 + anchors.left: parent.left + labelTextOn: "Echo Cancellation"; + labelTextSize: 16; + backgroundOnColor: "#E3E3E3"; + checked: AudioScriptingInterface.acousticEchoCancellation; + onCheckedChanged: { + AudioScriptingInterface.acousticEchoCancellation = checked; + checked = Qt.binding(function() { return AudioScriptingInterface.acousticEchoCancellation; }); + } + } + + HifiControlsUit.Switch { + id: noiseReductionSwitch; + height: root.switchHeight; + switchWidth: root.switchWidth; + anchors.top: acousticEchoCancellationSwitch.bottom; + anchors.topMargin: 24 + anchors.left: parent.left + labelTextOn: "Noise Reduction"; + labelTextSize: 16; + backgroundOnColor: "#E3E3E3"; + checked: AudioScriptingInterface.noiseReduction; + onCheckedChanged: { + AudioScriptingInterface.noiseReduction = checked; + checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding + } + } + + HifiControlsUit.Switch { + id: noiseReductionAutomaticSwitch; + height: root.switchHeight; + switchWidth: root.switchWidth; + anchors.top: noiseReductionSwitch.bottom; + anchors.topMargin: 24; + anchors.left: parent.left; + labelTextOn: "Noise Reduction: Manual/Automatic"; + labelTextSize: 16; + backgroundOnColor: "#E3E3E3"; + checked: AudioScriptingInterface.noiseReductionAutomatic; + onCheckedChanged: { + AudioScriptingInterface.noiseReductionAutomatic = checked; + checked = Qt.binding(function() { return AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding + } + } + } + } + + Item { + id: noiseReductionThresholdContainer + x: margins.paddings; + anchors.top: noiseReductionSwitches.bottom; + anchors.topMargin: 16; + width: parent.width - margins.paddings*2; + height: avatarGainSliderTextMetrics.height; + visible: AudioScriptingInterface.noiseReductionAutomatic !== true; + + HifiControlsUit.Slider { + id: noiseReductionThresholdSlider + anchors.right: parent.right + height: parent.height + width: 200 + minimumValue: 0.0 + maximumValue: 1.0 + stepSize: 0.1 + value: AudioScriptingInterface.getNoiseReductionThreshold() + onValueChanged: { + updateNoiseReductionThresholdFromQML(value); + } + onPressedChanged: { + if (!pressed) { + updateNoiseReductionThresholdFromQML(value); + } + } + + MouseArea { + anchors.fill: parent + onWheel: { + // Do nothing. + } + onDoubleClicked: { + noiseReductionThresholdSlider.value = 0.0 + } + onPressed: { + // Pass through to Slider + mouse.accepted = false + } + onReleased: { + // the above mouse.accepted seems to make this + // never get called, nonetheless... + mouse.accepted = false + } + } + } + TextMetrics { + id: noiseReductionThresholdSliderTextMetrics + text: noiseReductionThresholdSliderText.text + font: noiseReductionThresholdSliderText.font + } + RalewayRegular { + // The slider for my card is special, it controls the master gain + id: noiseReductionThresholdSliderText; + text: "Audio input threshold"; + size: 16; + anchors.left: parent.left; + color: hifi.colors.white; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignTop; + } + } + + Separator { + id: thirdSeparator; + anchors.top: noiseReductionThresholdContainer.bottom; + anchors.topMargin: 14; + } Item { id: inputDeviceHeader x: margins.paddings; width: parent.width - margins.paddings*2; height: 36; - anchors.top: secondSeparator.bottom; + anchors.top: thirdSeparator.bottom; anchors.topMargin: 10; HiFiGlyphs { @@ -609,7 +749,7 @@ Rectangle { } Separator { - id: thirdSeparator; + id: fourthSeparator; anchors.top: inputView.bottom; anchors.topMargin: 10; } @@ -617,7 +757,7 @@ Rectangle { Item { id: outputDeviceHeader; anchors.topMargin: 10; - anchors.top: thirdSeparator.bottom; + anchors.top: fourthSeparator.bottom; x: margins.paddings; width: parent.width - margins.paddings*2 height: 36 diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 36045bd8ec..d890069a61 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -314,7 +314,7 @@ void Audio::enableNoiseReductionAutomatic(bool enable) { } } -float Audio::getNoiseReductionThreshold() const { +float Audio::getNoiseReductionThreshold() { return resultWithReadLock([&] { return _noiseReductionThreshold; }); diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index cd1a2002ed..de66f6edf1 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -127,7 +127,6 @@ public: bool isMuted() const; bool noiseReductionEnabled() const; bool noiseReductionAutomatic() const; - float getNoiseReductionThreshold() const; bool warnWhenMutedEnabled() const; bool acousticEchoCancellationEnabled() const; float getInputVolume() const; @@ -274,6 +273,20 @@ public: * @returns {number} The injector gain (dB) in the client. */ Q_INVOKABLE float getSystemInjectorGain(); + + /**jsdoc + * Sets the noise gate threshold before your mic audio is transmitted. (Applies only if Audio.noiseReductionAutomatic is off.) + * @function Audio.setNoiseReductionThreshold + * @param {number} threshold - The level that your input must surpass to be transmitted. (0 - 1.0) + */ + Q_INVOKABLE void setNoiseReductionThreshold(float threshold); + + /**jsdoc + * Gets the noise reduction threshold. + * @function Audio.getNoiseReductionThreshold + * @returns {number} The noise reduction threshold. (0 - 1.0) + */ + Q_INVOKABLE float getNoiseReductionThreshold(); /**jsdoc * Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format. @@ -535,7 +548,6 @@ private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); void enableNoiseReductionAutomatic(bool enable); - void setNoiseReductionThreshold(float threshold); void enableWarnWhenMuted(bool enable); void enableAcousticEchoCancellation(bool enable); void setInputVolume(float volume); From 5b1549a24e1f41674453d377758e71befe51a013 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 11 Feb 2021 05:59:08 -0500 Subject: [PATCH 08/86] Fix typo + update JSDoc. --- interface/src/scripting/Audio.cpp | 2 +- interface/src/scripting/Audio.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index d890069a61..ddcaef653c 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -332,7 +332,7 @@ void Audio::setNoiseReductionThreshold(float threshold) { } }); if (changed) { - emit noiseReductionAutomaticChanged(threshold); + emit noiseReductionThresholdChanged(threshold); } } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index de66f6edf1..ae4e0f9d1a 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -277,14 +277,14 @@ public: /**jsdoc * Sets the noise gate threshold before your mic audio is transmitted. (Applies only if Audio.noiseReductionAutomatic is off.) * @function Audio.setNoiseReductionThreshold - * @param {number} threshold - The level that your input must surpass to be transmitted. (0 - 1.0) + * @param {number} threshold - The level that your input must surpass to be transmitted. 0.0 (open the gate completely) – 1.0 */ Q_INVOKABLE void setNoiseReductionThreshold(float threshold); /**jsdoc * Gets the noise reduction threshold. * @function Audio.getNoiseReductionThreshold - * @returns {number} The noise reduction threshold. (0 - 1.0) + * @returns {number} The noise reduction threshold. 0.01.0 */ Q_INVOKABLE float getNoiseReductionThreshold(); From 97fdd372af408f98e97fe595bdb25df194c4cb0d Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 11 Feb 2021 06:09:28 -0500 Subject: [PATCH 09/86] Fix Noise Reduction visibility in Audio.qml --- interface/resources/qml/hifi/audio/Audio.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 9f08f1bd3b..1d4b6f8a92 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -583,6 +583,7 @@ Rectangle { labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.noiseReductionAutomatic; + visible: AudioScriptingInterface.noiseReduction === true; onCheckedChanged: { AudioScriptingInterface.noiseReductionAutomatic = checked; checked = Qt.binding(function() { return AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding @@ -598,7 +599,7 @@ Rectangle { anchors.topMargin: 16; width: parent.width - margins.paddings*2; height: avatarGainSliderTextMetrics.height; - visible: AudioScriptingInterface.noiseReductionAutomatic !== true; + visible: AudioScriptingInterface.noiseReduction === true && AudioScriptingInterface.noiseReductionAutomatic !== true; HifiControlsUit.Slider { id: noiseReductionThresholdSlider From 799590a0fccde8342bd64f7e96855b1db2bf1412 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 11 Feb 2021 20:41:47 -0500 Subject: [PATCH 10/86] Add input level + gate indicator. --- interface/resources/qml/hifi/audio/Audio.qml | 103 +++++++++++++++++-- 1 file changed, 93 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 1d4b6f8a92..f98bce6b70 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -15,6 +15,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit @@ -598,17 +599,17 @@ Rectangle { anchors.top: noiseReductionSwitches.bottom; anchors.topMargin: 16; width: parent.width - margins.paddings*2; - height: avatarGainSliderTextMetrics.height; + height: avatarGainSliderTextMetrics.height + 10; visible: AudioScriptingInterface.noiseReduction === true && AudioScriptingInterface.noiseReductionAutomatic !== true; HifiControlsUit.Slider { id: noiseReductionThresholdSlider anchors.right: parent.right - height: parent.height + height: noiseReductionThresholdSliderTextMetrics.height width: 200 minimumValue: 0.0 maximumValue: 1.0 - stepSize: 0.1 + stepSize: 0.05 value: AudioScriptingInterface.getNoiseReductionThreshold() onValueChanged: { updateNoiseReductionThresholdFromQML(value); @@ -653,6 +654,88 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignTop; } + + Item { + id: noisePeak + anchors.right: parent.right + anchors.rightMargin: 5; + anchors.top: noiseReductionThresholdSlider.bottom; + + width: noiseReductionThresholdSlider.width - 10; + height: 8; + + Text { + id: status; + + anchors { + horizontalCenter: parent.horizontalCenter; + verticalCenter: parent.verticalCenter; + } + + visible: AudioScriptingInterface.muted; + color: "#E2334D"; + + text: "MUTED"; + font.pointSize: 10; + } + + Item { + id: noiseBar; + + property bool gated: false; + property var level: AudioScriptingInterface.inputLevel; + + width: parent.width; + height: parent.height; + + anchors { fill: parent } + + visible: !status.visible; + + Component.onCompleted: { + AudioScriptingInterface.noiseGateOpened.connect(function() { noiseBar.gated = false; }); + AudioScriptingInterface.noiseGateClosed.connect(function() { noiseBar.gated = true; }); + AudioScriptingInterface.inputLevelChanged.connect(function() { noiseBar.level = AudioScriptingInterface.inputLevel; }); + } + + Rectangle { // base + radius: 4; + anchors { fill: parent } + color: colors.gutter; + } + + Rectangle { // noiseMask + id: noiseMask; + width: parent.width * noiseBar.level; + radius: 5; + anchors { + bottom: parent.bottom; + bottomMargin: 0; + top: parent.top; + topMargin: 0; + left: parent.left; + leftMargin: 0; + } + } + + LinearGradient { + anchors { fill: noiseMask } + source: noiseMask + start: Qt.point(0, 0); + end: Qt.point(noiseBar.width, 0); + gradient: Gradient { + GradientStop { + position: 0; + color: noiseBar.gated ? "#E2334D" : "#39A38F"; + } + GradientStop { + position: 1; + color: noiseBar.gated ? "#E2334D" : "#39A38F"; + } + } + } + } + } } Separator { @@ -738,13 +821,13 @@ Rectangle { } } 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; + 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; } } } From a18c24832a33a33d95ad4f7465e15cdc0141bf60 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 11 Feb 2021 18:01:33 -0800 Subject: [PATCH 11/86] fix android build --- .../src/OculusMobileControllerManager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp b/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp index 54a796954e..ec744ce039 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp +++ b/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp @@ -161,7 +161,7 @@ public: QString getDefaultMappingConfig() const override; void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; private: void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, @@ -516,12 +516,16 @@ void OculusMobileInputDevice::handleRotationForUntrackedHand(const controller::I pose = pose.transform(controllerToAvatar); } -bool OculusMobileInputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool OculusMobileInputDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { + if (index > 2) { + return false; + } + + controller::Hand hand = (controller::Hand)index; + Locker locker(_lock); bool success = true; - qDebug()<<"AAAA: Haptic duration %f " << duration; - if (hand == controller::BOTH || hand == controller::LEFT) { success &= _hands[0].setHapticFeedback(strength, duration); } From 51bf54c7039440089e4180264ba04b7236d723a5 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Feb 2021 12:27:35 -0800 Subject: [PATCH 12/86] fix EntityItem::contains sphere case --- libraries/entities/src/EntityItem.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9d3947ab56..e3112c1265 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1764,8 +1764,7 @@ bool EntityItem::contains(const glm::vec3& point) const { // therefore we must do math using an unscaled localPoint relative to sphere center glm::vec3 dimensions = getScaledDimensions(); glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot())); - const float HALF_SQUARED = 0.25f; - return glm::length2(localPoint) < HALF_SQUARED * glm::length2(dimensions); + return glm::length2(localPoint) < glm::length2(0.5f * glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z))); } // we transform into the "normalized entity-frame" where the bounding box is centered on the origin From 88b9ebb11aa50ee290e54655254e6731aea5406e Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sat, 13 Feb 2021 17:17:27 -0500 Subject: [PATCH 13/86] Update Audio.qml "Noise Reduction: Manual/Automatic" -> "Manual Noise Reduction" --- interface/resources/qml/hifi/audio/Audio.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index f98bce6b70..b4e42d276f 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -580,14 +580,14 @@ Rectangle { anchors.top: noiseReductionSwitch.bottom; anchors.topMargin: 24; anchors.left: parent.left; - labelTextOn: "Noise Reduction: Manual/Automatic"; + labelTextOn: "Manual Noise Reduction"; labelTextSize: 16; backgroundOnColor: "#E3E3E3"; - checked: AudioScriptingInterface.noiseReductionAutomatic; + checked: !AudioScriptingInterface.noiseReductionAutomatic; visible: AudioScriptingInterface.noiseReduction === true; onCheckedChanged: { - AudioScriptingInterface.noiseReductionAutomatic = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding + AudioScriptingInterface.noiseReductionAutomatic = !checked; + checked = Qt.binding(function() { return !AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding } } } @@ -663,7 +663,7 @@ Rectangle { width: noiseReductionThresholdSlider.width - 10; height: 8; - + Text { id: status; From f9b88c68e06d1336089890878d04c1b09255a0ca Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 15 Feb 2021 21:13:58 -0800 Subject: [PATCH 14/86] treat non-uniform spheres as ellipsoids everywhere --- libraries/entities/src/EntityItem.cpp | 10 ++++----- libraries/entities/src/ZoneEntityItem.cpp | 21 +++++++++++++------ libraries/physics/src/ShapeFactory.cpp | 7 +------ libraries/shared/src/ShapeInfo.cpp | 7 +------ .../html/js/entityProperties.js | 2 +- .../create/modules/entityShapeVisualizer.js | 9 +------- 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index e3112c1265..ffd21d4f5c 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1759,12 +1759,11 @@ bool EntityItem::contains(const glm::vec3& point) const { ShapeType shapeType = getShapeType(); if (shapeType == SHAPE_TYPE_SPHERE) { - // SPHERE case is special: - // anything with shapeType == SPHERE must collide as a bounding sphere in the world-frame regardless of dimensions - // therefore we must do math using an unscaled localPoint relative to sphere center glm::vec3 dimensions = getScaledDimensions(); - glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot())); - return glm::length2(localPoint) < glm::length2(0.5f * glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z))); + if (dimensions.x == dimensions.y && dimensions.y == dimensions.z) { + glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot())); + return glm::length2(localPoint) < glm::length2(0.5f * dimensions.x); + } } // we transform into the "normalized entity-frame" where the bounding box is centered on the origin @@ -1791,6 +1790,7 @@ bool EntityItem::contains(const glm::vec3& point) const { localPoint = glm::abs(localPoint); return glm::all(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE))); } + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { // since we've transformed into the normalized space this is just a sphere-point intersection test return glm::length2(localPoint) <= NORMALIZED_RADIUS_SQUARED; diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 3eabfb4f1e..abccdf7167 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -270,7 +270,6 @@ void ZoneEntityItem::debugDump() const { } void ZoneEntityItem::setShapeType(ShapeType type) { - ShapeType oldShapeType = _shapeType; switch(type) { case SHAPE_TYPE_NONE: case SHAPE_TYPE_CAPSULE_X: @@ -288,7 +287,12 @@ void ZoneEntityItem::setShapeType(ShapeType type) { default: break; } - _shapeType = type; + + ShapeType oldShapeType; + withWriteLock([&] { + oldShapeType = _shapeType; + _shapeType = type; + }); if (type == SHAPE_TYPE_COMPOUND) { if (type != oldShapeType) { @@ -300,16 +304,21 @@ void ZoneEntityItem::setShapeType(ShapeType type) { } ShapeType ZoneEntityItem::getShapeType() const { - return _shapeType; + return resultWithReadLock([&] { + return _shapeType; + }); } void ZoneEntityItem::setCompoundShapeURL(const QString& url) { - QString oldCompoundShapeURL = _compoundShapeURL; + QString oldCompoundShapeURL; + ShapeType shapeType; withWriteLock([&] { + oldCompoundShapeURL = _compoundShapeURL; _compoundShapeURL = url; + shapeType = _shapeType; }); if (oldCompoundShapeURL != url) { - if (_shapeType == SHAPE_TYPE_COMPOUND) { + if (shapeType == SHAPE_TYPE_COMPOUND) { fetchCollisionGeometryResource(); } else { _shapeResource.reset(); @@ -333,7 +342,7 @@ bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, c bool ZoneEntityItem::contains(const glm::vec3& point) const { GeometryResource::Pointer resource = _shapeResource; - if (_shapeType == SHAPE_TYPE_COMPOUND && resource) { + if (getShapeType() == SHAPE_TYPE_COMPOUND && resource) { if (resource->isLoaded()) { const HFMModel& hfmModel = resource->getHFMModel(); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index ef5213df8f..5ac33f65bd 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -278,12 +278,6 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - case SHAPE_TYPE_SPHERE: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - shape = new btSphereShape(radius); - } - break; case SHAPE_TYPE_MULTISPHERE: { std::vector positions; std::vector radiuses; @@ -297,6 +291,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape->setMargin(MULTI_SPHERE_MARGIN); } break; + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index c60d1c2574..f9d41022d7 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -115,12 +115,6 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString case SHAPE_TYPE_HULL: case SHAPE_TYPE_MULTISPHERE: break; - case SHAPE_TYPE_SPHERE: { - float radius = glm::length(halfExtents) / SQUARE_ROOT_OF_3; - radius = glm::max(radius, MIN_HALF_EXTENT); - _halfExtents = glm::vec3(radius); - } - break; case SHAPE_TYPE_CIRCLE: { _halfExtents = glm::vec3(_halfExtents.x, MIN_HALF_EXTENT, _halfExtents.z); } @@ -228,6 +222,7 @@ float ShapeInfo::computeVolume() const { volume = 8.0f * _halfExtents.x * _halfExtents.y * _halfExtents.z; break; } + case SHAPE_TYPE_ELLIPSOID: case SHAPE_TYPE_SPHERE: { volume = 4.0f * PI * _halfExtents.x * _halfExtents.y * _halfExtents.z / 3.0f; break; diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index f9eb52a6bd..648b6a2bbc 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -281,7 +281,7 @@ const GROUPS = [ { label: "Shape Type", type: "dropdown", - options: { "box": "Box", "sphere": "Sphere", "ellipsoid": "Ellipsoid", + options: { "box": "Box", "sphere": "Sphere", "cylinder-y": "Cylinder", "compound": "Use Compound Shape URL" }, propertyID: "zoneShapeType", propertyName: "shapeType", // actual entity property name diff --git a/scripts/system/create/modules/entityShapeVisualizer.js b/scripts/system/create/modules/entityShapeVisualizer.js index 7a1cd74d25..8f218f3555 100644 --- a/scripts/system/create/modules/entityShapeVisualizer.js +++ b/scripts/system/create/modules/entityShapeVisualizer.js @@ -13,6 +13,7 @@ var SHAPETYPE_TO_SHAPE = { "box": "Cube", "ellipsoid": "Sphere", + "sphere": "Sphere", "cylinder-y": "Cylinder", }; @@ -35,14 +36,6 @@ function getEntityShapePropertiesForType(properties) { modelURL: properties.compoundShapeURL, localDimensions: properties.localDimensions }; - } else if (properties.shapeType === "sphere") { - var sphereDiameter = Math.max(properties.localDimensions.x, properties.localDimensions.y, - properties.localDimensions.z); - return { - type: "Sphere", - modelURL: properties.compoundShapeURL, - localDimensions: {x: sphereDiameter, y: sphereDiameter, z: sphereDiameter} - }; } break; } From 7e054ddbc45003300521b3e99e01474fbee26277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Gro=C3=9F?= Date: Wed, 17 Feb 2021 02:14:52 +0100 Subject: [PATCH 15/86] Remove nvtt workaround since it has been taken care of by vcpkg for years --- BUILD_LINUX.md | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 90782e90b4..e9c5047e38 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -168,39 +168,3 @@ If your goal is to set up a development environment, it is desirable to set the directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable. For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`. By default, vcpkg will build in the system `/tmp` directory. - -##### Ubuntu 18.04 only - -In Ubuntu 18.04 there is a problem related with NVidia driver library version. - -It can be worked around following these steps: - -1. Uninstall incompatible nvtt libraries: -`sudo apt-get remove libnvtt2 libnvtt-dev` - -1. Install libssl1.0-dev: -`sudo apt-get -y install libssl1.0-dev` - -1. Clone castano nvidia-texture-tools: -`git clone https://github.com/castano/nvidia-texture-tools` -`cd nvidia-texture-tools/` - -1. Make these changes in repo: -* In file **VERSION** set `2.2.1` -* In file **configure**: - * set `build="release"` - * set `-DNVTT_SHARED=1` - -1. Configure, build and install: -`./configure` -`make` -`sudo make install` - -1. Link compiled files: -`sudo ln -s /usr/local/lib/libnvcore.so /usr/lib/libnvcore.so` -`sudo ln -s /usr/local/lib/libnvimage.so /usr/lib/libnvimage.so` -`sudo ln -s /usr/local/lib/libnvmath.so /usr/lib/libnvmath.so` -`sudo ln -s /usr/local/lib/libnvtt.so /usr/lib/libnvtt.so` - -1. After running these steps you can run interface: -`interface/interface` From 72725e783a837c7e59744bdde95cef99890342a5 Mon Sep 17 00:00:00 2001 From: Phil Palmer Date: Fri, 19 Feb 2021 21:55:08 -0500 Subject: [PATCH 16/86] Small optimisations and tidying of things that were noticed in passing. No behaviour should change. * Removed the deprecated MyAvatar.setToggleHips script function and the "Toggle Hips Following" option from the Developer menu. They had no effect on any code. * In CharacterController::applyMotor, prevented unnecessary calls to btVector3::rotate() when the motor has no rotation. This change also improves readability through the use of clearly-named lambdas. * In AvatarData::getFauxJointIndex, prevented unnecessary string comparisons when the named joint is a real joint rather than a faux one. * In Avatar::getJointIndex, removed an unnecessary call to QHash::contains(), by supplying a default index for QHash::value(). * Removed unnecessary condition "forwardLeanAmount < 0" in MyAvatar::FollowHelper::shouldActivateHorizontal_userSitting. * Corrected the return type of MyAvatar::getSitStandStateChange from float to bool. * Added a missing 'f' suffix to a float literal in PreferencesDialog.cpp. --- interface/src/Menu.cpp | 2 -- interface/src/Menu.h | 1 - interface/src/avatar/MyAvatar.cpp | 17 ++------- interface/src/avatar/MyAvatar.h | 11 +----- interface/src/ui/PreferencesDialog.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 4 +-- libraries/avatars/src/AvatarData.cpp | 4 +++ libraries/physics/src/CharacterController.cpp | 35 ++++++++++++++----- 8 files changed, 36 insertions(+), 40 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 471645e342..3a320cc628 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -556,8 +556,6 @@ Menu::Menu() { }); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ToggleHipsFollowing, 0, false, - avatar.get(), SLOT(setToggleHips(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBaseOfSupport, 0, false, avatar.get(), SLOT(setEnableDebugDrawBaseOfSupport(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cac8e77f9e..5cd4c2112e 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -211,7 +211,6 @@ namespace MenuOption { const QString ThirdPerson = "Third Person Legacy"; const QString ThreePointCalibration = "3 Point Calibration"; const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp - const QString ToggleHipsFollowing = "Toggle Hips Following"; const QString ToolWindow = "Tool Window"; const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0f66f3bb41..c40974ac87 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1392,18 +1392,6 @@ float loadSetting(Settings& settings, const QString& name, float defaultValue) { return value; } -void MyAvatar::setToggleHips(bool followHead) { - _follow.setToggleHipsFollowing(followHead); -} - -void MyAvatar::FollowHelper::setToggleHipsFollowing(bool followHead) { - _toggleHipsFollowing = followHead; -} - -bool MyAvatar::FollowHelper::getToggleHipsFollowing() const { - return _toggleHipsFollowing; -} - void MyAvatar::setEnableDebugDrawBaseOfSupport(bool isEnabled) { _enableDebugDrawBaseOfSupport = isEnabled; } @@ -2030,7 +2018,6 @@ void MyAvatar::loadData() { allowAvatarLeaningPreferenceStrings[static_cast(AllowAvatarLeaningPreference::Default)]))); setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible)); - _follow.setToggleHipsFollowing (Menu::getInstance()->isOptionChecked(MenuOption::ToggleHipsFollowing)); setEnableDebugDrawBaseOfSupport(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBaseOfSupport)); setEnableDebugDrawDefaultPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawDefaultPose)); setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose)); @@ -5514,7 +5501,7 @@ void MyAvatar::setSitStandStateChange(bool stateChanged) { } // Determine if the user's real-world sit/stand state has changed. -float MyAvatar::getSitStandStateChange() const { +bool MyAvatar::getSitStandStateChange() const { return _sitStandStateChange; } @@ -5696,7 +5683,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal_userSitting(const MyAvatar bool stepDetected = false; if (forwardLeanAmount > MAX_FORWARD_LEAN) { stepDetected = true; - } else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) { + } else if (forwardLeanAmount < -MAX_BACKWARD_LEAN) { stepDetected = true; } else { stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3d278cf983..bb7ebb62ba 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1800,7 +1800,7 @@ public: void setAnalogPlusSprintSpeed(float value); float getAnalogPlusSprintSpeed() const; void setSitStandStateChange(bool stateChanged); - float getSitStandStateChange() const; + bool getSitStandStateChange() const; void updateSitStandState(float newHeightReading, float dt); QVector getScriptUrls(); @@ -2181,13 +2181,6 @@ public slots: */ Q_INVOKABLE void updateMotionBehaviorFromMenu(); - /**jsdoc - * @function MyAvatar.setToggleHips - * @param {boolean} enabled - Enabled. - * @deprecated This function is deprecated and will be removed. - */ - void setToggleHips(bool followHead); - /**jsdoc * Displays the base of support area debug graphics if in HMD mode. If your head goes outside this area your avatar's hips * are moved to counterbalance your avatar, and if your head moves too far then your avatar's position is moved (i.e., a @@ -2907,8 +2900,6 @@ private: void setForceActivateVertical(bool val); bool getForceActivateHorizontal() const; void setForceActivateHorizontal(bool val); - bool getToggleHipsFollowing() const; - void setToggleHipsFollowing(bool followHead); std::atomic _forceActivateRotation { false }; std::atomic _forceActivateVertical { false }; std::atomic _forceActivateHorizontal { false }; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9c53060f31..b78c0989e2 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -364,7 +364,7 @@ void setupPreferences() { auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Camera Sensitivity", getter, setter); preference->setMin(0.01f); preference->setMax(5.0f); - preference->setStep(0.1); + preference->setStep(0.1f); preference->setDecimals(2); preferences->addPreference(preference); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index f361e15999..7d8b313377 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1444,9 +1444,7 @@ int Avatar::getJointIndex(const QString& name) const { } withValidJointIndicesCache([&]() { - if (_modelJointIndicesCache.contains(name)) { - result = _modelJointIndicesCache.value(name) - 1; - } + result = _modelJointIndicesCache.value(name, result + 1) - 1; }); return result; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index adb7222ee3..9245f1a4ae 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1938,6 +1938,10 @@ void AvatarData::clearJointsData() { } int AvatarData::getFauxJointIndex(const QString& name) const { + static constexpr QChar fauxJointFirstChar('_');// The first character of all the faux joint names. + if (!name.startsWith(fauxJointFirstChar)) { + return -1; + }; if (name == "_SENSOR_TO_WORLD_MATRIX") { return SENSOR_TO_WORLD_MATRIX_INDEX; } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 777a3d3f87..2eaf771ae2 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -696,10 +696,29 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel return; } - // rotate into motor-frame + const bool motorHasRotation = !(motor.rotation == btQuaternion::getIdentity()); btVector3 axis = motor.rotation.getAxis(); btScalar angle = motor.rotation.getAngle(); - btVector3 velocity = worldVelocity.rotate(axis, -angle); + + // Rotate a vector from motor frame to world frame + auto rotateToWorldFrame = [&axis, &angle, &motorHasRotation](const btVector3 vectorInMotorFrame) { + if (motorHasRotation) { + return vectorInMotorFrame.rotate(axis, angle); + } else { + return vectorInMotorFrame; + } + }; + + // Rotate a vector from world frame to motor frame + auto rotateToMotorFrame = [&axis, &angle, &motorHasRotation](const btVector3 vectorInWorldFrame) { + if (motorHasRotation) { + return vectorInWorldFrame.rotate(axis, -angle); + } else { + return vectorInWorldFrame; + } + }; + + btVector3 velocity = rotateToMotorFrame(worldVelocity); int32_t collisionMask = computeCollisionMask(); if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS || @@ -712,15 +731,15 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel velocity += tau * (motor.velocity - velocity); // rotate back into world-frame - velocity = velocity.rotate(axis, angle); - _targetVelocity += (tau * motor.velocity).rotate(axis, angle); + velocity = rotateToWorldFrame(velocity); + _targetVelocity += rotateToWorldFrame(tau * motor.velocity); // store the velocity and weight velocities.push_back(velocity); weights.push_back(tau); } else { // compute local UP - btVector3 up = _currentUp.rotate(axis, -angle); + btVector3 up = rotateToMotorFrame(_currentUp); btVector3 motorVelocity = motor.velocity; // save these non-adjusted components for later @@ -729,7 +748,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel if (_stepHeight > _minStepHeight && !_steppingUp) { // there is a step --> compute velocity direction to go over step - btVector3 motorVelocityWF = motorVelocity.rotate(axis, angle); + btVector3 motorVelocityWF = rotateToWorldFrame(motorVelocity); if (motorVelocityWF.dot(_stepNormal) < 0.0f) { // the motor pushes against step _steppingUp = true; @@ -764,8 +783,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel } // add components back together and rotate into world-frame - velocity = (hVelocity + vVelocity).rotate(axis, angle); - _targetVelocity += maxTau * (hTargetVelocity + vTargetVelocity).rotate(axis, angle); + velocity = rotateToWorldFrame(hVelocity + vVelocity); + _targetVelocity += maxTau * rotateToWorldFrame(hTargetVelocity + vTargetVelocity); // store velocity and weights velocities.push_back(velocity); From 30cf1555e414d43e0a7ccd58b973e8da528db483 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 20 Feb 2021 22:47:33 -0800 Subject: [PATCH 17/86] fix keepAspectRatio, give text entities naturalDimensions --- .../src/RenderableImageEntityItem.cpp | 35 +++++++++++++++---- libraries/entities/src/ImageEntityItem.cpp | 9 ++++- libraries/entities/src/ImageEntityItem.h | 4 +++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index e03655f09c..dd6381953f 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -59,10 +59,22 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); + bool nextTextureLoaded = _texture && (_texture->isLoaded() || _texture->isFailed()); if (!_textureIsLoaded) { emit requestRenderUpdate(); + if (nextTextureLoaded) { + float width = _texture->getWidth(); + float height = _texture->getHeight(); + glm::vec3 naturalDimensions = glm::vec3(1.0f, 1.0f, 0.01f); + if (width < height) { + naturalDimensions.x = width / height; + } else { + naturalDimensions.y = height / width; + } + entity->setNaturalDimension(naturalDimensions); + } } - _textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed()); + _textureIsLoaded = nextTextureLoaded; } ShapeKey ImageEntityRenderer::getShapeKey() { @@ -100,7 +112,6 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); - batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); float imageWidth = _texture->getWidth(); @@ -125,15 +136,25 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { fromImage.setHeight(scaleY * _subImage.height()); } - float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f; - float y = _keepAspectRatio ? fromImage.height() / (2.0f * maxSize) : 0.5f; - glm::vec2 texCoordBottomLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + fromImage.height() - 0.5f) / imageHeight); glm::vec2 texCoordTopRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight); + if (_keepAspectRatio) { + glm::vec3 scale = transform.getScale(); + float targetAspectRatio = imageWidth / imageHeight; + float currentAspectRatio = scale.x / scale.y; + + if (targetAspectRatio < currentAspectRatio) { + scale.x *= targetAspectRatio / currentAspectRatio; + } else { + scale.y /= targetAspectRatio / currentAspectRatio; + } + transform.setScale(scale); + } + batch->setModelTransform(transform); + DependencyManager::get()->renderQuad( - *batch, glm::vec2(-x, -y), glm::vec2(x, y), texCoordBottomLeft, texCoordTopRight, + *batch, glm::vec2(-0.5f), glm::vec2(0.5f), texCoordBottomLeft, texCoordTopRight, color, _geometryId ); diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index afa6d9ae69..41cdde7676 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -34,6 +34,7 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); withReadLock([&] { _pulseProperties.getProperties(properties); + properties.setNaturalDimensions(_naturalDimensions); }); COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); @@ -217,4 +218,10 @@ PulsePropertyGroup ImageEntityItem::getPulseProperties() const { return resultWithReadLock([&] { return _pulseProperties; }); -} \ No newline at end of file +} + +void ImageEntityItem::setNaturalDimension(const glm::vec3& naturalDimensions) const { + withWriteLock([&] { + _naturalDimensions = naturalDimensions; + }); +} diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h index 4f7aac0c13..0cc4eae05a 100644 --- a/libraries/entities/src/ImageEntityItem.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -63,6 +63,8 @@ public: PulsePropertyGroup getPulseProperties() const; + void setNaturalDimension(const glm::vec3& naturalDimensions) const; + protected: glm::u8vec3 _color; float _alpha; @@ -72,6 +74,8 @@ protected: bool _emissive { false }; bool _keepAspectRatio { true }; QRect _subImage; + + mutable glm::vec3 _naturalDimensions; }; #endif // hifi_ImageEntityItem_h From 5677b5d5dcf7c79c3a4b157d818838ad7d2c49b1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 20 Feb 2021 23:03:30 -0800 Subject: [PATCH 18/86] docs --- libraries/entities/src/EntityItemProperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5f925c4b19..44a9019197 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -706,7 +706,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise * {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise + * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model or image if it has one, otherwise * {@link Vec3(0)|Vec3.ONE}. Read-only. * * @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates. From 87b94c8b14b86fbbbd00d12e39acec180ca2c7ad Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Sun, 21 Feb 2021 17:25:06 -0500 Subject: [PATCH 19/86] Update INSTALL.md --- INSTALL.md | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index a0966e3fc5..5abd7e8e2c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,6 +1,6 @@ # Creating an Installer -*Last Updated on December 2, 2020* +*Last Updated on February 21, 2021* Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. @@ -105,47 +105,60 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ##### Ubuntu | .deb 1. Ensure you are using an Ubuntu 18.04 system. -1. Set up Vircadia Builder to compile the server. +2. Get and bootstrap Vircadia Builder. ``` - git clone https://github.com/kasenvr/vircadia-builder.git + git clone https://github.com/vircadia/vircadia-builder.git cd vircadia-builder chmod +x vircadia-builder - ./vircadia-builder ``` -1. Build the server. +3. Set up Vircadia Builder to compile the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. ``` ./vircadia-builder --build server ``` -1. Navigate to the `pkg-scripts` directory. +4. Build the server. + ``` + ./vircadia-builder --build server + ``` +5. Navigate to the `pkg-scripts` directory. ``` cd ../Vircadia/source/pkg-scripts/ ``` -1. Generate the .deb package. +6. Generate the .deb package. ``` DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server ``` -1. If successful, the generated .deb package will be in the `pkg-scripts` folder. +7. If successful, the generated .deb package will be in the `pkg-scripts` folder. ##### Amazon Linux 2 | .rpm 1. Ensure you are using an Amazon Linux 2 system. -1. Set up Vircadia Builder to compile the server. +2. Update the system and install dependencies. ``` - git clone https://github.com/kasenvr/vircadia-builder.git + sudo yum update -y + sudo yum install git -y + sudo yum install rpm-build + ``` +3. Get and bootstrap Vircadia Builder. + ``` + git clone https://github.com/vircadia/vircadia-builder.git cd vircadia-builder + sudo ./install_amazon_linux_deps.sh chmod +x vircadia-builder - ./vircadia-builder ``` -1. Build the server. +4. Set up Vircadia Builder to compile the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. ``` ./vircadia-builder --build server ``` -1. Navigate to the `pkg-scripts` directory. +5. Build the server. + ``` + ./vircadia-builder --build server + ``` +6. Navigate to the `pkg-scripts` directory. ``` cd ../Vircadia/source/pkg-scripts/ ``` -1. Generate the .rpm package. +7. Generate the .rpm package. ``` ./make-rpm-server ``` -1. If successful, the generated .rpm package will be in the `pkg-scripts` folder. +8. If successful, the generated .rpm package will be in the `pkg-scripts` folder. From 94192a5657357c923aa1a8bda969476924cc32f3 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Sun, 21 Feb 2021 19:45:14 -0500 Subject: [PATCH 20/86] Update INSTALL.md --- INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 5abd7e8e2c..403605bdc8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -125,7 +125,7 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ``` 6. Generate the .deb package. ``` - DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server + DEBVERSION="Semver e.g. 2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server ``` 7. If successful, the generated .deb package will be in the `pkg-scripts` folder. @@ -159,6 +159,6 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ``` 7. Generate the .rpm package. ``` - ./make-rpm-server + RPMVERSION="Semver e.g. 2021.1.0" ./make-rpm-server ``` 8. If successful, the generated .rpm package will be in the `pkg-scripts` folder. From 82436b6f333b6eca13da9807af66510a42bb4dae Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Mon, 22 Feb 2021 03:14:22 -0500 Subject: [PATCH 21/86] Update INSTALL.md --- INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 403605bdc8..6f58ec7b81 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -123,7 +123,7 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ``` cd ../Vircadia/source/pkg-scripts/ ``` -6. Generate the .deb package. +6. Generate the .deb package. Set `DEBVERSION` to a valid semantic version value e.g. "2021.1.0". ``` DEBVERSION="Semver e.g. 2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server ``` @@ -157,7 +157,7 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ``` cd ../Vircadia/source/pkg-scripts/ ``` -7. Generate the .rpm package. +7. Generate the .rpm package. Set `RPMVERSION` to a valid semantic version value e.g. "2021.1.0". ``` RPMVERSION="Semver e.g. 2021.1.0" ./make-rpm-server ``` From 0a6e220c745e19f706d745c8ab4fd65493fab96c Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Tue, 23 Feb 2021 20:29:46 -0500 Subject: [PATCH 22/86] Apply suggestions from code review Co-authored-by: David Rowe --- interface/resources/qml/hifi/audio/Audio.qml | 32 ++++++++++---------- interface/src/scripting/Audio.h | 7 ++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index b4e42d276f..1f20486e1e 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -106,7 +106,7 @@ Rectangle { } } function updateNoiseReductionThresholdFromQML(sliderValue) { - if (AudioScriptingInterface.getNoiseReductionThreshold() != sliderValue) { + if (AudioScriptingInterface.getNoiseReductionThreshold() !== sliderValue) { AudioScriptingInterface.setNoiseReductionThreshold(sliderValue); } } @@ -496,7 +496,7 @@ Rectangle { Item { id: noiseReductionHeader x: margins.paddings; - width: parent.width - margins.paddings*2; + width: parent.width - margins.paddings * 2; height: 36; anchors.top: secondSeparator.bottom; anchors.topMargin: 10; @@ -506,7 +506,7 @@ Rectangle { 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.leftMargin: -size / 4; // The glyph has empty space at left about 25% anchors.verticalCenter: parent.verticalCenter; size: 30; } @@ -552,7 +552,7 @@ Rectangle { checked: AudioScriptingInterface.acousticEchoCancellation; onCheckedChanged: { AudioScriptingInterface.acousticEchoCancellation = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.acousticEchoCancellation; }); + checked = Qt.binding(function () { return AudioScriptingInterface.acousticEchoCancellation; }); } } @@ -569,7 +569,7 @@ Rectangle { checked: AudioScriptingInterface.noiseReduction; onCheckedChanged: { AudioScriptingInterface.noiseReduction = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding + checked = Qt.binding(function () { return AudioScriptingInterface.noiseReduction; }); // restore binding } } @@ -584,10 +584,10 @@ Rectangle { labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: !AudioScriptingInterface.noiseReductionAutomatic; - visible: AudioScriptingInterface.noiseReduction === true; + visible: AudioScriptingInterface.noiseReduction; onCheckedChanged: { AudioScriptingInterface.noiseReductionAutomatic = !checked; - checked = Qt.binding(function() { return !AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding + checked = Qt.binding(function () { return !AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding } } } @@ -598,9 +598,9 @@ Rectangle { x: margins.paddings; anchors.top: noiseReductionSwitches.bottom; anchors.topMargin: 16; - width: parent.width - margins.paddings*2; + width: parent.width - margins.paddings * 2; height: avatarGainSliderTextMetrics.height + 10; - visible: AudioScriptingInterface.noiseReduction === true && AudioScriptingInterface.noiseReductionAutomatic !== true; + visible: AudioScriptingInterface.noiseReduction && !AudioScriptingInterface.noiseReductionAutomatic; HifiControlsUit.Slider { id: noiseReductionThresholdSlider @@ -626,16 +626,16 @@ Rectangle { // Do nothing. } onDoubleClicked: { - noiseReductionThresholdSlider.value = 0.0 + noiseReductionThresholdSlider.value = 0.0; } onPressed: { // Pass through to Slider - mouse.accepted = false + mouse.accepted = false; } onReleased: { // the above mouse.accepted seems to make this // never get called, nonetheless... - mouse.accepted = false + mouse.accepted = false; } } } @@ -693,9 +693,9 @@ Rectangle { visible: !status.visible; Component.onCompleted: { - AudioScriptingInterface.noiseGateOpened.connect(function() { noiseBar.gated = false; }); - AudioScriptingInterface.noiseGateClosed.connect(function() { noiseBar.gated = true; }); - AudioScriptingInterface.inputLevelChanged.connect(function() { noiseBar.level = AudioScriptingInterface.inputLevel; }); + AudioScriptingInterface.noiseGateOpened.connect(function () { noiseBar.gated = false; }); + AudioScriptingInterface.noiseGateClosed.connect(function () { noiseBar.gated = true; }); + AudioScriptingInterface.inputLevelChanged.connect(function () { noiseBar.level = AudioScriptingInterface.inputLevel; }); } Rectangle { // base @@ -908,4 +908,4 @@ Rectangle { } } } -} \ No newline at end of file +} diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index ae4e0f9d1a..078e7561a8 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -275,7 +275,7 @@ public: Q_INVOKABLE float getSystemInjectorGain(); /**jsdoc - * Sets the noise gate threshold before your mic audio is transmitted. (Applies only if Audio.noiseReductionAutomatic is off.) + * Sets the noise gate threshold before your mic audio is transmitted. (Applies only if Audio.noiseReductionAutomatic is false.) * @function Audio.setNoiseReductionThreshold * @param {number} threshold - The level that your input must surpass to be transmitted. 0.0 (open the gate completely) – 1.0 */ @@ -419,14 +419,13 @@ signals: /**jsdoc * Triggered when the audio input noise reduction mode is changed. * @function Audio.noiseReductionAutomaticChanged - * @param {boolean} isEnabled - true if audio input noise reduction automatic mode is enabled, otherwise false. - * This means the mode is set to 'manual'. + * @param {boolean} isEnabled - true if audio input noise reduction automatic mode is enabled, false if in manual mode. * @returns {Signal} */ void noiseReductionAutomaticChanged(bool isEnabled); /**jsdoc - * Triggered when audio input noise reduction threshold is changed. + * Triggered when the audio input noise reduction threshold is changed. * @function Audio.noiseReductionThresholdChanged * @param {number} threshold - The threshold for the audio input noise reduction, range 0.0 (open the gate completely) – 1.0 * (close the gate completely). From 83c8b71fa5e76fbb1abab231df0cbe73cc63a4b4 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Wed, 24 Feb 2021 00:58:50 -0500 Subject: [PATCH 23/86] Add missing JSDocs. --- interface/src/scripting/Audio.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 078e7561a8..2b068b195c 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -50,6 +50,11 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * @property {boolean} noiseReduction - true if noise reduction is enabled, otherwise false. When * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just * above the noise floor. + * @property {boolean} noiseReductionAutomatic - true if audio input noise reduction automatic mode is enabled, + * false if in manual mode. Manual mode will allow you to use Audio.noiseReductionAutomatic + * to set a manual sensitivity for the noise gate. + * @property {number} noiseReductionThreshold - Sets the noise gate threshold before your mic audio is transmitted. + * (Applies only if Audio.noiseReductionAutomatic is false.) * @property {number} inputVolume - Adjusts the volume of the input audio, range 0.01.0. * If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some * devices, and others might only support values of 0.0 and 1.0. From 61defe74ce1348e77fe144d63b4686b3af6a3af3 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Wed, 24 Feb 2021 15:32:36 -0500 Subject: [PATCH 24/86] Update interface/src/scripting/Audio.h Co-authored-by: David Rowe --- interface/src/scripting/Audio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 2b068b195c..707c8aac33 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -51,7 +51,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just * above the noise floor. * @property {boolean} noiseReductionAutomatic - true if audio input noise reduction automatic mode is enabled, - * false if in manual mode. Manual mode will allow you to use Audio.noiseReductionAutomatic + * false if in manual mode. Manual mode allows you to use Audio.noiseReductionThreshold * to set a manual sensitivity for the noise gate. * @property {number} noiseReductionThreshold - Sets the noise gate threshold before your mic audio is transmitted. * (Applies only if Audio.noiseReductionAutomatic is false.) From 5ad2f9d1921f00d65292653e068258a41b5ec149 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 26 Feb 2021 09:05:52 +1300 Subject: [PATCH 25/86] Fix OSX crash on shutdown --- interface/src/Application.cpp | 2 ++ libraries/script-engine/src/ScriptEngine.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 06ce6c3d6c..eec1efbd99 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2989,6 +2989,8 @@ Application::~Application() { qInstallMessageHandler(LogHandler::verboseMessageHandler); #ifdef Q_OS_MAC + // 26 Feb 2021 - Tried re-enabling this call but OSX still crashes on exit. + // // 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not // fully understood whether it might cause other unknown crashes (B). // diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e553672b02..9fbf7a2801 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -470,7 +470,10 @@ void ScriptEngine::waitTillDoneRunning(bool shutdown) { // We should never be waiting (blocking) on our own thread assert(workerThread != QThread::currentThread()); -#ifdef Q_OS_MAC +#if 0 + // 26 Feb 2021 - Disabled this OSX-specific code because it causes OSX to crash on shutdown; without this code, OSX + // doesn't crash on shutdown. Qt 5.12.3 and Qt 5.15.2. + // // On mac, don't call QCoreApplication::processEvents() here. This is to prevent // [NSApplication terminate:] from prematurely destroying the static destructors // while we are waiting for the scripts to shutdown. We will pump the message From 080d42be39c8de9d9615ba99329e4c73370ec758 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 25 Feb 2021 20:35:02 -0500 Subject: [PATCH 26/86] Update AnimInverseKinematics.cpp --- libraries/animation/src/AnimInverseKinematics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 2fd52ee036..aa306532b7 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -756,7 +756,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani int index = target.getIndex(); int endIndex = _skeleton->getParentIndex(_hipsIndex); - while (index != endIndex) { + while (index != endIndex && index > -1) { AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index); float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength; From c79eb479f0041cad672b85a0dec923b84667b26d Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 25 Feb 2021 20:36:45 -0500 Subject: [PATCH 27/86] Update AnimInverseKinematics.cpp --- libraries/animation/src/AnimInverseKinematics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index aa306532b7..dfd0084155 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -756,7 +756,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani int index = target.getIndex(); int endIndex = _skeleton->getParentIndex(_hipsIndex); - while (index != endIndex && index > -1) { + while (index != endIndex && index != -1) { AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index); float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength; From 4276ea295c7b7f13bd2ec707010083f2489ab069 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 25 Feb 2021 21:29:54 -0500 Subject: [PATCH 28/86] Change -1 to NO_PARENT_INDEX. --- .../animation/src/AnimInverseKinematics.cpp | 53 ++++++++++--------- libraries/animation/src/AnimSkeleton.cpp | 18 +++---- libraries/animation/src/AnimSkeleton.h | 2 + 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index dfd0084155..0b1162d2b2 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -22,6 +22,7 @@ #include "AnimationLogging.h" #include "CubicHermiteSpline.h" #include "AnimUtil.h" +#include "AnimSkeleton.h" static const int MAX_TARGET_MARKERS = 30; static const float JOINT_CHAIN_INTERP_TIME = 0.5f; @@ -66,7 +67,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, cons poleVectorVar(poleVectorVarIn), weight(weightIn), numFlexCoefficients(flexCoefficientsIn.size()), - jointIndex(-1) + jointIndex(NO_PARENT_INDEX) { numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS); for (size_t i = 0; i < numFlexCoefficients; i++) { @@ -172,14 +173,14 @@ bool debounceJointWarnings() { void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) { - _hipsTargetIndex = -1; + _hipsTargetIndex = NO_PARENT_INDEX; targets.reserve(_targetVarVec.size()); for (auto& targetVar : _targetVarVec) { // update targetVar jointIndex cache - if (targetVar.jointIndex == -1) { + if (targetVar.jointIndex == NO_PARENT_INDEX) { int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName); if (jointIndex >= 0) { // this targetVar has a valid joint --> cache the indices @@ -190,7 +191,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } IKTarget target; - if (targetVar.jointIndex != -1) { + if (targetVar.jointIndex != NO_PARENT_INDEX) { target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); target.setIndex(targetVar.jointIndex); if (target.getType() != IKTarget::Type::Unknown) { @@ -329,7 +330,7 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // update the absolutePoses for (int i = 0; i < (int)_relativePoses.size(); ++i) { auto parentIndex = _skeleton->getParentIndex((int)i); - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -351,12 +352,12 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // finally set the relative rotation of each tip to agree with absolute target rotation for (auto& target: targets) { int tipIndex = target.getIndex(); - int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : -1; + int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : NO_PARENT_INDEX; int chainIndex = targetToChainMap[tipIndex]; bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f; float alpha = needsInterpolation ? getInterpolationAlpha(_prevJointChainInfoVec[chainIndex].timer) : 0.0f; // update rotationOnly targets that don't lie on the ik chain of other ik targets. - if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() && + if (parentIndex != NO_PARENT_INDEX && !_rotationAccumulators[tipIndex].isDirty() && (target.getType() == IKTarget::Type::RotationOnly || target.getType() == IKTarget::Type::Unknown)) { if (target.getType() == IKTarget::Type::RotationOnly) { const glm::quat& targetRotation = target.getRotation(); @@ -434,11 +435,11 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const int tipIndex = target.getIndex(); int pivotIndex = _skeleton->getParentIndex(tipIndex); - if (pivotIndex == -1 || pivotIndex == _hipsIndex) { + if (pivotIndex == NO_PARENT_INDEX || pivotIndex == _hipsIndex) { return; } int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); - if (pivotsParentIndex == -1) { + if (pivotsParentIndex == NO_PARENT_INDEX) { // TODO?: handle case where tip's parent is root? return; } @@ -485,7 +486,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const chainDepth++; // descend toward root, pivoting each joint to get tip closer to target position - while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) { + while (pivotIndex != _hipsIndex && pivotsParentIndex != NO_PARENT_INDEX) { assert(chainDepth < jointChainInfoOut.jointInfoVec.size()); @@ -579,12 +580,12 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const if (target.getPoleVectorEnabled()) { int topJointIndex = target.getIndex(); int midJointIndex = _skeleton->getParentIndex(topJointIndex); - if (midJointIndex != -1) { + if (midJointIndex != NO_PARENT_INDEX) { int baseJointIndex = _skeleton->getParentIndex(midJointIndex); - if (baseJointIndex != -1) { + if (baseJointIndex != NO_PARENT_INDEX) { int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex); AnimPose topPose, midPose, basePose; - int topChainIndex = -1, baseChainIndex = -1; + int topChainIndex = NO_PARENT_INDEX, baseChainIndex = NO_PARENT_INDEX; const size_t MAX_CHAIN_DEPTH = 30; AnimPose postAbsPoses[MAX_CHAIN_DEPTH]; AnimPose accum = absolutePoses[_hipsIndex]; @@ -756,7 +757,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani int index = target.getIndex(); int endIndex = _skeleton->getParentIndex(_hipsIndex); - while (index != endIndex && index != -1) { + while (index != endIndex && index != NO_PARENT_INDEX) { AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index); float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength; @@ -1460,7 +1461,7 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele // invalidate all targetVars for (auto& targetVar: _targetVarVec) { - targetVar.jointIndex = -1; + targetVar.jointIndex = NO_PARENT_INDEX; } for (auto& accumulator: _rotationAccumulators) { @@ -1480,18 +1481,18 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele if (_hipsIndex >= 0) { _hipsParentIndex = _skeleton->getParentIndex(_hipsIndex); } else { - _hipsParentIndex = -1; + _hipsParentIndex = NO_PARENT_INDEX; } _leftHandIndex = _skeleton->nameToJointIndex("LeftHand"); _rightHandIndex = _skeleton->nameToJointIndex("RightHand"); } else { clearConstraints(); - _headIndex = -1; - _hipsIndex = -1; - _hipsParentIndex = -1; - _leftHandIndex = -1; - _rightHandIndex = -1; + _headIndex = NO_PARENT_INDEX; + _hipsIndex = NO_PARENT_INDEX; + _hipsParentIndex = NO_PARENT_INDEX; + _leftHandIndex = NO_PARENT_INDEX; + _rightHandIndex = NO_PARENT_INDEX; } } @@ -1531,7 +1532,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } @@ -1580,7 +1581,7 @@ void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInf DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE); // draw line to parent - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); glm::vec4 color = GRAY; @@ -1629,13 +1630,13 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } glm::quat parentAbsRot; - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { parentAbsRot = poses[parentIndex].rot(); } @@ -1733,7 +1734,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC const float MIN_AXIS_LENGTH = 1.0e-4f; for (auto& target : targets) { - if (target.getIndex() != -1 && target.getType() == IKTarget::Type::RotationAndPosition) { + if (target.getIndex() != NO_PARENT_INDEX && target.getType() == IKTarget::Type::RotationAndPosition) { for (int i = 0; i < NUM_LIMBS; i++) { if (limbs[i].first == target.getIndex()) { int tipIndex = limbs[i].first; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index b26d00d8d0..609cfdbca6 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -66,7 +66,7 @@ int AnimSkeleton::nameToJointIndex(const QString& jointName) const { if (_jointIndicesByName.end() != itr) { return itr.value(); } - return -1; + return NO_PARENT_INDEX; } int AnimSkeleton::getNumJoints() const { @@ -80,7 +80,7 @@ int AnimSkeleton::getChainDepth(int jointIndex) const { do { chainDepth++; index = _parentIndices[index]; - } while (index != -1); + } while (index != NO_PARENT_INDEX); return chainDepth; } else { return 0; @@ -108,7 +108,7 @@ const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const { std::vector AnimSkeleton::getChildrenOfJoint(int jointIndex) const { // Children and grandchildren, etc. std::vector result; - if (jointIndex != -1) { + if (jointIndex != NO_PARENT_INDEX) { for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) { if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) { result.push_back(i); @@ -135,7 +135,7 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { int lastIndex = std::min((int)poses.size(), _jointsSize); for (int i = 0; i < lastIndex; ++i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { poses[i] = poses[parentIndex] * poses[i]; } } @@ -146,7 +146,7 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const { int lastIndex = std::min((int)poses.size(), _jointsSize); for (int i = lastIndex - 1; i >= 0; --i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { poses[i] = poses[parentIndex].inverse() * poses[i]; } } @@ -157,7 +157,7 @@ void AnimSkeleton::convertRelativeRotationsToAbsolute(std::vector& ro int lastIndex = std::min((int)rotations.size(), _jointsSize); for (int i = 0; i < lastIndex; ++i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { rotations[i] = rotations[parentIndex] * rotations[i]; } } @@ -168,7 +168,7 @@ void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector& ro int lastIndex = std::min((int)rotations.size(), _jointsSize); for (int i = lastIndex - 1; i >= 0; --i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != NO_PARENT_INDEX) { rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i]; } } @@ -280,7 +280,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // so we can restore them after a future mirror operation _nonMirroredIndices.push_back(i); } - int mirrorJointIndex = -1; + int mirrorJointIndex = NO_PARENT_INDEX; if (_joints[i].name.startsWith("Left")) { QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); mirrorJointIndex = nameToJointIndex(mirrorJointName); @@ -350,7 +350,7 @@ std::vector AnimSkeleton::lookUpJointIndices(const std::vector& jo result.reserve(jointNames.size()); for (auto& name : jointNames) { int index = nameToJointIndex(name); - if (index == -1) { + if (index == NO_PARENT_INDEX) { qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with name " << name; } result.push_back(index); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index efc1c1599f..c7452c3f38 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -30,6 +30,8 @@ public: const QString& getJointName(int jointIndex) const; int getNumJoints() const; int getChainDepth(int jointIndex) const; + + static const int NO_PARENT_INDEX { -1 }; // the default poses are the orientations of the joints on frame 0. const AnimPose& getRelativeDefaultPose(int jointIndex) const; From 0e03eccbdff66255d4764988cf3963012486cb70 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 25 Feb 2021 21:43:17 -0500 Subject: [PATCH 29/86] Change NO_PARENT_INDEX to INVALID_JOINT_INDEX. --- .../animation/src/AnimInverseKinematics.cpp | 52 +++++++++---------- libraries/animation/src/AnimSkeleton.cpp | 18 +++---- libraries/animation/src/AnimSkeleton.h | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 0b1162d2b2..cd4558d5b7 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -67,7 +67,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, cons poleVectorVar(poleVectorVarIn), weight(weightIn), numFlexCoefficients(flexCoefficientsIn.size()), - jointIndex(NO_PARENT_INDEX) + jointIndex(INVALID_JOINT_INDEX) { numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS); for (size_t i = 0; i < numFlexCoefficients; i++) { @@ -173,14 +173,14 @@ bool debounceJointWarnings() { void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) { - _hipsTargetIndex = NO_PARENT_INDEX; + _hipsTargetIndex = INVALID_JOINT_INDEX; targets.reserve(_targetVarVec.size()); for (auto& targetVar : _targetVarVec) { // update targetVar jointIndex cache - if (targetVar.jointIndex == NO_PARENT_INDEX) { + if (targetVar.jointIndex == INVALID_JOINT_INDEX) { int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName); if (jointIndex >= 0) { // this targetVar has a valid joint --> cache the indices @@ -191,7 +191,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } IKTarget target; - if (targetVar.jointIndex != NO_PARENT_INDEX) { + if (targetVar.jointIndex != INVALID_JOINT_INDEX) { target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); target.setIndex(targetVar.jointIndex); if (target.getType() != IKTarget::Type::Unknown) { @@ -330,7 +330,7 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // update the absolutePoses for (int i = 0; i < (int)_relativePoses.size(); ++i) { auto parentIndex = _skeleton->getParentIndex((int)i); - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -352,12 +352,12 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // finally set the relative rotation of each tip to agree with absolute target rotation for (auto& target: targets) { int tipIndex = target.getIndex(); - int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : NO_PARENT_INDEX; + int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : INVALID_JOINT_INDEX; int chainIndex = targetToChainMap[tipIndex]; bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f; float alpha = needsInterpolation ? getInterpolationAlpha(_prevJointChainInfoVec[chainIndex].timer) : 0.0f; // update rotationOnly targets that don't lie on the ik chain of other ik targets. - if (parentIndex != NO_PARENT_INDEX && !_rotationAccumulators[tipIndex].isDirty() && + if (parentIndex != INVALID_JOINT_INDEX && !_rotationAccumulators[tipIndex].isDirty() && (target.getType() == IKTarget::Type::RotationOnly || target.getType() == IKTarget::Type::Unknown)) { if (target.getType() == IKTarget::Type::RotationOnly) { const glm::quat& targetRotation = target.getRotation(); @@ -435,11 +435,11 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const int tipIndex = target.getIndex(); int pivotIndex = _skeleton->getParentIndex(tipIndex); - if (pivotIndex == NO_PARENT_INDEX || pivotIndex == _hipsIndex) { + if (pivotIndex == INVALID_JOINT_INDEX || pivotIndex == _hipsIndex) { return; } int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); - if (pivotsParentIndex == NO_PARENT_INDEX) { + if (pivotsParentIndex == INVALID_JOINT_INDEX) { // TODO?: handle case where tip's parent is root? return; } @@ -486,7 +486,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const chainDepth++; // descend toward root, pivoting each joint to get tip closer to target position - while (pivotIndex != _hipsIndex && pivotsParentIndex != NO_PARENT_INDEX) { + while (pivotIndex != _hipsIndex && pivotsParentIndex != INVALID_JOINT_INDEX) { assert(chainDepth < jointChainInfoOut.jointInfoVec.size()); @@ -580,12 +580,12 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const if (target.getPoleVectorEnabled()) { int topJointIndex = target.getIndex(); int midJointIndex = _skeleton->getParentIndex(topJointIndex); - if (midJointIndex != NO_PARENT_INDEX) { + if (midJointIndex != INVALID_JOINT_INDEX) { int baseJointIndex = _skeleton->getParentIndex(midJointIndex); - if (baseJointIndex != NO_PARENT_INDEX) { + if (baseJointIndex != INVALID_JOINT_INDEX) { int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex); AnimPose topPose, midPose, basePose; - int topChainIndex = NO_PARENT_INDEX, baseChainIndex = NO_PARENT_INDEX; + int topChainIndex = INVALID_JOINT_INDEX, baseChainIndex = INVALID_JOINT_INDEX; const size_t MAX_CHAIN_DEPTH = 30; AnimPose postAbsPoses[MAX_CHAIN_DEPTH]; AnimPose accum = absolutePoses[_hipsIndex]; @@ -757,7 +757,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani int index = target.getIndex(); int endIndex = _skeleton->getParentIndex(_hipsIndex); - while (index != endIndex && index != NO_PARENT_INDEX) { + while (index != endIndex && index != INVALID_JOINT_INDEX) { AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index); float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength; @@ -1461,7 +1461,7 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele // invalidate all targetVars for (auto& targetVar: _targetVarVec) { - targetVar.jointIndex = NO_PARENT_INDEX; + targetVar.jointIndex = INVALID_JOINT_INDEX; } for (auto& accumulator: _rotationAccumulators) { @@ -1481,18 +1481,18 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele if (_hipsIndex >= 0) { _hipsParentIndex = _skeleton->getParentIndex(_hipsIndex); } else { - _hipsParentIndex = NO_PARENT_INDEX; + _hipsParentIndex = INVALID_JOINT_INDEX; } _leftHandIndex = _skeleton->nameToJointIndex("LeftHand"); _rightHandIndex = _skeleton->nameToJointIndex("RightHand"); } else { clearConstraints(); - _headIndex = NO_PARENT_INDEX; - _hipsIndex = NO_PARENT_INDEX; - _hipsParentIndex = NO_PARENT_INDEX; - _leftHandIndex = NO_PARENT_INDEX; - _rightHandIndex = NO_PARENT_INDEX; + _headIndex = INVALID_JOINT_INDEX; + _hipsIndex = INVALID_JOINT_INDEX; + _hipsParentIndex = INVALID_JOINT_INDEX; + _leftHandIndex = INVALID_JOINT_INDEX; + _rightHandIndex = INVALID_JOINT_INDEX; } } @@ -1532,7 +1532,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } @@ -1581,7 +1581,7 @@ void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInf DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE); // draw line to parent - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); glm::vec4 color = GRAY; @@ -1630,13 +1630,13 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } glm::quat parentAbsRot; - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { parentAbsRot = poses[parentIndex].rot(); } @@ -1734,7 +1734,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC const float MIN_AXIS_LENGTH = 1.0e-4f; for (auto& target : targets) { - if (target.getIndex() != NO_PARENT_INDEX && target.getType() == IKTarget::Type::RotationAndPosition) { + if (target.getIndex() != INVALID_JOINT_INDEX && target.getType() == IKTarget::Type::RotationAndPosition) { for (int i = 0; i < NUM_LIMBS; i++) { if (limbs[i].first == target.getIndex()) { int tipIndex = limbs[i].first; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 609cfdbca6..bd338aae4a 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -66,7 +66,7 @@ int AnimSkeleton::nameToJointIndex(const QString& jointName) const { if (_jointIndicesByName.end() != itr) { return itr.value(); } - return NO_PARENT_INDEX; + return INVALID_JOINT_INDEX; } int AnimSkeleton::getNumJoints() const { @@ -80,7 +80,7 @@ int AnimSkeleton::getChainDepth(int jointIndex) const { do { chainDepth++; index = _parentIndices[index]; - } while (index != NO_PARENT_INDEX); + } while (index != INVALID_JOINT_INDEX); return chainDepth; } else { return 0; @@ -108,7 +108,7 @@ const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const { std::vector AnimSkeleton::getChildrenOfJoint(int jointIndex) const { // Children and grandchildren, etc. std::vector result; - if (jointIndex != NO_PARENT_INDEX) { + if (jointIndex != INVALID_JOINT_INDEX) { for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) { if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) { result.push_back(i); @@ -135,7 +135,7 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { int lastIndex = std::min((int)poses.size(), _jointsSize); for (int i = 0; i < lastIndex; ++i) { int parentIndex = _parentIndices[i]; - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { poses[i] = poses[parentIndex] * poses[i]; } } @@ -146,7 +146,7 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const { int lastIndex = std::min((int)poses.size(), _jointsSize); for (int i = lastIndex - 1; i >= 0; --i) { int parentIndex = _parentIndices[i]; - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { poses[i] = poses[parentIndex].inverse() * poses[i]; } } @@ -157,7 +157,7 @@ void AnimSkeleton::convertRelativeRotationsToAbsolute(std::vector& ro int lastIndex = std::min((int)rotations.size(), _jointsSize); for (int i = 0; i < lastIndex; ++i) { int parentIndex = _parentIndices[i]; - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { rotations[i] = rotations[parentIndex] * rotations[i]; } } @@ -168,7 +168,7 @@ void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector& ro int lastIndex = std::min((int)rotations.size(), _jointsSize); for (int i = lastIndex - 1; i >= 0; --i) { int parentIndex = _parentIndices[i]; - if (parentIndex != NO_PARENT_INDEX) { + if (parentIndex != INVALID_JOINT_INDEX) { rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i]; } } @@ -280,7 +280,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // so we can restore them after a future mirror operation _nonMirroredIndices.push_back(i); } - int mirrorJointIndex = NO_PARENT_INDEX; + int mirrorJointIndex = INVALID_JOINT_INDEX; if (_joints[i].name.startsWith("Left")) { QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); mirrorJointIndex = nameToJointIndex(mirrorJointName); @@ -350,7 +350,7 @@ std::vector AnimSkeleton::lookUpJointIndices(const std::vector& jo result.reserve(jointNames.size()); for (auto& name : jointNames) { int index = nameToJointIndex(name); - if (index == NO_PARENT_INDEX) { + if (index == INVALID_JOINT_INDEX) { qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with name " << name; } result.push_back(index); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index c7452c3f38..e36ceb4042 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -31,7 +31,7 @@ public: int getNumJoints() const; int getChainDepth(int jointIndex) const; - static const int NO_PARENT_INDEX { -1 }; + static const int INVALID_JOINT_INDEX { -1 }; // the default poses are the orientations of the joints on frame 0. const AnimPose& getRelativeDefaultPose(int jointIndex) const; From 520d21b7978f59f2e32058dc3940c764e38d889c Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 25 Feb 2021 23:15:36 -0500 Subject: [PATCH 30/86] Update Audio.cpp --- interface/src/scripting/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index ddcaef653c..6277ab8b5d 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -26,7 +26,7 @@ QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; Setting::Handle enableNoiseReductionAutomaticSetting { QStringList { Audio::AUDIO, "NoiseReductionAutomatic" }, false }; -Setting::Handle setNoiseReductionThresholdSetting { QStringList { Audio::AUDIO, "NoiseReductionThreshold" }, 0.2f }; +Setting::Handle setNoiseReductionThresholdSetting { QStringList { Audio::AUDIO, "NoiseReductionThreshold" }, 0.1f }; Setting::Handle enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true }; Setting::Handle enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true }; From df7fd920d7b1d6cf93191783ee5da7a94d994c55 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 25 Feb 2021 23:28:00 -0500 Subject: [PATCH 31/86] Change INVALID_JOINT_INDEX to AnimSkeleton::INVALID_JOINT_INDEX --- .../animation/src/AnimInverseKinematics.cpp | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index cd4558d5b7..095e8333ee 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -67,7 +67,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, cons poleVectorVar(poleVectorVarIn), weight(weightIn), numFlexCoefficients(flexCoefficientsIn.size()), - jointIndex(INVALID_JOINT_INDEX) + jointIndex(AnimSkeleton::INVALID_JOINT_INDEX) { numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS); for (size_t i = 0; i < numFlexCoefficients; i++) { @@ -173,14 +173,14 @@ bool debounceJointWarnings() { void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) { - _hipsTargetIndex = INVALID_JOINT_INDEX; + _hipsTargetIndex = AnimSkeleton::INVALID_JOINT_INDEX; targets.reserve(_targetVarVec.size()); for (auto& targetVar : _targetVarVec) { // update targetVar jointIndex cache - if (targetVar.jointIndex == INVALID_JOINT_INDEX) { + if (targetVar.jointIndex == AnimSkeleton::INVALID_JOINT_INDEX) { int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName); if (jointIndex >= 0) { // this targetVar has a valid joint --> cache the indices @@ -191,7 +191,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } IKTarget target; - if (targetVar.jointIndex != INVALID_JOINT_INDEX) { + if (targetVar.jointIndex != AnimSkeleton::INVALID_JOINT_INDEX) { target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); target.setIndex(targetVar.jointIndex); if (target.getType() != IKTarget::Type::Unknown) { @@ -330,7 +330,7 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // update the absolutePoses for (int i = 0; i < (int)_relativePoses.size(); ++i) { auto parentIndex = _skeleton->getParentIndex((int)i); - if (parentIndex != INVALID_JOINT_INDEX) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -352,12 +352,12 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // finally set the relative rotation of each tip to agree with absolute target rotation for (auto& target: targets) { int tipIndex = target.getIndex(); - int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : INVALID_JOINT_INDEX; + int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : AnimSkeleton::INVALID_JOINT_INDEX; int chainIndex = targetToChainMap[tipIndex]; bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f; float alpha = needsInterpolation ? getInterpolationAlpha(_prevJointChainInfoVec[chainIndex].timer) : 0.0f; // update rotationOnly targets that don't lie on the ik chain of other ik targets. - if (parentIndex != INVALID_JOINT_INDEX && !_rotationAccumulators[tipIndex].isDirty() && + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX && !_rotationAccumulators[tipIndex].isDirty() && (target.getType() == IKTarget::Type::RotationOnly || target.getType() == IKTarget::Type::Unknown)) { if (target.getType() == IKTarget::Type::RotationOnly) { const glm::quat& targetRotation = target.getRotation(); @@ -435,11 +435,11 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const int tipIndex = target.getIndex(); int pivotIndex = _skeleton->getParentIndex(tipIndex); - if (pivotIndex == INVALID_JOINT_INDEX || pivotIndex == _hipsIndex) { + if (pivotIndex == AnimSkeleton::INVALID_JOINT_INDEX || pivotIndex == _hipsIndex) { return; } int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); - if (pivotsParentIndex == INVALID_JOINT_INDEX) { + if (pivotsParentIndex == AnimSkeleton::INVALID_JOINT_INDEX) { // TODO?: handle case where tip's parent is root? return; } @@ -486,7 +486,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const chainDepth++; // descend toward root, pivoting each joint to get tip closer to target position - while (pivotIndex != _hipsIndex && pivotsParentIndex != INVALID_JOINT_INDEX) { + while (pivotIndex != _hipsIndex && pivotsParentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { assert(chainDepth < jointChainInfoOut.jointInfoVec.size()); @@ -580,12 +580,12 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const if (target.getPoleVectorEnabled()) { int topJointIndex = target.getIndex(); int midJointIndex = _skeleton->getParentIndex(topJointIndex); - if (midJointIndex != INVALID_JOINT_INDEX) { + if (midJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) { int baseJointIndex = _skeleton->getParentIndex(midJointIndex); - if (baseJointIndex != INVALID_JOINT_INDEX) { + if (baseJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) { int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex); AnimPose topPose, midPose, basePose; - int topChainIndex = INVALID_JOINT_INDEX, baseChainIndex = INVALID_JOINT_INDEX; + int topChainIndex = AnimSkeleton::INVALID_JOINT_INDEX, baseChainIndex = AnimSkeleton::INVALID_JOINT_INDEX; const size_t MAX_CHAIN_DEPTH = 30; AnimPose postAbsPoses[MAX_CHAIN_DEPTH]; AnimPose accum = absolutePoses[_hipsIndex]; @@ -757,7 +757,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani int index = target.getIndex(); int endIndex = _skeleton->getParentIndex(_hipsIndex); - while (index != endIndex && index != INVALID_JOINT_INDEX) { + while (index != endIndex && index != AnimSkeleton::INVALID_JOINT_INDEX) { AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index); float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength; @@ -1461,7 +1461,7 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele // invalidate all targetVars for (auto& targetVar: _targetVarVec) { - targetVar.jointIndex = INVALID_JOINT_INDEX; + targetVar.jointIndex = AnimSkeleton::INVALID_JOINT_INDEX; } for (auto& accumulator: _rotationAccumulators) { @@ -1481,18 +1481,18 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele if (_hipsIndex >= 0) { _hipsParentIndex = _skeleton->getParentIndex(_hipsIndex); } else { - _hipsParentIndex = INVALID_JOINT_INDEX; + _hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX; } _leftHandIndex = _skeleton->nameToJointIndex("LeftHand"); _rightHandIndex = _skeleton->nameToJointIndex("RightHand"); } else { clearConstraints(); - _headIndex = INVALID_JOINT_INDEX; - _hipsIndex = INVALID_JOINT_INDEX; - _hipsParentIndex = INVALID_JOINT_INDEX; - _leftHandIndex = INVALID_JOINT_INDEX; - _rightHandIndex = INVALID_JOINT_INDEX; + _headIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _hipsIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _leftHandIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _rightHandIndex = AnimSkeleton::INVALID_JOINT_INDEX; } } @@ -1532,7 +1532,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != INVALID_JOINT_INDEX) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } @@ -1581,7 +1581,7 @@ void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInf DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE); // draw line to parent - if (parentIndex != INVALID_JOINT_INDEX) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); glm::vec4 color = GRAY; @@ -1630,13 +1630,13 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != INVALID_JOINT_INDEX) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } glm::quat parentAbsRot; - if (parentIndex != INVALID_JOINT_INDEX) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { parentAbsRot = poses[parentIndex].rot(); } @@ -1734,7 +1734,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC const float MIN_AXIS_LENGTH = 1.0e-4f; for (auto& target : targets) { - if (target.getIndex() != INVALID_JOINT_INDEX && target.getType() == IKTarget::Type::RotationAndPosition) { + if (target.getIndex() != AnimSkeleton::INVALID_JOINT_INDEX && target.getType() == IKTarget::Type::RotationAndPosition) { for (int i = 0; i < NUM_LIMBS; i++) { if (limbs[i].first == target.getIndex()) { int tipIndex = limbs[i].first; From b102b3477ae5a347a11eacacc8a526d95dfb0205 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Fri, 26 Feb 2021 20:04:53 +0100 Subject: [PATCH 32/86] Set resource total size to content size when using a qrc:// resource When a Resource is requested from qrc://, the Resource::handleDownloadProgress event is never called, and therefore _bytesTotal remains at zero. This confuses ResourceCache later, which has a sanity check that doesn't pass when resources are retrieved, yet no data accumulates. Fixes #1065 --- libraries/networking/src/ResourceCache.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4ba456d859..54297415c0 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -794,8 +794,6 @@ void Resource::handleReplyFinished() { { "size_mb", _bytesTotal / 1000000.0 } }); - setSize(_bytesTotal); - // Make sure we keep the Resource alive here auto self = _self.lock(); ResourceCache::requestCompleted(_self); @@ -809,6 +807,14 @@ void Resource::handleReplyFinished() { } auto data = _request->getData(); + if (_request->getUrl().scheme() == "qrc") { + // For resources under qrc://, there's no actual download being done, so + // handleDownloadProgress never gets called. We get the full length here + // at the end. + _bytesTotal = data.length(); + } + + setSize(_bytesTotal); emit loaded(data); downloadFinished(data); } else { From 3a945f87ddef1b07c0455d979ed23232907139dc Mon Sep 17 00:00:00 2001 From: Kalila L Date: Fri, 26 Feb 2021 21:01:22 -0500 Subject: [PATCH 33/86] Add messaging mixer rate limiting per node functionality. --- .../src/messages/MessagesMixer.cpp | 55 +++++++++++++++++++ .../src/messages/MessagesMixer.h | 14 ++++- .../resources/describe-settings.json | 18 ++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index bcf4881fcf..a4f5a65ce6 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -47,6 +47,13 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa MessagesClient::decodeMessagesPacket(receivedMessage, channel, isText, message, data, senderID); auto nodeList = DependencyManager::get(); + if (_allSubscribers.value(senderNode->getUUID()) >= _maxMessagesPerSecond) { + // We will reject all messages that go over this limit for that second. + // FIXME: We will add logging options to offer analytics on this later. + return; + } + + _allSubscribers[senderNode->getUUID()] = _allSubscribers.value(senderNode->getUUID()) + 1; nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { @@ -62,6 +69,8 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa void MessagesMixer::handleMessagesSubscribe(QSharedPointer message, SharedNodePointer senderNode) { QString channel = QString::fromUtf8(message->getMessage()); _channelSubscribers[channel] << senderNode->getUUID(); + + _allSubscribers[senderNode->getUUID()] << 0; } void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer message, SharedNodePointer senderNode) { @@ -69,6 +78,10 @@ void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer me if (_channelSubscribers.contains(channel)) { _channelSubscribers[channel].remove(senderNode->getUUID()); } + + if (_allSubscribers.contains(senderNode->getUUID())) { + _allSubscribers.remove(senderNode->getUUID()); + } } void MessagesMixer::sendStatsPacket() { @@ -88,7 +101,49 @@ void MessagesMixer::sendStatsPacket() { } void MessagesMixer::run() { + // wait until we have the domain-server settings, otherwise we bail + DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); + connect(&domainHandler, &DomainHandler::settingsReceived, this, &MessagesMixer::domainSettingsRequestComplete); + ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); + + startMaxMessagesProcessor(); +} + +void MessagesMixer::domainSettingsRequestComplete() { auto nodeList = DependencyManager::get(); nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); + + DomainHandler& domainHandler = nodeList->getDomainHandler(); + + // parse the settings to pull out the values we need + parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject()); +} + +void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { + const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; + QJsonObject messagesMixerGroupObject = domainSettings[MESSAGES_MIXER_SETTINGS_KEY].toObject(); + + const QString NODE_MESSAGES_PER_SECOND_KEY = "max_node_messages_per_second"; + QJsonValue maxMessagesPerSecondValue = messagesMixerGroupObject.value(NODE_MESSAGES_PER_SECOND_KEY); + if (!maxMessagesPerSecondValue.isUndefined()) { + _maxMessagesPerSecond = maxMessagesPerSecondValue.toInt(); + } else { + _maxMessagesPerSecond = DEFAULT_NODE_MESSAGES_PER_SECOND; + } +} + +void MessagesMixer::processMaxMessagesContainer() { + _allSubscribers.clear(); +} + +void MessagesMixer::startMaxMessagesProcessor() { + maxMessagesPerSecondTimer = new QTimer(); + connect(maxMessagesPerSecondTimer, &QTimer::timeout, this, &MessagesMixer::processMaxMessagesContainer); + maxMessagesPerSecondTimer->start(1000); // Clear the container every second. +} + +void MessagesMixer::stopMaxMessagesProcessor() { + delete maxMessagesPerSecondTimer; + maxMessagesPerSecondTimer = NULL; } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 800d42199b..68c21e2c57 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -32,9 +32,21 @@ private slots: void handleMessages(QSharedPointer message, SharedNodePointer senderNode); void handleMessagesSubscribe(QSharedPointer message, SharedNodePointer senderNode); void handleMessagesUnsubscribe(QSharedPointer message, SharedNodePointer senderNode); + void parseDomainServerSettings(const QJsonObject& domainSettings); + void domainSettingsRequestComplete(); + + void startMaxMessagesProcessor(); + void stopMaxMessagesProcessor(); + void processMaxMessagesContainer(); private: - QHash> _channelSubscribers; + QHash> _channelSubscribers; + QHash _allSubscribers; + + const int DEFAULT_NODE_MESSAGES_PER_SECOND = 1000; + int _maxMessagesPerSecond{ 0 }; + + QTimer* maxMessagesPerSecondTimer; }; #endif // hifi_MessagesMixer_h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index bb7acf344c..b2b56f8eff 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1488,6 +1488,24 @@ } ] }, + { + "name": "messages_mixer", + "label": "Messages Mixer", + "assignment-types": [ + 4 + ], + "settings": [ + { + "name": "max_node_messages_per_second", + "type": "int", + "label": "Per-Node Send Messages", + "help": "Desired maximum send messages (in messages per second) per node", + "placeholder": 1000, + "default": 1000, + "advanced": true + } + ] + }, { "name": "entity_server_settings", "label": "Entities", From 4a9d4d4cbe9cb01ed442b484d72702c5965317c5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 28 Feb 2021 14:04:27 +1300 Subject: [PATCH 34/86] Fix privacy setting text --- interface/src/ui/PreferencesDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9c53060f31..4de353ad0e 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -250,9 +250,9 @@ void setupPreferences() { { auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); }; auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); }; - preferences->addPreference(new CheckPreference("Privacy", "Send data - High Fidelity uses information provided by your " + preferences->addPreference(new CheckPreference("Privacy", "Send data - Vircadia uses information provided by your " "client to improve the product through the logging of errors, tracking of usage patterns, " - "installation and system details. By allowing High Fidelity to collect this information " + "installation and system details. By allowing Vircadia to collect this information " "you are helping to improve the product. ", getter, setter)); } From 0693fcbf60a74d022d99422d91806dce5675f1a0 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Mon, 1 Mar 2021 17:55:09 -0500 Subject: [PATCH 35/86] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julian Groß --- INSTALLER.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/INSTALLER.md b/INSTALLER.md index a96af042b5..0fdbb028f3 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -102,7 +102,7 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS #### Server -##### Ubuntu | .deb +##### Ubuntu 18.04 | .deb 1. Ensure you are using an Ubuntu 18.04 system. 2. Get and bootstrap Vircadia Builder. @@ -111,11 +111,11 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS cd vircadia-builder chmod +x vircadia-builder ``` -3. Set up Vircadia Builder to compile the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. +3. Run Vircadia Builder to build the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. ``` ./vircadia-builder --build server ``` -4. Build the server. +4. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. ``` ./vircadia-builder --build server ``` @@ -145,11 +145,11 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS sudo ./install_amazon_linux_deps.sh chmod +x vircadia-builder ``` -4. Set up Vircadia Builder to compile the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. +4. Run Vircadia Builder to build the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. ``` ./vircadia-builder --build server ``` -5. Build the server. +5. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. ``` ./vircadia-builder --build server ``` From 138e8a0007139341868efc15c36a7afaf5ed1cb6 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Mon, 1 Mar 2021 19:05:39 -0500 Subject: [PATCH 36/86] Update INSTALLER.md --- INSTALLER.md | 120 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 14 deletions(-) diff --git a/INSTALLER.md b/INSTALLER.md index 0fdbb028f3..bcbd24a6c0 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -104,8 +104,13 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ##### Ubuntu 18.04 | .deb -1. Ensure you are using an Ubuntu 18.04 system. -2. Get and bootstrap Vircadia Builder. +1. Ensure you are using an Ubuntu 18.04 system, here are the recommended specs: + ``` + AWS EC2 Instance Type: C5a.4xlarge + CPU Cores: 16 + Minimum Disk Space: 40GB + ``` +3. Get and bootstrap Vircadia Builder. ``` git clone https://github.com/vircadia/vircadia-builder.git cd vircadia-builder @@ -115,23 +120,69 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ``` ./vircadia-builder --build server ``` -4. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. +4. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step. ``` ./vircadia-builder --build server ``` -5. Navigate to the `pkg-scripts` directory. +5. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.* +6. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to. + ``` + Git repository: https://github.com/vircadia/vircadia/ + # OR, for example + Git repository: https://github.com/digisomni/vircadia/ + ``` +7. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc" + ``` + Git tag: master + # OR, for example + Git tag: v2021.1.0-rc + ``` +8. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`. + ``` + Release type: DEV + # OR, for example we recommend you use + Release type: PRODUCTION + ``` +9. This value is the release version. Release numbers should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`. + ``` + Release number: 2021.1.0 + ``` +10. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`. + ``` + Build number: fd6973b + ``` +11. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user. + ``` + Installation dir: /home/ubuntu/Vircadia + ``` +12. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. + ``` + CPU cores to use for Vircadia: 16 + ``` +13. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. + ``` + CPU cores to use for Qt5: 16 + ``` +14. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit. +15. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times. +16. Navigate to the `pkg-scripts` directory. ``` cd ../Vircadia/source/pkg-scripts/ ``` -6. Generate the .deb package. Set `DEBVERSION` to a valid semantic version value e.g. "2021.1.0". +17. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.* ``` - DEBVERSION="Semver e.g. 2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server + DEBVERSION="2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server ``` -7. If successful, the generated .deb package will be in the `pkg-scripts` folder. +18. If successful, the generated .deb package will be in the `pkg-scripts` folder. ##### Amazon Linux 2 | .rpm -1. Ensure you are using an Amazon Linux 2 system. +1. Ensure you are using an Amazon Linux 2 system, here are the recommended specs: + ``` + AWS EC2 Instance Type: C5a.4xlarge + CPU Cores: 16 + Minimum Disk Space: 40GB + ``` 2. Update the system and install dependencies. ``` sudo yum update -y @@ -145,20 +196,61 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS sudo ./install_amazon_linux_deps.sh chmod +x vircadia-builder ``` -4. Run Vircadia Builder to build the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. +4. Run Vircadia Builder to build the server. ``` ./vircadia-builder --build server ``` -5. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. +5. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step. ``` ./vircadia-builder --build server ``` -6. Navigate to the `pkg-scripts` directory. +6. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.* +7. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to. + ``` + Git repository: https://github.com/vircadia/vircadia/ + # OR, for example + Git repository: https://github.com/digisomni/vircadia/ + ``` +8. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc". + ``` + Git tag: master + # OR, for example + Git tag: v2021.1.0-rc + ``` +9. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`. + ``` + Release type: DEV + # OR, for example we recommend you use + Release type: PRODUCTION + ``` +10. This value is the release version. Release numbers typically should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`. + ``` + Release number: 2021.1.0 + ``` +11. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`. + ``` + Build number: fd6973b + ``` +12. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user. + ``` + Installation dir: /root/Vircadia + ``` +13. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. + ``` + CPU cores to use for Vircadia: 16 + ``` +14. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. + ``` + CPU cores to use for Qt5: 16 + ``` +15. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit. +16. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times. +17. Navigate to the `pkg-scripts` directory. ``` cd ../Vircadia/source/pkg-scripts/ ``` -7. Generate the .rpm package. Set `RPMVERSION` to a valid semantic version value e.g. "2021.1.0". +18. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.* ``` - RPMVERSION="Semver e.g. 2021.1.0" ./make-rpm-server + RPMVERSION="2021.1.0" ./make-rpm-server ``` -8. If successful, the generated .rpm package will be in the `pkg-scripts` folder. +19. If successful, the generated .rpm package will be in the `pkg-scripts` folder of the Vircadia source files. From 945e9bb9e95df965f22ae3f90892f10414a05cde Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Mon, 1 Mar 2021 19:07:05 -0500 Subject: [PATCH 37/86] Update INSTALLER.md --- INSTALLER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALLER.md b/INSTALLER.md index bcbd24a6c0..efde9261bd 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -1,6 +1,6 @@ # Creating an Installer -*Last Updated on February 21, 2021* +*Last Updated on March 1, 2021* Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. From 13fd2b43d7fc35976cea1e8a924e593341857c3b Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Wed, 3 Mar 2021 01:50:14 -0500 Subject: [PATCH 38/86] Update INSTALLER.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julian Groß --- INSTALLER.md | 1 - 1 file changed, 1 deletion(-) diff --git a/INSTALLER.md b/INSTALLER.md index efde9261bd..79c53b93d7 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -194,7 +194,6 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS git clone https://github.com/vircadia/vircadia-builder.git cd vircadia-builder sudo ./install_amazon_linux_deps.sh - chmod +x vircadia-builder ``` 4. Run Vircadia Builder to build the server. ``` From 8c9a80645560a7a80681e7a7ab3ab1ec52413430 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Wed, 3 Mar 2021 01:51:59 -0500 Subject: [PATCH 39/86] Update INSTALLER.md --- INSTALLER.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/INSTALLER.md b/INSTALLER.md index 79c53b93d7..183780d95f 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -114,9 +114,8 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ``` git clone https://github.com/vircadia/vircadia-builder.git cd vircadia-builder - chmod +x vircadia-builder ``` -3. Run Vircadia Builder to build the server. See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings. +3. Run Vircadia Builder. ``` ./vircadia-builder --build server ``` @@ -195,7 +194,7 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS cd vircadia-builder sudo ./install_amazon_linux_deps.sh ``` -4. Run Vircadia Builder to build the server. +4. Run Vircadia Builder. ``` ./vircadia-builder --build server ``` From 4dd3ee6e1e212f14c928c600462687e77907cfe0 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Wed, 3 Mar 2021 02:47:19 -0500 Subject: [PATCH 40/86] "Test your voice" now takes into account noise gate state. --- libraries/audio-client/src/AudioClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index a3d2568b6d..38635870fd 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1266,7 +1266,7 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { // If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here. bool hasReverb = _reverb || _receivedAudioStream.hasReverb(); - if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) { + if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) { return; } From 809bd936d304b575526b0007cad98c977ec0a001 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Wed, 3 Mar 2021 21:17:17 -0500 Subject: [PATCH 41/86] Update Audio.cpp Fix default value for "NoiseReductionAutomatic" to true. --- interface/src/scripting/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 6277ab8b5d..4a96e36939 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -25,7 +25,7 @@ QString Audio::DESKTOP { "Desktop" }; QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; -Setting::Handle enableNoiseReductionAutomaticSetting { QStringList { Audio::AUDIO, "NoiseReductionAutomatic" }, false }; +Setting::Handle enableNoiseReductionAutomaticSetting { QStringList { Audio::AUDIO, "NoiseReductionAutomatic" }, true }; Setting::Handle setNoiseReductionThresholdSetting { QStringList { Audio::AUDIO, "NoiseReductionThreshold" }, 0.1f }; Setting::Handle enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true }; Setting::Handle enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true }; From 99fde1efd03c3abbf80693ff4177635bee94dc86 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Wed, 3 Mar 2021 21:31:05 -0500 Subject: [PATCH 42/86] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julian Groß --- INSTALLER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALLER.md b/INSTALLER.md index 183780d95f..07325f50bb 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -233,11 +233,11 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ``` Installation dir: /root/Vircadia ``` -13. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. +13. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server. ``` CPU cores to use for Vircadia: 16 ``` -14. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. +14. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server. ``` CPU cores to use for Qt5: 16 ``` From 2891d731b8108865afe8078772f860e498bcf909 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Wed, 3 Mar 2021 21:46:28 -0500 Subject: [PATCH 43/86] Update INSTALLER.md --- INSTALLER.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/INSTALLER.md b/INSTALLER.md index 07325f50bb..4bdf4410b3 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -104,10 +104,10 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ##### Ubuntu 18.04 | .deb -1. Ensure you are using an Ubuntu 18.04 system, here are the recommended specs: +1. Ensure you are using an Ubuntu 18.04 system. ``` - AWS EC2 Instance Type: C5a.4xlarge - CPU Cores: 16 + Minimum CPU Cores: 4 + Recommended CPU Cores: 16 Minimum Disk Space: 40GB ``` 3. Get and bootstrap Vircadia Builder. @@ -176,10 +176,10 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ##### Amazon Linux 2 | .rpm -1. Ensure you are using an Amazon Linux 2 system, here are the recommended specs: +1. Ensure you are using an Amazon Linux 2 system. You will need many CPU cores to complete this process within a reasonable time. As an alternative to AWS EC2, you may use a [virtual machine](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-2-virtual-machine.html). Here are the recommended specs: ``` AWS EC2 Instance Type: C5a.4xlarge - CPU Cores: 16 + Recommended CPU Cores: 16 Minimum Disk Space: 40GB ``` 2. Update the system and install dependencies. From fd4abbb539ff48f21eeeab05ad012d6363278e66 Mon Sep 17 00:00:00 2001 From: Phil Palmer Date: Wed, 3 Mar 2021 23:37:21 -0500 Subject: [PATCH 44/86] Apply suggestions from code review: Re-add deprecated MyAvatar.setToggleHips and have it do nothing but output a log warning. --- interface/src/avatar/MyAvatar.cpp | 6 ++++++ interface/src/avatar/MyAvatar.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c40974ac87..957dee3164 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1392,6 +1392,12 @@ float loadSetting(Settings& settings, const QString& name, float defaultValue) { return value; } +void MyAvatar::setToggleHips(bool followHead) { + Q_UNUSED(followHead); + qCDebug(interfaceapp) << "MyAvatar.setToggleHips is deprecated; it no longer does anything; it will soon be removed from the API; " + "please update your script"; +} + void MyAvatar::setEnableDebugDrawBaseOfSupport(bool isEnabled) { _enableDebugDrawBaseOfSupport = isEnabled; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index bb7ebb62ba..c25d8306de 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -2181,6 +2181,13 @@ public slots: */ Q_INVOKABLE void updateMotionBehaviorFromMenu(); + /**jsdoc + * @function MyAvatar.setToggleHips + * @param {boolean} enabled - Enabled. + * @deprecated This function is deprecated and will be removed. + */ + void setToggleHips(bool followHead); + /**jsdoc * Displays the base of support area debug graphics if in HMD mode. If your head goes outside this area your avatar's hips * are moved to counterbalance your avatar, and if your head moves too far then your avatar's position is moved (i.e., a From c3ea217d2d51b35c1798d0652d97e013b5768410 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 4 Mar 2021 16:53:54 -0500 Subject: [PATCH 45/86] Update INSTALLER.md --- INSTALLER.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/INSTALLER.md b/INSTALLER.md index 4bdf4410b3..813f9986da 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -104,9 +104,8 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ##### Ubuntu 18.04 | .deb -1. Ensure you are using an Ubuntu 18.04 system. +1. Ensure you are using an Ubuntu 18.04 system. There is no required minimum to the amount of CPU cores needed, however it's recommended that you use as many as you have available in order to have an efficient experience. ``` - Minimum CPU Cores: 4 Recommended CPU Cores: 16 Minimum Disk Space: 40GB ``` From 193baeabf5e627c6c3be871f53a644a69fa8627b Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 4 Mar 2021 16:55:18 -0500 Subject: [PATCH 46/86] Update INSTALLER.md --- INSTALLER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALLER.md b/INSTALLER.md index 813f9986da..4f6ebc174d 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -1,6 +1,6 @@ # Creating an Installer -*Last Updated on March 1, 2021* +*Last Updated on March 4, 2021* Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. From c434783b10d6ec63d42d708a850fa22266927624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Gro=C3=9F?= Date: Sat, 6 Mar 2021 19:48:20 +0100 Subject: [PATCH 47/86] Define code block language to fix sphinx warnings --- BUILD.md | 96 ++++++++++++++++++++++++++---------------------- BUILD_ANDROID.md | 31 ++++++++++------ BUILD_OSX.md | 24 +++++++----- 3 files changed, 87 insertions(+), 64 deletions(-) diff --git a/BUILD.md b/BUILD.md index 0e1a54f406..781578d343 100644 --- a/BUILD.md +++ b/BUILD.md @@ -42,15 +42,17 @@ Vircadia uses CMake to generate build files and project files for your platform. #### Qt -CMake will download Qt 5.12.3 using vcpkg. +CMake will download Qt 5.12.3 using vcpkg. -To override this (i.e. use an installed Qt configuration - you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt **lib/cmake** folder. +To override this (i.e. use an installed Qt configuration - you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt **lib/cmake** folder. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). The path it needs to be set to will depend on where and how Qt5 was installed. e.g. - export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.12.3/gcc_64/lib/cmake - export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/clang_64/lib/cmake/ - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.12.3/lib/cmake - export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake +```bash +export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.12.3/gcc_64/lib/cmake +export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/clang_64/lib/cmake/ +export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.12.3/lib/cmake +export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake +``` #### VCPKG @@ -60,7 +62,9 @@ You do not need to install vcpkg. Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory. However, those files can potentially get cleaned up by the OS, so in order to avoid this and having to redo the lengthy build step, you can set the following environment variable: - export HIFI_VCPKG_BASE=/path/to/directory +```bash +export HIFI_VCPKG_BASE=/path/to/directory +``` Where `/path/to/directory` is the path to a directory where you wish the build files to get stored. @@ -68,45 +72,49 @@ Where `/path/to/directory` is the path to a directory where you wish the build f ##### Possible Environment Variables - // The URL to post the dump to. - CMAKE_BACKTRACE_URL - // The identifying tag of the release. - CMAKE_BACKTRACE_TOKEN - - // The release version, e.g., 2021.3.2. - RELEASE_NUMBER - // The release name, e.g., Eos. - RELEASE_NAME - // The build commit, e.g., use a Git hash for the most recent commit in the branch - fd6973b. - - BUILD_NUMBER - - // The type of release. - RELEASE_TYPE=PRODUCTION|PR|DEV - - // The Interface will have a custom default home and startup location. - INITIAL_STARTUP_LOCATION=Location/IP/URL - - // Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place. - HF_PFX_FILE=Path to certificate - HF_PFX_PASSPHRASE=Passphrase for certificate - - // Determine the build type - PRODUCTION_BUILD=0|1 - PR_BUILD=0|1 - STABLE_BUILD=0|1 - - // Determine if to utilize testing or stable Metaverse URLs - USE_STABLE_GLOBAL_SERVICES=1 - BUILD_GLOBAL_SERVICES=STABLE - +```text +// The URL to post the dump to. +CMAKE_BACKTRACE_URL +// The identifying tag of the release. +CMAKE_BACKTRACE_TOKEN + +// The release version, e.g., 2021.3.2. +RELEASE_NUMBER +// The release name, e.g., Eos. +RELEASE_NAME +// The build commit, e.g., use a Git hash for the most recent commit in the branch - fd6973b. + +BUILD_NUMBER + +// The type of release. +RELEASE_TYPE=PRODUCTION|PR|DEV + +// The Interface will have a custom default home and startup location. +INITIAL_STARTUP_LOCATION=Location/IP/URL + +// Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place. +HF_PFX_FILE=Path to certificate +HF_PFX_PASSPHRASE=Passphrase for certificate + +// Determine the build type +PRODUCTION_BUILD=0|1 +PR_BUILD=0|1 +STABLE_BUILD=0|1 + +// Determine if to utilize testing or stable Metaverse URLs +USE_STABLE_GLOBAL_SERVICES=1 +BUILD_GLOBAL_SERVICES=STABLE +``` + ##### Generate Files Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean. - mkdir build - cd build - cmake .. +```bash +mkdir build +cd build +cmake .. +``` If CMake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`. @@ -128,7 +136,9 @@ Any variables that need to be set for CMake to find dependencies can be set as E For example, to pass the QT_CMAKE_PREFIX_PATH variable (if not using the vcpkg'ed version) during build file generation: - cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/lib/cmake +```bash +cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/lib/cmake +``` #### Finding Dependencies diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index c52f6a1d8f..0556e175d6 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -42,11 +42,13 @@ Follow the directions [here](https://developer.android.com/studio/publish/app-si Create a `gradle.properties` file in the `.gradle` folder (`$HOME/.gradle` on Unix, `Users//.gradle` on Windows). Edit the file to contain the following - HIFI_ANDROID_PRECOMPILED=/Android/hifi_externals - HIFI_ANDROID_KEYSTORE=/.jks - HIFI_ANDROID_KEYSTORE_PASSWORD= - HIFI_ANDROID_KEY_ALIAS= - HIFI_ANDROID_KEY_PASSWORD= +```properties +HIFI_ANDROID_PRECOMPILED=/Android/hifi_externals +HIFI_ANDROID_KEYSTORE=/.jks +HIFI_ANDROID_KEYSTORE_PASSWORD= +HIFI_ANDROID_KEY_ALIAS= +HIFI_ANDROID_KEY_PASSWORD= +``` Note, do not use $HOME for the path. It must be a fully qualified path name. Also, be sure to use forward slashes in your path. @@ -54,21 +56,26 @@ Note, do not use $HOME for the path. It must be a fully qualified path name. Als Add these lines to `gradle.properties` - SUPPRESS_QUEST_INTERFACE - SUPPRESS_QUEST_FRAME_PLAYER +```properties +SUPPRESS_QUEST_INTERFACE +SUPPRESS_QUEST_FRAME_PLAYER +``` #### If you are building for an Oculus Quest Add these lines to `gradle.properties` - SUPPRESS_INTERFACE - SUPPRESS_FRAME_PLAYER - +```properties +SUPPRESS_INTERFACE +SUPPRESS_FRAME_PLAYER +``` #### The Frame Player for both Android Phone and Oculus Quest is optional, so if you encounter problems with these during your build, you can skip them by adding these lines to `gradle.properties` - SUPPRESS_FRAME_PLAYER - SUPPRESS_QUEST_FRAME_PLAYER +```properties +SUPPRESS_FRAME_PLAYER +SUPPRESS_QUEST_FRAME_PLAYER +``` ### Clone the repository diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 525a7a5965..9bf1985409 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -8,22 +8,26 @@ 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 npm +```bash +brew install cmake openssl npm +``` ### Python 3 -Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/). +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 version `10.12` of the OSX SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on OSX. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g. - cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ +```bash +cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ +``` ### 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. +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 via `export OPENSSL_ROOT_DIR=/usr/local/opt/openssl` or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake` @@ -31,12 +35,14 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via ### Xcode You can ask CMake to generate Xcode project files instead of Unix Makefiles using the `-G Xcode` parameter after CMake. 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 ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 .. -If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally instaled CMake +```bash +sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer + +cmake ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 .. +``` + +If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally installed CMake. 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. From 6ad9113bccc199e5a2a4cca5afcf824afa135f84 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 7 Mar 2021 20:57:04 -0500 Subject: [PATCH 48/86] Rename "Vircadia Sandbox" -> "Vircadia Server" --- cmake/macros/GenerateInstallers.cmake | 6 +++--- cmake/macros/SetPackagingParameters.cmake | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 89eeecfbbb..e09a7dc322 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -21,9 +21,9 @@ macro(GENERATE_INSTALLERS) set(INSTALLER_TYPE "client_only") string(REGEX REPLACE "Vircadia" "Vircadia Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION}) elseif (SERVER_ONLY) - set(_PACKAGE_NAME_EXTRA "-Sandbox") + set(_PACKAGE_NAME_EXTRA "-Server") set(INSTALLER_TYPE "server_only") - string(REGEX REPLACE "Vircadia" "Vircadia Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION}) + string(REGEX REPLACE "Vircadia" "Vircadia Server" _DISPLAY_NAME ${BUILD_ORGANIZATION}) else () set(_DISPLAY_NAME ${BUILD_ORGANIZATION}) set(INSTALLER_TYPE "full") @@ -123,7 +123,7 @@ macro(GENERATE_INSTALLERS) endif () if (BUILD_SERVER) - cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Sandbox") + cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Server") endif () include(CPack) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 9311594938..4585beeb5f 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -130,7 +130,7 @@ macro(SET_PACKAGING_PARAMETERS) if (CLIENT_ONLY) set(CONSOLE_EXEC_NAME "Console.app") else () - set(CONSOLE_EXEC_NAME "Sandbox.app") + set(CONSOLE_EXEC_NAME "Server.app") endif() set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}") @@ -177,12 +177,12 @@ macro(SET_PACKAGING_PARAMETERS) if (PRODUCTION_BUILD) set(INTERFACE_SHORTCUT_NAME "Vircadia") set(CONSOLE_SHORTCUT_NAME "Console") - set(SANDBOX_SHORTCUT_NAME "Sandbox") + set(SANDBOX_SHORTCUT_NAME "Server") set(APP_USER_MODEL_ID "com.vircadia.console") else () set(INTERFACE_SHORTCUT_NAME "Vircadia - ${BUILD_VERSION_NO_SHA}") set(CONSOLE_SHORTCUT_NAME "Console - ${BUILD_VERSION_NO_SHA}") - set(SANDBOX_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}") + set(SANDBOX_SHORTCUT_NAME "Server - ${BUILD_VERSION_NO_SHA}") endif () set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}") From 9d7fa10092768030459aaf0d9e2e831c0595d18c Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 7 Mar 2021 21:03:30 -0500 Subject: [PATCH 49/86] Don't create server start menu shortcut if using Windows express install --- cmake/templates/NSIS.template.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 4c44ed1fbd..bce2a2961f 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -251,8 +251,8 @@ Var substringResult !macro InitSection SecName ; This macro reads component installed flag from the registry and - ;changes checked state of the section on the components page. - ;Input: section index constant name specified in Section command. + ; changes checked state of the section on the components page. + ; Input: section index constant name specified in Section command. ClearErrors ;Reading component status from registry @@ -718,7 +718,7 @@ Function InstallTypesPage StrCpy $OffsetUnits u StrCpy $Express "0" - ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface and Vircadia Sandbox" + ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface" pop $ExpressInstallRadioButton ${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel IntOp $CurrentOffset $CurrentOffset + 15 @@ -1346,6 +1346,7 @@ Section "-Core installation" ${EndIf} ${If} @SERVER_COMPONENT_CONDITIONAL@ + ${AndIf} $Express == "0" ; handling for server console shortcut Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" \ From c9ab2a686fe892fd9a9bddb1ec41c029292e9d9f Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 7 Mar 2021 21:16:53 -0500 Subject: [PATCH 50/86] Update conditional checking. --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index bce2a2961f..33587f3e5a 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -1346,7 +1346,7 @@ Section "-Core installation" ${EndIf} ${If} @SERVER_COMPONENT_CONDITIONAL@ - ${AndIf} $Express == "0" + ${AndIf} $Express != "1" ; handling for server console shortcut Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" \ From fe3f0069538d381f67aa7566d9da71b13a382daa Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Mon, 8 Mar 2021 00:14:50 -0500 Subject: [PATCH 51/86] Update SetPackagingParameters.cmake Try to fix Apple builds. --- cmake/macros/SetPackagingParameters.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 4585beeb5f..bc4459c65f 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -130,7 +130,7 @@ macro(SET_PACKAGING_PARAMETERS) if (CLIENT_ONLY) set(CONSOLE_EXEC_NAME "Console.app") else () - set(CONSOLE_EXEC_NAME "Server.app") + set(CONSOLE_EXEC_NAME "Sandbox.app") endif() set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}") From 3ed98d0f3e3a4529e7f779b368888682f6580ea2 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Mon, 8 Mar 2021 02:00:34 -0500 Subject: [PATCH 52/86] Add "ScriptGatekeeper" class and "PRELOADED_SCRIPT_WHITELIST" variable. --- BUILD.md | 3 ++ cmake/macros/SetPackagingParameters.cmake | 3 ++ cmake/templates/BuildInfo.h.in | 1 + libraries/networking/src/AddressManager.cpp | 1 + libraries/script-engine/src/ScriptEngine.h | 1 - libraries/script-engine/src/ScriptEngines.cpp | 2 + libraries/script-engine/src/ScriptEngines.h | 3 ++ .../script-engine/src/ScriptGatekeeper.cpp | 41 +++++++++++++++++++ .../script-engine/src/ScriptGatekeeper.h | 29 +++++++++++++ 9 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 libraries/script-engine/src/ScriptGatekeeper.cpp create mode 100644 libraries/script-engine/src/ScriptGatekeeper.h diff --git a/BUILD.md b/BUILD.md index 0e1a54f406..f1b93556dc 100644 --- a/BUILD.md +++ b/BUILD.md @@ -86,6 +86,9 @@ Where `/path/to/directory` is the path to a directory where you wish the build f // The Interface will have a custom default home and startup location. INITIAL_STARTUP_LOCATION=Location/IP/URL + // The Interface will have a custom default script whitelist, comma separated, no spaces. + // This will also activate the whitelist on Interface's first run. + PRELOADED_SCRIPT_WHITELIST=ListOfEntries // Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place. HF_PFX_FILE=Path to certificate diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 9311594938..4c453cdfc0 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -25,7 +25,10 @@ macro(SET_PACKAGING_PARAMETERS) set_from_env(RELEASE_NUMBER RELEASE_NUMBER "") set_from_env(RELEASE_NAME RELEASE_NAME "") set_from_env(STABLE_BUILD STABLE_BUILD 0) + set_from_env(INITIAL_STARTUP_LOCATION INITIAL_STARTUP_LOCATION "") + set_from_env(PRELOADED_SCRIPT_WHITELIST PRELOADED_SCRIPT_WHITELIST "") + set_from_env(BYPASS_SIGNING BYPASS_SIGNING 0) message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}") diff --git a/cmake/templates/BuildInfo.h.in b/cmake/templates/BuildInfo.h.in index 7f3a63d4b4..42a7eddb6c 100644 --- a/cmake/templates/BuildInfo.h.in +++ b/cmake/templates/BuildInfo.h.in @@ -30,6 +30,7 @@ namespace BuildInfo { const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@"; const QString BUILD_TIME = "@BUILD_TIME@"; const QString INITIAL_STARTUP_LOCATION = "@INITIAL_STARTUP_LOCATION@"; + const QString PRELOADED_SCRIPT_WHITELIST = "@PRELOADED_SCRIPT_WHITELIST@"; enum BuildType { Dev, diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 94bcb718d3..ffa5a34118 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2014-09-10. // Copyright 2014 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 diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1d85de4d94..a19e63a665 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -174,7 +174,6 @@ public: QString getFilename() const; - QList getListOfEntityScriptIDs(); /**jsdoc diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 381d931620..d091e6e4b5 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -68,6 +68,8 @@ void ScriptEngines::onErrorLoadingScript(const QString& url) { ScriptEngines::ScriptEngines(ScriptEngine::Context context, const QUrl& defaultScriptsOverride) : _context(context), _defaultScriptsOverride(defaultScriptsOverride) { + scriptGatekeeper.initialize(); + _scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.sort(0, Qt::AscendingOrder); _scriptsModelFilter.setDynamicSortFilter(true); diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index df736e101d..8c897f70fd 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -24,6 +24,7 @@ #include "ScriptEngine.h" #include "ScriptsModel.h" #include "ScriptsModelFilter.h" +#include "ScriptGatekeeper.h" class ScriptEngine; @@ -176,6 +177,8 @@ public: bool isStopped() const { return _isStopped; } void addScriptEngine(ScriptEnginePointer); + + ScriptGatekeeper scriptGatekeeper; signals: diff --git a/libraries/script-engine/src/ScriptGatekeeper.cpp b/libraries/script-engine/src/ScriptGatekeeper.cpp new file mode 100644 index 0000000000..d69e8c9561 --- /dev/null +++ b/libraries/script-engine/src/ScriptGatekeeper.cpp @@ -0,0 +1,41 @@ +// +// ScriptGatekeeper.cpp +// libraries/script-engine/src +// +// Created by Kalila L. on Mar 7 2021 +// Copyright 2021 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 "ScriptGatekeeper.h" + +#include "BuildInfo.h" +#include "SettingHandle.h" + +void ScriptGatekeeper::initialize() { + if (_initialized == true) { + return; + } + + QVariant rawCurrentWhitelistValues = Setting::Handle(SCRIPT_WHITELIST_ENTRIES_KEY).get(); + QString settingsSafeValues = rawCurrentWhitelistValues.toString(); + + Setting::Handle whitelistEnabled{ SCRIPT_WHITELIST_ENABLED_KEY, false }; + Setting::Handle isFirstRun { Settings::firstRun, true }; + + QString preloadedVal = BuildInfo::PRELOADED_SCRIPT_WHITELIST; + + if (settingsSafeValues.isEmpty() && !preloadedVal.isEmpty() && isFirstRun.get()) { + // We assume that the whitelist should be enabled if a preloaded whitelist is attached, so we activate it if it's not already active. + qDebug() << "hi" << !whitelistEnabled.get() << whitelistEnabled.get(); + if (!whitelistEnabled.get()) { + whitelistEnabled.set(true); + } + + Setting::Handle(SCRIPT_WHITELIST_ENTRIES_KEY).set(preloadedVal); + } + + _initialized = true; +} diff --git a/libraries/script-engine/src/ScriptGatekeeper.h b/libraries/script-engine/src/ScriptGatekeeper.h new file mode 100644 index 0000000000..a73ef5995a --- /dev/null +++ b/libraries/script-engine/src/ScriptGatekeeper.h @@ -0,0 +1,29 @@ +// +// ScriptGatekeeper.h +// libraries/script-engine/src +// +// Created by Kalila L. on Mar 7 2021 +// Copyright 2021 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 +// + +#ifndef vircadia_ScriptGatekeeper_h +#define vircadia_ScriptGatekeeper_h + +#include + +class ScriptGatekeeper : public QObject { + Q_OBJECT +public: + void initialize(); + + QString SCRIPT_WHITELIST_ENABLED_KEY{ "private/whitelistEnabled" }; + QString SCRIPT_WHITELIST_ENTRIES_KEY{ "private/settingsSafeURLS" }; + +private: + bool _initialized{ false }; +}; + +#endif // vircadia_ScriptGatekeeper_h From b5d89c44bf3ee6cd780c91d7b8d1e6655adb842c Mon Sep 17 00:00:00 2001 From: Kalila L Date: Mon, 8 Mar 2021 02:01:53 -0500 Subject: [PATCH 53/86] Rename "INITIAL_STARTUP_LOCATION" -> "PRELOADED_STARTUP_LOCATION". --- BUILD.md | 2 +- cmake/macros/SetPackagingParameters.cmake | 2 +- cmake/templates/BuildInfo.h.in | 2 +- interface/src/Application.cpp | 2 +- libraries/networking/src/AddressManager.cpp | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/BUILD.md b/BUILD.md index f1b93556dc..f647cff476 100644 --- a/BUILD.md +++ b/BUILD.md @@ -85,7 +85,7 @@ Where `/path/to/directory` is the path to a directory where you wish the build f RELEASE_TYPE=PRODUCTION|PR|DEV // The Interface will have a custom default home and startup location. - INITIAL_STARTUP_LOCATION=Location/IP/URL + PRELOADED_STARTUP_LOCATION=Location/IP/URL // The Interface will have a custom default script whitelist, comma separated, no spaces. // This will also activate the whitelist on Interface's first run. PRELOADED_SCRIPT_WHITELIST=ListOfEntries diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 4c453cdfc0..42fbe0b0d0 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -26,7 +26,7 @@ macro(SET_PACKAGING_PARAMETERS) set_from_env(RELEASE_NAME RELEASE_NAME "") set_from_env(STABLE_BUILD STABLE_BUILD 0) - set_from_env(INITIAL_STARTUP_LOCATION INITIAL_STARTUP_LOCATION "") + set_from_env(PRELOADED_STARTUP_LOCATION PRELOADED_STARTUP_LOCATION "") set_from_env(PRELOADED_SCRIPT_WHITELIST PRELOADED_SCRIPT_WHITELIST "") set_from_env(BYPASS_SIGNING BYPASS_SIGNING 0) diff --git a/cmake/templates/BuildInfo.h.in b/cmake/templates/BuildInfo.h.in index 42a7eddb6c..99726d270f 100644 --- a/cmake/templates/BuildInfo.h.in +++ b/cmake/templates/BuildInfo.h.in @@ -29,7 +29,7 @@ namespace BuildInfo { const QString BUILD_NUMBER = "@BUILD_NUMBER@"; const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@"; const QString BUILD_TIME = "@BUILD_TIME@"; - const QString INITIAL_STARTUP_LOCATION = "@INITIAL_STARTUP_LOCATION@"; + const QString PRELOADED_STARTUP_LOCATION = "@PRELOADED_STARTUP_LOCATION@"; const QString PRELOADED_SCRIPT_WHITELIST = "@PRELOADED_SCRIPT_WHITELIST@"; enum BuildType { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eec1efbd99..d8338162b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4015,7 +4015,7 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // If this is a first run we short-circuit the address passed in if (_firstRun.get()) { - if (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) { + if (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) { DependencyManager::get()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS); Menu::getInstance()->triggerOption(MenuOption::HomeLocation); } diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index ffa5a34118..76587118f1 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -36,11 +36,11 @@ const QString REDIRECT_HIFI_ADDRESS = NetworkingConstants::REDIRECT_HIFI_ADDRESS const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; -const QString DEFAULT_VIRCADIA_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) - ? BuildInfo::INITIAL_STARTUP_LOCATION +const QString DEFAULT_VIRCADIA_ADDRESS = (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) + ? BuildInfo::PRELOADED_STARTUP_LOCATION : NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS; -const QString DEFAULT_HOME_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) - ? BuildInfo::INITIAL_STARTUP_LOCATION +const QString DEFAULT_HOME_ADDRESS = (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) + ? BuildInfo::PRELOADED_STARTUP_LOCATION : NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS; Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_VIRCADIA_ADDRESS); From 308ebc081223072caef68fcaa89c1471d8f8fc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Gro=C3=9F?= Date: Mon, 8 Mar 2021 20:46:27 +0100 Subject: [PATCH 54/86] Define undefined code blocks to fix Sphinx warnings --- INSTALLER.md | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/INSTALLER.md b/INSTALLER.md index 4f6ebc174d..4132ef7298 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -105,70 +105,70 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ##### Ubuntu 18.04 | .deb 1. Ensure you are using an Ubuntu 18.04 system. There is no required minimum to the amount of CPU cores needed, however it's recommended that you use as many as you have available in order to have an efficient experience. - ``` + ```text Recommended CPU Cores: 16 Minimum Disk Space: 40GB ``` 3. Get and bootstrap Vircadia Builder. - ``` + ```bash git clone https://github.com/vircadia/vircadia-builder.git cd vircadia-builder ``` 3. Run Vircadia Builder. - ``` + ```bash ./vircadia-builder --build server ``` 4. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step. - ``` + ```bash ./vircadia-builder --build server ``` 5. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.* 6. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to. - ``` + ```text Git repository: https://github.com/vircadia/vircadia/ # OR, for example Git repository: https://github.com/digisomni/vircadia/ ``` 7. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc" - ``` + ```text Git tag: master # OR, for example Git tag: v2021.1.0-rc ``` 8. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`. - ``` + ```text Release type: DEV # OR, for example we recommend you use Release type: PRODUCTION ``` 9. This value is the release version. Release numbers should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`. - ``` + ```text Release number: 2021.1.0 ``` 10. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`. - ``` + ```text Build number: fd6973b ``` 11. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user. - ``` + ```text Installation dir: /home/ubuntu/Vircadia ``` 12. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. - ``` + ```text CPU cores to use for Vircadia: 16 ``` 13. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. - ``` + ```text CPU cores to use for Qt5: 16 ``` 14. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit. 15. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times. 16. Navigate to the `pkg-scripts` directory. - ``` + ```bash cd ../Vircadia/source/pkg-scripts/ ``` 17. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.* - ``` + ```bash DEBVERSION="2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server ``` 18. If successful, the generated .deb package will be in the `pkg-scripts` folder. @@ -176,78 +176,78 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS ##### Amazon Linux 2 | .rpm 1. Ensure you are using an Amazon Linux 2 system. You will need many CPU cores to complete this process within a reasonable time. As an alternative to AWS EC2, you may use a [virtual machine](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-2-virtual-machine.html). Here are the recommended specs: - ``` + ```text AWS EC2 Instance Type: C5a.4xlarge Recommended CPU Cores: 16 Minimum Disk Space: 40GB ``` 2. Update the system and install dependencies. - ``` + ```bash sudo yum update -y sudo yum install git -y sudo yum install rpm-build ``` 3. Get and bootstrap Vircadia Builder. - ``` + ```bash git clone https://github.com/vircadia/vircadia-builder.git cd vircadia-builder sudo ./install_amazon_linux_deps.sh ``` 4. Run Vircadia Builder. - ``` + ```bash ./vircadia-builder --build server ``` 5. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step. - ``` + ```bash ./vircadia-builder --build server ``` 6. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.* 7. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to. - ``` + ```text Git repository: https://github.com/vircadia/vircadia/ # OR, for example Git repository: https://github.com/digisomni/vircadia/ ``` 8. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc". - ``` + ```text Git tag: master # OR, for example Git tag: v2021.1.0-rc ``` 9. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`. - ``` + ```text Release type: DEV # OR, for example we recommend you use Release type: PRODUCTION ``` 10. This value is the release version. Release numbers typically should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`. - ``` + ```text Release number: 2021.1.0 ``` 11. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`. - ``` + ```text Build number: fd6973b ``` 12. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user. - ``` + ```text Installation dir: /root/Vircadia ``` 13. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server. - ``` + ```text CPU cores to use for Vircadia: 16 ``` 14. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server. - ``` + ```text CPU cores to use for Qt5: 16 ``` 15. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit. 16. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times. 17. Navigate to the `pkg-scripts` directory. - ``` + ```bash cd ../Vircadia/source/pkg-scripts/ ``` 18. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.* - ``` + ```bash RPMVERSION="2021.1.0" ./make-rpm-server ``` 19. If successful, the generated .rpm package will be in the `pkg-scripts` folder of the Vircadia source files. From 3dc11637ed99003232d9ae98c0f9badb570bc07a Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Wed, 10 Mar 2021 01:27:05 -0500 Subject: [PATCH 55/86] Apply suggestions from code review Co-authored-by: David Rowe --- libraries/script-engine/src/ScriptGatekeeper.cpp | 4 ++-- libraries/script-engine/src/ScriptGatekeeper.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptGatekeeper.cpp b/libraries/script-engine/src/ScriptGatekeeper.cpp index d69e8c9561..d7ee1addd4 100644 --- a/libraries/script-engine/src/ScriptGatekeeper.cpp +++ b/libraries/script-engine/src/ScriptGatekeeper.cpp @@ -15,14 +15,14 @@ #include "SettingHandle.h" void ScriptGatekeeper::initialize() { - if (_initialized == true) { + if (_initialized) { return; } QVariant rawCurrentWhitelistValues = Setting::Handle(SCRIPT_WHITELIST_ENTRIES_KEY).get(); QString settingsSafeValues = rawCurrentWhitelistValues.toString(); - Setting::Handle whitelistEnabled{ SCRIPT_WHITELIST_ENABLED_KEY, false }; + Setting::Handle whitelistEnabled { SCRIPT_WHITELIST_ENABLED_KEY, false }; Setting::Handle isFirstRun { Settings::firstRun, true }; QString preloadedVal = BuildInfo::PRELOADED_SCRIPT_WHITELIST; diff --git a/libraries/script-engine/src/ScriptGatekeeper.h b/libraries/script-engine/src/ScriptGatekeeper.h index a73ef5995a..22bda8d5d5 100644 --- a/libraries/script-engine/src/ScriptGatekeeper.h +++ b/libraries/script-engine/src/ScriptGatekeeper.h @@ -23,7 +23,7 @@ public: QString SCRIPT_WHITELIST_ENTRIES_KEY{ "private/settingsSafeURLS" }; private: - bool _initialized{ false }; + bool _initialized { false }; }; #endif // vircadia_ScriptGatekeeper_h From 1f4cb7b9d6eae47ded2d09c27b466ca8c1b0af92 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Wed, 10 Mar 2021 01:27:32 -0500 Subject: [PATCH 56/86] Update ScriptGatekeeper.cpp --- libraries/script-engine/src/ScriptGatekeeper.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptGatekeeper.cpp b/libraries/script-engine/src/ScriptGatekeeper.cpp index d7ee1addd4..c223197a8c 100644 --- a/libraries/script-engine/src/ScriptGatekeeper.cpp +++ b/libraries/script-engine/src/ScriptGatekeeper.cpp @@ -29,7 +29,6 @@ void ScriptGatekeeper::initialize() { if (settingsSafeValues.isEmpty() && !preloadedVal.isEmpty() && isFirstRun.get()) { // We assume that the whitelist should be enabled if a preloaded whitelist is attached, so we activate it if it's not already active. - qDebug() << "hi" << !whitelistEnabled.get() << whitelistEnabled.get(); if (!whitelistEnabled.get()) { whitelistEnabled.set(true); } From 78cc98bc7b3d6549a47f08f975e518b74c6b35d8 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 11 Mar 2021 01:03:54 -0500 Subject: [PATCH 57/86] Add improved functionality for `Users.kick` API. Add banByUsername, banByFingerprint, and banByIP parameters. --- .../src/DomainServerSettingsManager.cpp | 103 ++++++++++++------ interface/src/Application.cpp | 6 +- interface/src/Application.h | 2 +- libraries/networking/src/NodeList.cpp | 8 +- libraries/networking/src/NodeList.h | 2 +- .../src/UsersScriptingInterface.cpp | 7 +- .../src/UsersScriptingInterface.h | 12 +- 7 files changed, 90 insertions(+), 50 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 08cf56b188..8d1a479407 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -863,6 +863,18 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerreadWithoutCopy(NUM_BYTES_RFC4122_UUID)); + bool hasOptionalBanParameters = false; + bool banByUsername = false; + bool banByFingerprint = false; + bool banByIP = false; + // pull optional ban parameters from the packet + if (message.data()->getSize() > NUM_BYTES_RFC4122_UUID) { + hasOptionalBanParameters = true; + message->readPrimitive(&banByUsername); + message->readPrimitive(&banByFingerprint); + message->readPrimitive(&banByIP); + } + if (!nodeUUID.isNull() && nodeUUID != sendingNode->getUUID()) { // make sure we actually have a node with this UUID auto limitedNodeList = DependencyManager::get(); @@ -881,24 +893,40 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointergetPermissions().getKey()]; - - newPermissions = !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain); - - // ensure that the connect permission is clear - userPermissions->clear(NodePermissions::Permission::canConnectToDomain); + // if we have optional ban parameters, we should ban the username based on the parameter + if (hasOptionalBanParameters) { + if (banByUsername) { + // check if there were already permissions + bool hadPermissions = havePermissionsForName(verifiedUsername); + + // grab or create permissions for the given username + auto userPermissions = _agentPermissions[matchingNode->getPermissions().getKey()]; + + newPermissions = !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain); + + // ensure that the connect permission is clear + userPermissions->clear(NodePermissions::Permission::canConnectToDomain); + } + } else { + // check if there were already permissions + bool hadPermissions = havePermissionsForName(verifiedUsername); + + // grab or create permissions for the given username + auto userPermissions = _agentPermissions[matchingNode->getPermissions().getKey()]; + + newPermissions = !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain); + + // ensure that the connect permission is clear + userPermissions->clear(NodePermissions::Permission::canConnectToDomain); + } } // if we didn't have a username, or this domain-server uses the "multi-kick" setting to // kick logged in users via username AND machine fingerprint (or IP as fallback) // then we remove connect permissions for the machine fingerprint (or IP as fallback) const QString MULTI_KICK_SETTINGS_KEYPATH = "security.multi_kick_logged_in"; - - if (verifiedUsername.isEmpty() || valueOrDefaultValueForKeyPath(MULTI_KICK_SETTINGS_KEYPATH).toBool()) { + qDebug() << "verifiedUsername.isEmpty()" << verifiedUsername.isEmpty(); + if (banByFingerprint || verifiedUsername.isEmpty() || valueOrDefaultValueForKeyPath(MULTI_KICK_SETTINGS_KEYPATH).toBool()) { // remove connect permissions for the machine fingerprint DomainServerNodeData* nodeData = static_cast(matchingNode->getLinkedData()); if (nodeData) { @@ -923,36 +951,39 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerclear(NodePermissions::Permission::canConnectToDomain); } } else { - // if no node data, all we can do is IP address - auto& kickAddress = matchingNode->getActiveSocket() - ? matchingNode->getActiveSocket()->getAddress() - : matchingNode->getPublicSocket().getAddress(); + // if no node data, all we can do is ban by IP address + banByIP = true; + } + } + + if (banByIP) { + auto& kickAddress = matchingNode->getActiveSocket() + ? matchingNode->getActiveSocket()->getAddress() + : matchingNode->getPublicSocket().getAddress(); - // probably isLoopback covers it, as whenever I try to ban an agent on same machine as the domain-server - // it is always 127.0.0.1, but looking at the public and local addresses just to be sure - // TODO: soon we will have feedback (in the form of a message to the client) after we kick. When we - // do, we will have a success flag, and perhaps a reason for failure. For now, just don't do it. - if (kickAddress == limitedNodeList->getPublicSockAddr().getAddress() || - kickAddress == limitedNodeList->getLocalSockAddr().getAddress() || - kickAddress.isLoopback() ) { - qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest"; - return; - } + // probably isLoopback covers it, as whenever I try to ban an agent on same machine as the domain-server + // it is always 127.0.0.1, but looking at the public and local addresses just to be sure + // TODO: soon we will have feedback (in the form of a message to the client) after we kick. When we + // do, we will have a success flag, and perhaps a reason for failure. For now, just don't do it. + if (kickAddress == limitedNodeList->getPublicSockAddr().getAddress() || + kickAddress == limitedNodeList->getLocalSockAddr().getAddress() || + kickAddress.isLoopback() ) { + qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest"; + return; + } + NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid()); - NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid()); + // check if there were already permissions for the IP + bool hadIPPermissions = hasPermissionsForIP(kickAddress); - // check if there were already permissions for the IP - bool hadIPPermissions = hasPermissionsForIP(kickAddress); + // grab or create permissions for the given IP address + auto ipPermissions = _ipPermissions[ipAddressKey]; - // grab or create permissions for the given IP address - auto ipPermissions = _ipPermissions[ipAddressKey]; + if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) { + newPermissions = true; - if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) { - newPermissions = true; - - ipPermissions->clear(NodePermissions::Permission::canConnectToDomain); - } + ipPermissions->clear(NodePermissions::Permission::canConnectToDomain); } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eec1efbd99..f2e17c8dc1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2493,7 +2493,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return viewFrustum.getPosition(); }); - DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID) { userKickConfirmation(nodeID); }); + DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { userKickConfirmation(nodeID, banByUsername, banByFingerprint, banByIP); }); render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { bool isTablet = url == TabletScriptingInterface::QML; @@ -3575,7 +3575,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { _desktopRootItemCreated = true; } -void Application::userKickConfirmation(const QUuid& nodeID) { +void Application::userKickConfirmation(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { auto avatarHashMap = DependencyManager::get(); auto avatar = avatarHashMap->getAvatarBySessionID(nodeID); @@ -3600,7 +3600,7 @@ void Application::userKickConfirmation(const QUuid& nodeID) { // ask the NodeList to kick the user with the given session ID if (yes) { - DependencyManager::get()->kickNodeBySessionID(nodeID); + DependencyManager::get()->kickNodeBySessionID(nodeID, banByUsername, banByFingerprint, banByIP); } DependencyManager::get()->setWaitForKickResponse(false); diff --git a/interface/src/Application.h b/interface/src/Application.h index 5cb5fdd5c0..65638b92db 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -608,7 +608,7 @@ private: void toggleTabletUI(bool shouldOpen = false) const; bool shouldCaptureMouse() const; - void userKickConfirmation(const QUuid& nodeID); + void userKickConfirmation(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP); MainWindow* _window; QElapsedTimer& _sessionRunTimer; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index a523a7ff36..6f3f774452 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -1263,17 +1263,21 @@ float NodeList::getInjectorGain() { return _injectorGain; } -void NodeList::kickNodeBySessionID(const QUuid& nodeID) { +void NodeList::kickNodeBySessionID(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) if (!nodeID.isNull() && getSessionUUID() != nodeID ) { if (getThisNodeCanKick()) { // setup the packet - auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID, true); + auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID + (sizeof(bool) * 3), true); // write the node ID to the packet kickPacket->write(nodeID.toRfc4122()); + // write the ban parameters to the packet + kickPacket->writePrimitive(banByUsername); + kickPacket->writePrimitive(banByFingerprint); + kickPacket->writePrimitive(banByIP); qCDebug(networking) << "Sending packet to kick node" << uuidStringWithoutCurlyBraces(nodeID); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 4954c53c84..3573db1b9f 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -86,7 +86,7 @@ public: void setInjectorGain(float gain); float getInjectorGain(); - void kickNodeBySessionID(const QUuid& nodeID); + void kickNodeBySessionID(const QUuid& nodeID, bool banByUsername = true, bool banByFingerprint = true, bool banByIP = false); void muteNodeBySessionID(const QUuid& nodeID); void requestUsernameFromSessionID(const QUuid& nodeID); bool getRequestsDomainListData() { return _requestsDomainListData; } diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 9beb52f20a..c22c1d270e 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2016-07-11. // Copyright 2016 High Fidelity, Inc. +// Copyright 2021 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 @@ -51,15 +52,15 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) { return DependencyManager::get()->getAvatarGain(nodeID); } -void UsersScriptingInterface::kick(const QUuid& nodeID) { +void UsersScriptingInterface::kick(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { if (_kickConfirmationOperator) { bool waitingForKickResponse = _kickResponseLock.resultWithReadLock([&] { return _waitingForKickResponse; }); if (getCanKick() && !waitingForKickResponse) { - _kickConfirmationOperator(nodeID); + _kickConfirmationOperator(nodeID, banByUsername, banByFingerprint, banByIP); } } else { - DependencyManager::get()->kickNodeBySessionID(nodeID); + DependencyManager::get()->kickNodeBySessionID(nodeID, banByUsername, banByFingerprint, banByIP); } } diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 3b0666481a..ad86189afa 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2016-07-11. // Copyright 2016 High Fidelity, Inc. +// Copyright 2021 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 @@ -41,7 +42,7 @@ class UsersScriptingInterface : public QObject, public Dependency { public: UsersScriptingInterface(); - void setKickConfirmationOperator(std::function kickConfirmationOperator) { + void setKickConfirmationOperator(std::function kickConfirmationOperator) { _kickConfirmationOperator = kickConfirmationOperator; } @@ -112,12 +113,15 @@ public slots: /**jsdoc * Kicks and bans a user. This removes them from the server and prevents them from returning. The ban is by user name if - * available, or machine fingerprint otherwise. + * available and by machine fingerprint. *

This function only works if you're an administrator of the domain you're in.

* @function Users.kick * @param {Uuid} sessionID - The session ID of the user to kick and ban. + * @param {boolean} [banByUsername=true] - Should ban the user's username. + * @param {boolean} [banByFingerprint=true] - Should ban the user's machine fingerprint. + * @param {boolean} [banByIP=false] - Should ban the user's IP address. */ - void kick(const QUuid& nodeID); + void kick(const QUuid& nodeID, bool banByUsername = true, bool banByFingerprint = true, bool banByIP = false); /**jsdoc * Mutes a user's microphone for everyone. The mute is not permanent: the user can unmute themselves. @@ -237,7 +241,7 @@ private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); - std::function _kickConfirmationOperator; + std::function _kickConfirmationOperator; ReadWriteLockable _kickResponseLock; bool _waitingForKickResponse { false }; From 712b5e508833b736e4af51a024ae391055cc7556 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 11 Mar 2021 17:06:07 -0500 Subject: [PATCH 58/86] Update DomainServerSettingsManager.cpp --- domain-server/src/DomainServerSettingsManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 8d1a479407..0b0db28876 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -925,7 +925,7 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer(matchingNode->getLinkedData()); From 4b61d3328d1795130efc2d391c4b00d6b8d0a603 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Fri, 12 Mar 2021 10:50:50 -0500 Subject: [PATCH 59/86] CR. --- .../src/messages/MessagesMixer.cpp | 47 +++++++++++-------- .../src/messages/MessagesMixer.h | 4 +- .../resources/describe-settings.json | 4 +- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index a4f5a65ce6..c54229b7f2 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -20,6 +20,7 @@ #include const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; +const int MESSAGES_MIXER_RATE_LIMITER_INTERVAL = 1000; // 1 second MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(message) { @@ -44,16 +45,17 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa QByteArray data; QUuid senderID; bool isText; + auto senderUUID = senderNode->getUUID(); MessagesClient::decodeMessagesPacket(receivedMessage, channel, isText, message, data, senderID); auto nodeList = DependencyManager::get(); - if (_allSubscribers.value(senderNode->getUUID()) >= _maxMessagesPerSecond) { + if (_allSubscribers.value(senderUUID) >= _maxMessagesPerSecond) { // We will reject all messages that go over this limit for that second. // FIXME: We will add logging options to offer analytics on this later. return; } - _allSubscribers[senderNode->getUUID()] = _allSubscribers.value(senderNode->getUUID()) + 1; + _allSubscribers[senderUUID] += 1; nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { @@ -67,20 +69,32 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa } void MessagesMixer::handleMessagesSubscribe(QSharedPointer message, SharedNodePointer senderNode) { + auto senderUUID = senderNode->getUUID(); QString channel = QString::fromUtf8(message->getMessage()); - _channelSubscribers[channel] << senderNode->getUUID(); - _allSubscribers[senderNode->getUUID()] << 0; + _channelSubscribers[channel] << senderUUID; + + _allSubscribers[senderUUID] = 0; } void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer message, SharedNodePointer senderNode) { + auto senderUUID = senderNode->getUUID(); QString channel = QString::fromUtf8(message->getMessage()); + if (_channelSubscribers.contains(channel)) { - _channelSubscribers[channel].remove(senderNode->getUUID()); + _channelSubscribers[channel].remove(senderUUID); } - if (_allSubscribers.contains(senderNode->getUUID())) { - _allSubscribers.remove(senderNode->getUUID()); + bool isSenderSubscribed = false; + QList> allChannels = _channelSubscribers.values(); + foreach (const QSet channel, allChannels) { + if (channel.contains(senderUUID)) { + isSenderSubscribed = true; + } + } + + if (!isSenderSubscribed && _allSubscribers.contains(senderUUID)) { + _allSubscribers.remove(senderUUID); } } @@ -114,8 +128,6 @@ void MessagesMixer::domainSettingsRequestComplete() { auto nodeList = DependencyManager::get(); nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); - DomainHandler& domainHandler = nodeList->getDomainHandler(); - // parse the settings to pull out the values we need parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject()); } @@ -126,11 +138,7 @@ void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) const QString NODE_MESSAGES_PER_SECOND_KEY = "max_node_messages_per_second"; QJsonValue maxMessagesPerSecondValue = messagesMixerGroupObject.value(NODE_MESSAGES_PER_SECOND_KEY); - if (!maxMessagesPerSecondValue.isUndefined()) { - _maxMessagesPerSecond = maxMessagesPerSecondValue.toInt(); - } else { - _maxMessagesPerSecond = DEFAULT_NODE_MESSAGES_PER_SECOND; - } + _maxMessagesPerSecond = maxMessagesPerSecondValue.toInt(DEFAULT_NODE_MESSAGES_PER_SECOND); } void MessagesMixer::processMaxMessagesContainer() { @@ -138,12 +146,13 @@ void MessagesMixer::processMaxMessagesContainer() { } void MessagesMixer::startMaxMessagesProcessor() { - maxMessagesPerSecondTimer = new QTimer(); - connect(maxMessagesPerSecondTimer, &QTimer::timeout, this, &MessagesMixer::processMaxMessagesContainer); - maxMessagesPerSecondTimer->start(1000); // Clear the container every second. + _maxMessagesTimer = new QTimer(); + connect(_maxMessagesTimer, &QTimer::timeout, this, &MessagesMixer::processMaxMessagesContainer); + _maxMessagesTimer->start(MESSAGES_MIXER_RATE_LIMITER_INTERVAL); // Clear the container every second. } void MessagesMixer::stopMaxMessagesProcessor() { - delete maxMessagesPerSecondTimer; - maxMessagesPerSecondTimer = NULL; + _maxMessagesTimer->stop(); + _maxMessagesTimer->deleteLater(); + _maxMessagesTimer = nullptr; } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 68c21e2c57..db64dbd0c8 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -44,9 +44,9 @@ private: QHash _allSubscribers; const int DEFAULT_NODE_MESSAGES_PER_SECOND = 1000; - int _maxMessagesPerSecond{ 0 }; + int _maxMessagesPerSecond { 0 }; - QTimer* maxMessagesPerSecondTimer; + QTimer* _maxMessagesTimer; }; #endif // hifi_MessagesMixer_h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index b2b56f8eff..32868c9b80 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1498,8 +1498,8 @@ { "name": "max_node_messages_per_second", "type": "int", - "label": "Per-Node Send Messages", - "help": "Desired maximum send messages (in messages per second) per node", + "label": "Maximum Message Rate", + "help": "Maximum message send rate (messages per second) per node", "placeholder": 1000, "default": 1000, "advanced": true From e12e74669877d24f56fa8dd6abb6394794b24010 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Fri, 12 Mar 2021 17:36:01 -0500 Subject: [PATCH 60/86] Optimize rate limiting on messaging mixer further. --- assignment-client/src/messages/MessagesMixer.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index c54229b7f2..1ab698b456 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -73,8 +73,6 @@ void MessagesMixer::handleMessagesSubscribe(QSharedPointer mess QString channel = QString::fromUtf8(message->getMessage()); _channelSubscribers[channel] << senderUUID; - - _allSubscribers[senderUUID] = 0; } void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer message, SharedNodePointer senderNode) { @@ -84,18 +82,6 @@ void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer me if (_channelSubscribers.contains(channel)) { _channelSubscribers[channel].remove(senderUUID); } - - bool isSenderSubscribed = false; - QList> allChannels = _channelSubscribers.values(); - foreach (const QSet channel, allChannels) { - if (channel.contains(senderUUID)) { - isSenderSubscribed = true; - } - } - - if (!isSenderSubscribed && _allSubscribers.contains(senderUUID)) { - _allSubscribers.remove(senderUUID); - } } void MessagesMixer::sendStatsPacket() { From ecda58dc2e9aa57f378b689d0e34b3703b6d17da Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sat, 13 Mar 2021 14:00:23 -0500 Subject: [PATCH 61/86] Tray "Visit Sandbox" -> "Visit Local Server" --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index e83b37ac4c..80be5c3fc2 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -460,7 +460,7 @@ var labels = { } }, goHome: { - label: 'Visit Sandbox', + label: 'Visit Local Server', click: visitSandboxClicked, enabled: false }, From 1ed767df41ae029ba8cb8f8164132871c02be390 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 14 Mar 2021 01:11:34 +0100 Subject: [PATCH 62/86] Implement Linux machine fingerprints We do this by using either /etc/machine-id, or hashing the MACs of all non-virtual networking devices. We make this fingerpint unique to our platform by adding our own string to the hash, and add some stretching for good measure. This way Vircadia's fingerprint isn't going to match the system's ID or anything another application generates. --- libraries/networking/src/FingerprintUtils.cpp | 79 ++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 216e0f28dd..83a4f19703 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #ifdef Q_OS_WIN #include @@ -36,8 +39,80 @@ QUuid FingerprintUtils::_machineFingerprint { QUuid() }; QString FingerprintUtils::getMachineFingerprintString() { QString uuidString; #ifdef Q_OS_LINUX - // sadly need to be root to get smbios guid from linux, so - // for now lets do nothing. + // As per the documentation: + // https://man7.org/linux/man-pages/man5/machine-id.5.html + // + // we use the machine id as a base, but add an application-specific key to it. + // If machine-id isn't available, we try hardware networking devices instead. + + QCryptographicHash hash(QCryptographicHash::Keccak_256); + + QFile machineIdFile("/etc/machine-id"); + if (!machineIdFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + // No machine ID, probably no systemd. + qCWarning(networking) << "Failed to open /etc/machine-id"; + + QDir netDevicesDir("/sys/class/net"); + QFileInfoList netDevicesInfo = netDevicesDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); + + if (netDevicesInfo.empty()) { + // Let getMachineFingerprint handle this contingency + qCWarning(networking) << "Failed to find any hardware networking devices"; + return ""; + } + + for(auto& fileInfo : netDevicesInfo) { + + if (fileInfo.isSymLink() && fileInfo.symLinkTarget().contains("virtual")) { + // symlink points to something like: + // ../../devices/virtual/net/lo + // these are not real devices and have random IDs, so we + // don't care about them. + continue; + } + + QFile addressFile(fileInfo.filePath() + "/address"); + if (addressFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCDebug(networking) << "Adding contents of " << addressFile.fileName(); + hash.addData(addressFile.readAll()); + } else { + qCWarning(networking) << "Failed to read " << addressFile.fileName(); + } + } + } else { + QByteArray data = machineIdFile.readAll(); + hash.addData(data); + } + + // Makes this hash unique to us + hash.addData("Vircadia"); + + // Stretching + for (int i=0; i < 65535; i++) { + hash.addData(hash.result()); + } + + QByteArray result = hash.result(); + result.resize(128 / 8); // GUIDs are 128 bit numbers + + // Set UUID version to 4, ensuring it's a valid UUID + result[6] = (result[6] & 0x0F) | 0x40; + + // Of course, Qt couldn't be nice and just parse something like: + // 1b1b9d6d45c2473bac13dc3011ff58d6 + // + // So we have to turn it into: + // {1b1b9d6d-45c2-473b-ac13-dc3011ff58d6} + + uuidString = result.toHex(); + uuidString.insert(20, '-'); + uuidString.insert(16, '-'); + uuidString.insert(12, '-'); + uuidString.insert(8, '-'); + uuidString.prepend("{"); + uuidString.append("}"); + + qCDebug(networking) << "Linux machine fingerprint:" << uuidString; #endif //Q_OS_LINUX #ifdef Q_OS_MAC From 13a9989fba0dd444e60d6fdf6117abacd77a50da Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 14 Mar 2021 19:28:41 -0400 Subject: [PATCH 63/86] Domain chat muted by default. --- scripts/communityScripts/chat/FloofChat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/communityScripts/chat/FloofChat.js b/scripts/communityScripts/chat/FloofChat.js index 1208a36e5f..26ab0d04e7 100644 --- a/scripts/communityScripts/chat/FloofChat.js +++ b/scripts/communityScripts/chat/FloofChat.js @@ -61,8 +61,8 @@ var settingsRoot = "FloofChat"; var vircadiaGotoUrl = "https://metaverse.vircadia.com/interim/d-goto/app/goto.json"; var gotoJSONUrl = Settings.getValue(settingsRoot + "/gotoJSONUrl", vircadiaGotoUrl); -var muted = Settings.getValue(settingsRoot + "/muted", {"Local": false, "Domain": false, "Grid": true}); -var mutedAudio = Settings.getValue(settingsRoot + "/mutedAudio", {"Local": false, "Domain": false, "Grid": true}); +var muted = Settings.getValue(settingsRoot + "/muted", {"Local": false, "Domain": true, "Grid": true}); +var mutedAudio = Settings.getValue(settingsRoot + "/mutedAudio", {"Local": false, "Domain": true, "Grid": true}); var notificationSound = SoundCache.getSound(Script.resolvePath("resources/bubblepop.wav")); var ws; From 9d15b00fbbda3cfac5c970e3e9fe928eeb7990d6 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 14 Mar 2021 21:50:08 -0400 Subject: [PATCH 64/86] Add check to stop processor before starting. --- assignment-client/src/messages/MessagesMixer.cpp | 4 ++++ assignment-client/src/messages/MessagesMixer.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 1ab698b456..e23eb5ba66 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -132,6 +132,10 @@ void MessagesMixer::processMaxMessagesContainer() { } void MessagesMixer::startMaxMessagesProcessor() { + if (_maxMessagesTimer) { + stopMaxMessagesProcessor(); + } + _maxMessagesTimer = new QTimer(); connect(_maxMessagesTimer, &QTimer::timeout, this, &MessagesMixer::processMaxMessagesContainer); _maxMessagesTimer->start(MESSAGES_MIXER_RATE_LIMITER_INTERVAL); // Clear the container every second. diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index db64dbd0c8..9a43aca347 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -46,7 +46,7 @@ private: const int DEFAULT_NODE_MESSAGES_PER_SECOND = 1000; int _maxMessagesPerSecond { 0 }; - QTimer* _maxMessagesTimer; + QTimer* _maxMessagesTimer { nullptr }; }; #endif // hifi_MessagesMixer_h From 008c0f0b1e4d311cfb31093765e4e19d4ad7a34f Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 14 Mar 2021 22:35:56 -0400 Subject: [PATCH 65/86] Add iterator to improve max messages check performance. --- assignment-client/src/messages/MessagesMixer.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index e23eb5ba66..eb004ddad7 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -49,13 +49,15 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa MessagesClient::decodeMessagesPacket(receivedMessage, channel, isText, message, data, senderID); auto nodeList = DependencyManager::get(); - if (_allSubscribers.value(senderUUID) >= _maxMessagesPerSecond) { - // We will reject all messages that go over this limit for that second. - // FIXME: We will add logging options to offer analytics on this later. - return; - } - _allSubscribers[senderUUID] += 1; + auto itr = _allSubscribers.find(senderUUID); + if (itr == _allSubscribers.end()) { + _allSubscribers[senderUUID] = 1; + } else if (*itr >= _maxMessagesPerSecond) { // this syntax might be wrong I forget + return; + } else { + *itr += 1; + } nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { From 14d1af05a3593b8cdbada99d5c00fc9e63497271 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 14 Mar 2021 22:36:32 -0400 Subject: [PATCH 66/86] Missed comment. --- assignment-client/src/messages/MessagesMixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index eb004ddad7..010f7f2f9e 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -53,7 +53,7 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa auto itr = _allSubscribers.find(senderUUID); if (itr == _allSubscribers.end()) { _allSubscribers[senderUUID] = 1; - } else if (*itr >= _maxMessagesPerSecond) { // this syntax might be wrong I forget + } else if (*itr >= _maxMessagesPerSecond) { return; } else { *itr += 1; From c278122f9ae1b292c252c5f4e32fe4b7fbef39d3 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Mon, 15 Mar 2021 01:26:07 -0400 Subject: [PATCH 67/86] CR. --- assignment-client/src/messages/MessagesMixer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 010f7f2f9e..017d3a80b7 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -103,8 +103,9 @@ void MessagesMixer::sendStatsPacket() { } void MessagesMixer::run() { - // wait until we have the domain-server settings, otherwise we bail - DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); + auto nodeList = DependencyManager::get(); + nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); + DomainHandler& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, &DomainHandler::settingsReceived, this, &MessagesMixer::domainSettingsRequestComplete); ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); @@ -114,7 +115,6 @@ void MessagesMixer::run() { void MessagesMixer::domainSettingsRequestComplete() { auto nodeList = DependencyManager::get(); - nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); // parse the settings to pull out the values we need parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject()); From 440618825d1d0b797e50d77aa25ea69c7caf94a1 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Mon, 15 Mar 2021 20:24:25 -0400 Subject: [PATCH 68/86] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d260c76d0d..830236f458 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,11 @@ Vircadiaâ„¢ is a 3D social software project seeking to incrementally bring about ### How to generate an Installer -- [For Windows](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md) -- [For Mac](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x) -- [For Linux - AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages) +- [For Windows - Interface & Server](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md) +- [For Mac - Interface](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x) +- [For Linux - Server .deb - Vircadia Builder](INSTALLER.md#ubuntu-1804--deb) +- [For Linux - Server .rpm - Vircadia Builder](INSTALLER.md#amazon-linux-2--rpm) +- [For Linux - Interface AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages) ### Boot to Metaverse: [The Goal](https://vircadia.com/vision/) From d4c2829147abb561e168559c905f131718e401d3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 16 Mar 2021 21:22:19 +1300 Subject: [PATCH 69/86] Fix program log spam after using Audio dialog --- interface/resources/qml/hifi/audio/Audio.qml | 24 +++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 1f20486e1e..0f7d521653 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -692,10 +692,28 @@ Rectangle { visible: !status.visible; + function onNoiseGateOpened() { + noiseBar.gated = false; + } + + function onNoiseGateClosed() { + noiseBar.gated = true; + } + + function onInputLevelChanged(level) { + noiseBar.level = level; + } + Component.onCompleted: { - AudioScriptingInterface.noiseGateOpened.connect(function () { noiseBar.gated = false; }); - AudioScriptingInterface.noiseGateClosed.connect(function () { noiseBar.gated = true; }); - AudioScriptingInterface.inputLevelChanged.connect(function () { noiseBar.level = AudioScriptingInterface.inputLevel; }); + AudioScriptingInterface.noiseGateOpened.connect(onNoiseGateOpened); + AudioScriptingInterface.noiseGateClosed.connect(onNoiseGateClosed); + AudioScriptingInterface.inputLevelChanged.connect(onInputLevelChanged); + } + + Component.onDestruction: { + AudioScriptingInterface.noiseGateOpened.disconnect(onNoiseGateOpened); + AudioScriptingInterface.noiseGateClosed.disconnect(onNoiseGateClosed); + AudioScriptingInterface.inputLevelChanged.disconnect(onInputLevelChanged); } Rectangle { // base From 0216b3468119bdd0b3fdceaec6f5dcb1b70dfe06 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Wed, 17 Mar 2021 19:37:06 -0400 Subject: [PATCH 70/86] State of refactoring --- .../src/DomainServerSettingsManager.cpp | 39 +++++++------------ interface/src/Application.cpp | 6 +-- interface/src/Application.h | 2 +- libraries/networking/src/NodeList.cpp | 20 +++++++--- libraries/networking/src/NodeList.h | 2 +- .../UserActivityLoggerScriptingInterface.h | 1 + .../src/UsersScriptingInterface.cpp | 7 ++-- .../src/UsersScriptingInterface.h | 14 +++++-- libraries/shared/src/ModerationFlags.h | 28 +++++++++++++ 9 files changed, 77 insertions(+), 42 deletions(-) create mode 100644 libraries/shared/src/ModerationFlags.h diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 0b0db28876..0f5f4741d9 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include "DomainServerNodeData.h" @@ -864,15 +865,17 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerreadWithoutCopy(NUM_BYTES_RFC4122_UUID)); bool hasOptionalBanParameters = false; - bool banByUsername = false; - bool banByFingerprint = false; - bool banByIP = false; + int banParameters; + bool banByUsername; + bool banByFingerprint; + bool banByIP; // pull optional ban parameters from the packet if (message.data()->getSize() > NUM_BYTES_RFC4122_UUID) { hasOptionalBanParameters = true; - message->readPrimitive(&banByUsername); - message->readPrimitive(&banByFingerprint); - message->readPrimitive(&banByIP); + message->readPrimitive(&banParameters); + banByUsername = banParameters & ModerationFlags::BanFlags::BAN_BY_USERNAME; + banByFingerprint = banParameters & ModerationFlags::BanFlags::BAN_BY_FINGERPRINT; + banByIP = banParameters & ModerationFlags::BanFlags::BAN_BY_IP; } if (!nodeUUID.isNull() && nodeUUID != sendingNode->getUUID()) { @@ -894,28 +897,16 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointergetPermissions().getKey()]; - - newPermissions = !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain); - - // ensure that the connect permission is clear - userPermissions->clear(NodePermissions::Permission::canConnectToDomain); - } - } else { + if (!hasOptionalBanParameters || banByUsername) { // check if there were already permissions bool hadPermissions = havePermissionsForName(verifiedUsername); - + // grab or create permissions for the given username auto userPermissions = _agentPermissions[matchingNode->getPermissions().getKey()]; - - newPermissions = !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain); - + + newPermissions = + !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain); + // ensure that the connect permission is clear userPermissions->clear(NodePermissions::Permission::canConnectToDomain); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f2e17c8dc1..18f993f2f7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2493,7 +2493,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return viewFrustum.getPosition(); }); - DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { userKickConfirmation(nodeID, banByUsername, banByFingerprint, banByIP); }); + DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, int banFlags) { userKickConfirmation(nodeID, banFlags); }); render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { bool isTablet = url == TabletScriptingInterface::QML; @@ -3575,7 +3575,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { _desktopRootItemCreated = true; } -void Application::userKickConfirmation(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { +void Application::userKickConfirmation(const QUuid& nodeID, int banFlags) { auto avatarHashMap = DependencyManager::get(); auto avatar = avatarHashMap->getAvatarBySessionID(nodeID); @@ -3600,7 +3600,7 @@ void Application::userKickConfirmation(const QUuid& nodeID, bool banByUsername, // ask the NodeList to kick the user with the given session ID if (yes) { - DependencyManager::get()->kickNodeBySessionID(nodeID, banByUsername, banByFingerprint, banByIP); + DependencyManager::get()->kickNodeBySessionID(nodeID, banFlags); } DependencyManager::get()->setWaitForKickResponse(false); diff --git a/interface/src/Application.h b/interface/src/Application.h index 65638b92db..aa2c62658e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -608,7 +608,7 @@ private: void toggleTabletUI(bool shouldOpen = false) const; bool shouldCaptureMouse() const; - void userKickConfirmation(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP); + void userKickConfirmation(const QUuid& nodeID, int banFlags); MainWindow* _window; QElapsedTimer& _sessionRunTimer; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 6f3f774452..e67423a24a 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -42,6 +42,7 @@ #include "udt/PacketHeaders.h" #include "SharedUtil.h" #include +#include using namespace std::chrono; @@ -1263,21 +1264,30 @@ float NodeList::getInjectorGain() { return _injectorGain; } -void NodeList::kickNodeBySessionID(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { +void NodeList::kickNodeBySessionID(const QUuid& nodeID, int banFlags) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) if (!nodeID.isNull() && getSessionUUID() != nodeID ) { if (getThisNodeCanKick()) { // setup the packet - auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID + (sizeof(bool) * 3), true); + auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID + sizeof(int), true); + + //int banParameters {}; + //if (banByUsername) { + // banParameters |= ModerationFlags::BanFlags::BAN_BY_USERNAME; + //} + //if (banByFingerprint) { + // banParameters |= ModerationFlags::BanFlags::BAN_BY_FINGERPRINT; + //} + //if (banByIP) { + // banParameters |= ModerationFlags::BanFlags::BAN_BY_IP; + //} // write the node ID to the packet kickPacket->write(nodeID.toRfc4122()); // write the ban parameters to the packet - kickPacket->writePrimitive(banByUsername); - kickPacket->writePrimitive(banByFingerprint); - kickPacket->writePrimitive(banByIP); + kickPacket->writePrimitive(banFlags); qCDebug(networking) << "Sending packet to kick node" << uuidStringWithoutCurlyBraces(nodeID); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 3573db1b9f..673b5dab55 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -86,7 +86,7 @@ public: void setInjectorGain(float gain); float getInjectorGain(); - void kickNodeBySessionID(const QUuid& nodeID, bool banByUsername = true, bool banByFingerprint = true, bool banByIP = false); + void kickNodeBySessionID(const QUuid& nodeID, int banFlags); void muteNodeBySessionID(const QUuid& nodeID); void requestUsernameFromSessionID(const QUuid& nodeID); bool getRequestsDomainListData() { return _requestsDomainListData; } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 1cda1235e9..94e1590e88 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -1,3 +1,4 @@ + // // UserActivityLoggerScriptingInterface.h // libraries/networking/src diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index c22c1d270e..9b580528d5 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -52,15 +52,14 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) { return DependencyManager::get()->getAvatarGain(nodeID); } -void UsersScriptingInterface::kick(const QUuid& nodeID, bool banByUsername, bool banByFingerprint, bool banByIP) { - +void UsersScriptingInterface::kick(const QUuid& nodeID, int banFlags) { if (_kickConfirmationOperator) { bool waitingForKickResponse = _kickResponseLock.resultWithReadLock([&] { return _waitingForKickResponse; }); if (getCanKick() && !waitingForKickResponse) { - _kickConfirmationOperator(nodeID, banByUsername, banByFingerprint, banByIP); + _kickConfirmationOperator(nodeID, banFlags); } } else { - DependencyManager::get()->kickNodeBySessionID(nodeID, banByUsername, banByFingerprint, banByIP); + DependencyManager::get()->kickNodeBySessionID(nodeID, banFlags); } } diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index ad86189afa..c103508719 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -17,6 +17,7 @@ #include #include +#include /**jsdoc * The Users API provides features to regulate your interaction with other users. @@ -40,9 +41,14 @@ class UsersScriptingInterface : public QObject, public Dependency { Q_PROPERTY(bool canKick READ getCanKick) Q_PROPERTY(bool requestsDomainListData READ getRequestsDomainListData WRITE setRequestsDomainListData) + Q_PROPERTY(unsigned int NO_BAN READ ModerationFlags::BanFlags::NO_BAN CONSTANT) + Q_PROPERTY(unsigned int BAN_BY_USERNAME READ ModerationFlags::BanFlags::BAN_BY_USERNAME CONSTANT) + Q_PROPERTY(unsigned int BAN_BY_FINGERPRINT READ ModerationFlags::BanFlags::BAN_BY_FINGERPRINT CONSTANT) + Q_PROPERTY(unsigned int BAN_BY_IP READ ModerationFlags::BanFlags::BAN_BY_IP CONSTANT) + public: UsersScriptingInterface(); - void setKickConfirmationOperator(std::function kickConfirmationOperator) { + void setKickConfirmationOperator(std::function kickConfirmationOperator) { _kickConfirmationOperator = kickConfirmationOperator; } @@ -113,7 +119,7 @@ public slots: /**jsdoc * Kicks and bans a user. This removes them from the server and prevents them from returning. The ban is by user name if - * available and by machine fingerprint. + * available and by machine fingerprint. The ban functionality can be controlled with flags. *

This function only works if you're an administrator of the domain you're in.

* @function Users.kick * @param {Uuid} sessionID - The session ID of the user to kick and ban. @@ -121,7 +127,7 @@ public slots: * @param {boolean} [banByFingerprint=true] - Should ban the user's machine fingerprint. * @param {boolean} [banByIP=false] - Should ban the user's IP address. */ - void kick(const QUuid& nodeID, bool banByUsername = true, bool banByFingerprint = true, bool banByIP = false); + void kick(const QUuid& nodeID, int banFlags = 0); /**jsdoc * Mutes a user's microphone for everyone. The mute is not permanent: the user can unmute themselves. @@ -241,7 +247,7 @@ private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); - std::function _kickConfirmationOperator; + std::function _kickConfirmationOperator; ReadWriteLockable _kickResponseLock; bool _waitingForKickResponse { false }; diff --git a/libraries/shared/src/ModerationFlags.h b/libraries/shared/src/ModerationFlags.h new file mode 100644 index 0000000000..3930f65e28 --- /dev/null +++ b/libraries/shared/src/ModerationFlags.h @@ -0,0 +1,28 @@ +// +// ModerationFlags.h +// libraries/shared/src +// +// Created by Kalila L. on Mar 11 2021. +// Copyright 2021 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 +// + +#ifndef vircadia_ModerationFlags_h +#define vircadia_ModerationFlags_h + +class ModerationFlags { +public: + enum BanFlags + { + NO_BAN = 0, + BAN_BY_USERNAME = 1, + BAN_BY_FINGERPRINT = 2, + BAN_BY_IP = 4 + }; + + int test(int lol) { return lol; }; +}; + +#endif // vircadia_ModerationFlags_h \ No newline at end of file From be0bd3940d8542b4c039672131184f7b256768de Mon Sep 17 00:00:00 2001 From: Kalila L Date: Wed, 17 Mar 2021 23:25:08 -0400 Subject: [PATCH 71/86] Finish flag work on kick functionality. --- interface/src/Application.h | 3 ++- .../src/UsersScriptingInterface.h | 19 +++++++++++-------- libraries/shared/src/ModerationFlags.h | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index aa2c62658e..cb1230c850 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -50,6 +50,7 @@ #include #include #include +#include #include "avatar/MyAvatar.h" #include "FancyCamera.h" @@ -608,7 +609,7 @@ private: void toggleTabletUI(bool shouldOpen = false) const; bool shouldCaptureMouse() const; - void userKickConfirmation(const QUuid& nodeID, int banFlags); + void userKickConfirmation(const QUuid& nodeID, int banFlags = ModerationFlags::getDefaultBanFlags()); MainWindow* _window; QElapsedTimer& _sessionRunTimer; diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index c103508719..42e63cdbf1 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -41,10 +41,10 @@ class UsersScriptingInterface : public QObject, public Dependency { Q_PROPERTY(bool canKick READ getCanKick) Q_PROPERTY(bool requestsDomainListData READ getRequestsDomainListData WRITE setRequestsDomainListData) - Q_PROPERTY(unsigned int NO_BAN READ ModerationFlags::BanFlags::NO_BAN CONSTANT) - Q_PROPERTY(unsigned int BAN_BY_USERNAME READ ModerationFlags::BanFlags::BAN_BY_USERNAME CONSTANT) - Q_PROPERTY(unsigned int BAN_BY_FINGERPRINT READ ModerationFlags::BanFlags::BAN_BY_FINGERPRINT CONSTANT) - Q_PROPERTY(unsigned int BAN_BY_IP READ ModerationFlags::BanFlags::BAN_BY_IP CONSTANT) + Q_PROPERTY(unsigned int NO_BAN READ getNoBan CONSTANT) + Q_PROPERTY(unsigned int BAN_BY_USERNAME READ getBanByUsername CONSTANT) + Q_PROPERTY(unsigned int BAN_BY_FINGERPRINT READ getBanByFingerprint CONSTANT) + Q_PROPERTY(unsigned int BAN_BY_IP READ getBanByIP CONSTANT) public: UsersScriptingInterface(); @@ -123,11 +123,9 @@ public slots: *

This function only works if you're an administrator of the domain you're in.

* @function Users.kick * @param {Uuid} sessionID - The session ID of the user to kick and ban. - * @param {boolean} [banByUsername=true] - Should ban the user's username. - * @param {boolean} [banByFingerprint=true] - Should ban the user's machine fingerprint. - * @param {boolean} [banByIP=false] - Should ban the user's IP address. + * @param {number} - Preferred ban flags. Bans a user by username (if available) and machine fingerprint by default. */ - void kick(const QUuid& nodeID, int banFlags = 0); + void kick(const QUuid& nodeID, int banFlags = ModerationFlags::getDefaultBanFlags()); /**jsdoc * Mutes a user's microphone for everyone. The mute is not permanent: the user can unmute themselves. @@ -247,6 +245,11 @@ private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); + static constexpr unsigned int getNoBan() { return ModerationFlags::BanFlags::NO_BAN; }; + static constexpr unsigned int getBanByUsername() { return ModerationFlags::BanFlags::BAN_BY_USERNAME; }; + static constexpr unsigned int getBanByFingerprint() { return ModerationFlags::BanFlags::BAN_BY_FINGERPRINT; }; + static constexpr unsigned int getBanByIP() { return ModerationFlags::BanFlags::BAN_BY_IP; }; + std::function _kickConfirmationOperator; ReadWriteLockable _kickResponseLock; diff --git a/libraries/shared/src/ModerationFlags.h b/libraries/shared/src/ModerationFlags.h index 3930f65e28..2724e353e6 100644 --- a/libraries/shared/src/ModerationFlags.h +++ b/libraries/shared/src/ModerationFlags.h @@ -22,7 +22,7 @@ public: BAN_BY_IP = 4 }; - int test(int lol) { return lol; }; + static constexpr unsigned int getDefaultBanFlags() { return (BanFlags::BAN_BY_USERNAME | BanFlags::BAN_BY_FINGERPRINT); }; }; #endif // vircadia_ModerationFlags_h \ No newline at end of file From c6c3dc66ea8b7e3dd6b6ef265acd40bee3015336 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Thu, 18 Mar 2021 01:39:13 -0400 Subject: [PATCH 72/86] Add IP ban button to the "People" app. --- interface/resources/qml/hifi/Pal.qml | 39 ++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 55f2bb80b1..67ba8bd7aa 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -506,6 +506,7 @@ Rectangle { id: itemCell; property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore"; property bool isButton: styleData.role === "mute" || styleData.role === "kick"; + property bool isBan: styleData.role === "kick"; property bool isAvgAudio: styleData.role === "avgAudioLevel"; opacity: !isButton ? (model && model.isPresent ? 1.0 : 0.4) : 1.0; // Admin actions shouldn't turn gray @@ -605,7 +606,9 @@ Rectangle { color: 2; // Red visible: isButton; enabled: !nameCard.isReplicated; - anchors.centerIn: parent; + anchors.verticalCenter: itemCell.verticalCenter; + anchors.left: parent.left; + anchors.leftMargin: styleData.role === "kick" ? 4 : 18; width: 32; height: 32; onClicked: { @@ -620,7 +623,39 @@ Rectangle { HiFiGlyphs { text: (styleData.role === "kick") ? hifi.glyphs.error : hifi.glyphs.muted; // Size - size: parent.height*1.3; + size: parent.height * 1.3; + // Anchors + anchors.fill: parent; + // Style + horizontalAlignment: Text.AlignHCenter; + color: enabled ? hifi.buttons.textColor[actionButton.color] + : hifi.buttons.disabledTextColor[actionButton.colorScheme]; + } + } + + HifiControlsUit.Button { + id: hardBanButton; + color: 2; // Red + visible: isBan; + enabled: !nameCard.isReplicated; + anchors.verticalCenter: itemCell.verticalCenter; + anchors.left: parent.left; + anchors.leftMargin: actionButton.width + 14; + width: 32; + height: 32; + onClicked: { + Users[styleData.role](model.sessionId, 7); // Ban params add up to 7 from Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP + UserActivityLogger["palAction"](styleData.role, model.sessionId); + if (styleData.role === "kick") { + nearbyUserModelData.splice(model.userIndex, 1); + nearbyUserModel.remove(model.userIndex); // after changing nearbyUserModelData, b/c ListModel can frob the data + } + } + // muted/error glyphs + HiFiGlyphs { + text: hifi.glyphs.alert; + // Size + size: parent.height * 1.3; // Anchors anchors.fill: parent; // Style From da7bcf15846d764d45449e1af907438033242097 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 18 Mar 2021 17:53:21 -0400 Subject: [PATCH 73/86] Update NodeList.cpp --- libraries/networking/src/NodeList.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e67423a24a..b6660e6c59 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -1273,17 +1273,6 @@ void NodeList::kickNodeBySessionID(const QUuid& nodeID, int banFlags) { // setup the packet auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID + sizeof(int), true); - //int banParameters {}; - //if (banByUsername) { - // banParameters |= ModerationFlags::BanFlags::BAN_BY_USERNAME; - //} - //if (banByFingerprint) { - // banParameters |= ModerationFlags::BanFlags::BAN_BY_FINGERPRINT; - //} - //if (banByIP) { - // banParameters |= ModerationFlags::BanFlags::BAN_BY_IP; - //} - // write the node ID to the packet kickPacket->write(nodeID.toRfc4122()); // write the ban parameters to the packet From 9dadc44f6ce93072b4afa844f4b0b13144b909b4 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Thu, 18 Mar 2021 17:57:49 -0400 Subject: [PATCH 74/86] Update UserActivityLoggerScriptingInterface.h --- libraries/networking/src/UserActivityLoggerScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 94e1590e88..ea01a8446c 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -1,10 +1,10 @@ - // // UserActivityLoggerScriptingInterface.h // libraries/networking/src // // Created by Ryan Huffman on 6/06/16. // Copyright 2016 High Fidelity, Inc. +// Copyright 2021 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 From fd592dfb061afe2e52a04c293426620dc09bb329 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Fri, 19 Mar 2021 04:11:26 -0400 Subject: [PATCH 75/86] CR. --- domain-server/src/DomainServerSettingsManager.cpp | 2 +- interface/resources/qml/hifi/Pal.qml | 2 +- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- libraries/networking/src/NodeList.cpp | 2 +- libraries/networking/src/NodeList.h | 2 +- libraries/script-engine/src/UsersScriptingInterface.cpp | 2 +- libraries/script-engine/src/UsersScriptingInterface.h | 6 +++--- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 0f5f4741d9..b113c28318 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -870,7 +870,7 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointergetSize() > NUM_BYTES_RFC4122_UUID) { + if (message.data()->getSize() == (NUM_BYTES_RFC4122_UUID + sizeof(int))) { hasOptionalBanParameters = true; message->readPrimitive(&banParameters); banByUsername = banParameters & ModerationFlags::BanFlags::BAN_BY_USERNAME; diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 67ba8bd7aa..d35ae5ea90 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -644,7 +644,7 @@ Rectangle { width: 32; height: 32; onClicked: { - Users[styleData.role](model.sessionId, 7); // Ban params add up to 7 from Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP + Users[styleData.role](model.sessionId, Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP); // Ban params add up to 7 from Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP UserActivityLogger["palAction"](styleData.role, model.sessionId); if (styleData.role === "kick") { nearbyUserModelData.splice(model.userIndex, 1); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 18f993f2f7..59371176be 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2493,7 +2493,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return viewFrustum.getPosition(); }); - DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, int banFlags) { userKickConfirmation(nodeID, banFlags); }); + DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, unsigned int banFlags) { userKickConfirmation(nodeID, banFlags); }); render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { bool isTablet = url == TabletScriptingInterface::QML; @@ -3575,7 +3575,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { _desktopRootItemCreated = true; } -void Application::userKickConfirmation(const QUuid& nodeID, int banFlags) { +void Application::userKickConfirmation(const QUuid& nodeID, unsigned int banFlags) { auto avatarHashMap = DependencyManager::get(); auto avatar = avatarHashMap->getAvatarBySessionID(nodeID); diff --git a/interface/src/Application.h b/interface/src/Application.h index cb1230c850..18f90e8db9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -609,7 +609,7 @@ private: void toggleTabletUI(bool shouldOpen = false) const; bool shouldCaptureMouse() const; - void userKickConfirmation(const QUuid& nodeID, int banFlags = ModerationFlags::getDefaultBanFlags()); + void userKickConfirmation(const QUuid& nodeID, unsigned int banFlags = ModerationFlags::getDefaultBanFlags()); MainWindow* _window; QElapsedTimer& _sessionRunTimer; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b6660e6c59..45df4d57f9 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -1264,7 +1264,7 @@ float NodeList::getInjectorGain() { return _injectorGain; } -void NodeList::kickNodeBySessionID(const QUuid& nodeID, int banFlags) { +void NodeList::kickNodeBySessionID(const QUuid& nodeID, unsigned int banFlags) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 673b5dab55..c861dd26e7 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -86,7 +86,7 @@ public: void setInjectorGain(float gain); float getInjectorGain(); - void kickNodeBySessionID(const QUuid& nodeID, int banFlags); + void kickNodeBySessionID(const QUuid& nodeID, unsigned int banFlags); void muteNodeBySessionID(const QUuid& nodeID); void requestUsernameFromSessionID(const QUuid& nodeID); bool getRequestsDomainListData() { return _requestsDomainListData; } diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 9b580528d5..883c728c2f 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -52,7 +52,7 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) { return DependencyManager::get()->getAvatarGain(nodeID); } -void UsersScriptingInterface::kick(const QUuid& nodeID, int banFlags) { +void UsersScriptingInterface::kick(const QUuid& nodeID, unsigned int banFlags) { if (_kickConfirmationOperator) { bool waitingForKickResponse = _kickResponseLock.resultWithReadLock([&] { return _waitingForKickResponse; }); if (getCanKick() && !waitingForKickResponse) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 42e63cdbf1..e61c8734f3 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -48,7 +48,7 @@ class UsersScriptingInterface : public QObject, public Dependency { public: UsersScriptingInterface(); - void setKickConfirmationOperator(std::function kickConfirmationOperator) { + void setKickConfirmationOperator(std::function kickConfirmationOperator) { _kickConfirmationOperator = kickConfirmationOperator; } @@ -125,7 +125,7 @@ public slots: * @param {Uuid} sessionID - The session ID of the user to kick and ban. * @param {number} - Preferred ban flags. Bans a user by username (if available) and machine fingerprint by default. */ - void kick(const QUuid& nodeID, int banFlags = ModerationFlags::getDefaultBanFlags()); + void kick(const QUuid& nodeID, unsigned int banFlags = ModerationFlags::getDefaultBanFlags()); /**jsdoc * Mutes a user's microphone for everyone. The mute is not permanent: the user can unmute themselves. @@ -250,7 +250,7 @@ private: static constexpr unsigned int getBanByFingerprint() { return ModerationFlags::BanFlags::BAN_BY_FINGERPRINT; }; static constexpr unsigned int getBanByIP() { return ModerationFlags::BanFlags::BAN_BY_IP; }; - std::function _kickConfirmationOperator; + std::function _kickConfirmationOperator; ReadWriteLockable _kickResponseLock; bool _waitingForKickResponse { false }; From f067feb37a5f19bd4a1ab314447c307c4d91af31 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Fri, 19 Mar 2021 04:44:58 -0400 Subject: [PATCH 76/86] Add docs for ban functionality. --- .../script-engine/src/UsersScriptingInterface.h | 8 ++++++-- libraries/shared/src/ModerationFlags.h | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index e61c8734f3..a02553eca8 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -33,6 +33,10 @@ * false. Read-only. * @property {boolean} requestsDomainListData - true if the client requests extra data from the mixers (such as * positional data of an avatar they've ignored). Read-only. + * @property {BanFlags} NO_BAN - Do not ban person. Read-only. + * @property {BanFlags} BAN_BY_USERNAME - Ban person by username. Read-only. + * @property {BanFlags} BAN_BY_FINGERPRINT - Ban person by fingerprint. Read-only. + * @property {BanFlags} BAN_BY_IP - Ban person by IP address. Read-only. */ class UsersScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -118,8 +122,8 @@ public slots: float getAvatarGain(const QUuid& nodeID); /**jsdoc - * Kicks and bans a user. This removes them from the server and prevents them from returning. The ban is by user name if - * available and by machine fingerprint. The ban functionality can be controlled with flags. + * Kicks and bans a user. This removes them from the server and prevents them from returning. The ban is by user name (if + * available) and by machine fingerprint. The ban functionality can be controlled with flags. *

This function only works if you're an administrator of the domain you're in.

* @function Users.kick * @param {Uuid} sessionID - The session ID of the user to kick and ban. diff --git a/libraries/shared/src/ModerationFlags.h b/libraries/shared/src/ModerationFlags.h index 2724e353e6..9bdd4bfbea 100644 --- a/libraries/shared/src/ModerationFlags.h +++ b/libraries/shared/src/ModerationFlags.h @@ -14,6 +14,23 @@ class ModerationFlags { public: + + /**jsdoc + *

A set of flags for moderation ban actions. The value is constructed by using the | (bitwise OR) operator on the + * individual flag values.

+ * + * + * + * + * + * + * + * + * + * + *
Flag NameValueDescription
NO_BAN0Don't ban user when kicking. This does not currently have an effect.
BAN_BY_USERNAME1Ban the person by their username.
BAN_BY_FINGERPRINT2Ban the person by their machine fingerprint.
BAN_BY_IP4Ban the person by their IP address.
+ * @typedef {number} BanFlags + */ enum BanFlags { NO_BAN = 0, From 35e836d34d481a32728fd0a53b143b88c4b8d768 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 19 Mar 2021 22:32:29 -0700 Subject: [PATCH 77/86] image caching must maintain original image information and pass it around --- libraries/baking/src/TextureBaker.cpp | 22 ++-- .../src/RenderableImageEntityItem.cpp | 12 +- libraries/gpu/src/gpu/Texture.h | 8 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 64 ++++----- .../image/src/image/TextureProcessing.cpp | 10 +- libraries/image/src/image/TextureProcessing.h | 6 +- .../src/material-networking/TextureCache.cpp | 121 +++++++++--------- .../src/material-networking/TextureCache.h | 6 +- 8 files changed, 128 insertions(+), 121 deletions(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index d70c6586b7..d9b63d6b7a 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -168,20 +168,20 @@ void TextureBaker::processTexture() { gpu::BackendTarget::GLES32 }}; for (auto target : BACKEND_TARGETS) { - auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE, - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, - target, _abortProcessing); - if (!processedTexture) { + auto processedTextureAndSize = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE, + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, + target, _abortProcessing); + if (!processedTextureAndSize.first) { handleError("Could not process texture " + _textureURL.toString()); return; } - processedTexture->setSourceHash(hash); + processedTextureAndSize.first->setSourceHash(hash); if (shouldStop()) { return; } - auto memKTX = gpu::Texture::serialize(*processedTexture); + auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second); if (!memKTX) { handleError("Could not serialize " + _textureURL.toString() + " to KTX"); return; @@ -211,19 +211,19 @@ void TextureBaker::processTexture() { // Uncompressed KTX if (_textureType == image::TextureUsage::Type::SKY_TEXTURE || _textureType == image::TextureUsage::Type::AMBIENT_TEXTURE) { buffer->reset(); - auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE, - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing); - if (!processedTexture) { + auto processedTextureAndSize = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE, + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing); + if (!processedTextureAndSize.first) { handleError("Could not process texture " + _textureURL.toString()); return; } - processedTexture->setSourceHash(hash); + processedTextureAndSize.first->setSourceHash(hash); if (shouldStop()) { return; } - auto memKTX = gpu::Texture::serialize(*processedTexture); + auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second); if (!memKTX) { handleError("Could not serialize " + _textureURL.toString() + " to KTX"); return; diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index dd6381953f..4d372b17e5 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -63,8 +63,8 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint if (!_textureIsLoaded) { emit requestRenderUpdate(); if (nextTextureLoaded) { - float width = _texture->getWidth(); - float height = _texture->getHeight(); + float width = _texture->getOriginalWidth(); + float height = _texture->getOriginalHeight(); glm::vec3 naturalDimensions = glm::vec3(1.0f, 1.0f, 0.01f); if (width < height) { naturalDimensions.x = width / height; @@ -116,13 +116,15 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); + float originalWidth = _texture->getOriginalWidth(); + float originalHeight = _texture->getOriginalHeight(); QRect fromImage; if (_subImage.width() <= 0) { fromImage.setX(0); fromImage.setWidth(imageWidth); } else { - float scaleX = imageWidth / _texture->getOriginalWidth(); + float scaleX = imageWidth / originalWidth; fromImage.setX(scaleX * _subImage.x()); fromImage.setWidth(scaleX * _subImage.width()); } @@ -131,7 +133,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { fromImage.setY(0); fromImage.setHeight(imageHeight); } else { - float scaleY = imageHeight / _texture->getOriginalHeight(); + float scaleY = imageHeight / originalHeight; fromImage.setY(scaleY * _subImage.y()); fromImage.setHeight(scaleY * _subImage.height()); } @@ -141,7 +143,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { if (_keepAspectRatio) { glm::vec3 scale = transform.getScale(); - float targetAspectRatio = imageWidth / imageHeight; + float targetAspectRatio = originalWidth / originalHeight; float currentAspectRatio = scale.x / scale.y; if (targetAspectRatio < currentAspectRatio) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 54c7d49421..a525cda1ab 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -579,11 +579,11 @@ public: ExternalUpdates getUpdates() const; // Serialize a texture into a KTX file - static ktx::KTXUniquePointer serialize(const Texture& texture); + static ktx::KTXUniquePointer serialize(const Texture& texture, const glm::ivec2& originalSize); - static TexturePointer build(const ktx::KTXDescriptor& descriptor); - static TexturePointer unserialize(const std::string& ktxFile); - static TexturePointer unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string()); + static std::pair build(const ktx::KTXDescriptor& descriptor); + static std::pair unserialize(const std::string& ktxFile); + static std::pair unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string()); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index a5cea3e60e..9f9955c5a8 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -13,6 +13,7 @@ #include "Texture.h" #include +#include #include @@ -30,15 +31,16 @@ struct GPUKTXPayload { using Version = uint8; static const std::string KEY; - static const Version CURRENT_VERSION { 1 }; + static const Version CURRENT_VERSION { 2 }; static const size_t PADDING { 2 }; - static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + PADDING }; - static_assert(GPUKTXPayload::SIZE == 36, "Packing size may differ between platforms"); + static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + sizeof(glm::ivec2) + PADDING }; + static_assert(GPUKTXPayload::SIZE == 44, "Packing size may differ between platforms"); static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned"); Sampler::Desc _samplerDesc; Texture::Usage _usage; TextureUsageType _usageType; + glm::ivec2 _originalSize; Byte* serialize(Byte* data) const { *(Version*)data = CURRENT_VERSION; @@ -56,6 +58,9 @@ struct GPUKTXPayload { memcpy(data, &_usageType, sizeof(TextureUsageType)); data += sizeof(TextureUsageType); + memcpy(data, glm::value_ptr(_originalSize), sizeof(glm::ivec2)); + data += sizeof(glm::ivec2); + return data + PADDING; } @@ -66,13 +71,7 @@ struct GPUKTXPayload { Version version = *(const Version*)data; if (version != CURRENT_VERSION) { - glm::vec4 borderColor(1.0f); - if (memcmp(&borderColor, data, sizeof(glm::vec4)) == 0) { - memcpy(this, data, sizeof(GPUKTXPayload)); - return true; - } else { - return false; - } + return false; } data += sizeof(Version); @@ -87,6 +86,10 @@ struct GPUKTXPayload { data += sizeof(uint32); memcpy(&_usageType, data, sizeof(TextureUsageType)); + data += sizeof(TextureUsageType); + + memcpy(&_originalSize, data, sizeof(glm::ivec2)); + data += sizeof(glm::ivec2); return true; } @@ -382,7 +385,7 @@ void Texture::setKtxBacking(const cache::FilePointer& cacheEntry) { } -ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { +ktx::KTXUniquePointer Texture::serialize(const Texture& texture, const glm::ivec2& originalSize) { ktx::Header header; // From texture format to ktx format description @@ -459,6 +462,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { gpuKeyval._samplerDesc = texture.getSampler().getDesc(); gpuKeyval._usage = texture.getUsage(); gpuKeyval._usageType = texture.getUsageType(); + gpuKeyval._originalSize = originalSize; Byte keyvalPayload[GPUKTXPayload::SIZE]; gpuKeyval.serialize(keyvalPayload); @@ -514,19 +518,19 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { +std::pair Texture::build(const ktx::KTXDescriptor& descriptor) { Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; const auto& header = descriptor.header; if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) { - return nullptr; + return { nullptr, { 0, 0 } }; } // Find Texture Type based on dimensions Type type = TEX_1D; if (header.pixelWidth == 0) { - return nullptr; + return { nullptr, { 0, 0 } }; } else if (header.pixelHeight == 0) { type = TEX_1D; } else if (header.pixelDepth == 0) { @@ -569,39 +573,39 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { texture->overrideIrradiance(std::make_shared(irradianceKtxKeyValue._irradianceSH)); } - return texture; + return { texture, gpuktxKeyValue._originalSize }; } -TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) { +std::pair Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) { std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(cacheEntry->getFilepath().c_str())); if (!ktxPointer) { - return nullptr; + return { nullptr, { 0, 0 } }; } - auto texture = build(ktxPointer->toDescriptor()); - if (texture) { - texture->setKtxBacking(cacheEntry); - if (texture->source().empty()) { - texture->setSource(source); + auto textureAndSize = build(ktxPointer->toDescriptor()); + if (textureAndSize.first) { + textureAndSize.first->setKtxBacking(cacheEntry); + if (textureAndSize.first->source().empty()) { + textureAndSize.first->setSource(source); } } - return texture; + return { textureAndSize.first, textureAndSize.second }; } -TexturePointer Texture::unserialize(const std::string& ktxfile) { +std::pair Texture::unserialize(const std::string& ktxfile) { std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(ktxfile.c_str())); if (!ktxPointer) { - return nullptr; + return { nullptr, { 0, 0 } }; } - auto texture = build(ktxPointer->toDescriptor()); - if (texture) { - texture->setKtxBacking(ktxfile); - texture->setSource(ktxfile); + auto textureAndSize = build(ktxPointer->toDescriptor()); + if (textureAndSize.first) { + textureAndSize.first->setKtxBacking(ktxfile); + textureAndSize.first->setSource(ktxfile); } - return texture; + return { textureAndSize.first, textureAndSize.second }; } bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { diff --git a/libraries/image/src/image/TextureProcessing.cpp b/libraries/image/src/image/TextureProcessing.cpp index 53991ae431..1b45fc1bae 100644 --- a/libraries/image/src/image/TextureProcessing.cpp +++ b/libraries/image/src/image/TextureProcessing.cpp @@ -338,9 +338,9 @@ void mapToRedChannel(Image& image, ColorChannel sourceChannel) { } } -gpu::TexturePointer processImage(std::shared_ptr content, const std::string& filename, ColorChannel sourceChannel, - int maxNumPixels, TextureUsage::Type textureType, - bool compress, BackendTarget target, const std::atomic& abortProcessing) { +std::pair processImage(std::shared_ptr content, const std::string& filename, ColorChannel sourceChannel, + int maxNumPixels, TextureUsage::Type textureType, + bool compress, BackendTarget target, const std::atomic& abortProcessing) { Image image = processRawImageData(*content.get(), filename); // Texture content can take up a lot of memory. Here we release our ownership of that content @@ -354,7 +354,7 @@ gpu::TexturePointer processImage(std::shared_ptr content, const std:: if (imageWidth == 0 || imageHeight == 0 || image.getFormat() == Image::Format_Invalid) { QString reason(image.getFormat() == Image::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)"); qCWarning(imagelogging) << "Failed to load:" << qPrintable(reason); - return nullptr; + return { nullptr, { imageWidth, imageHeight } }; } // Validate the image is less than _maxNumPixels, and downscale if necessary @@ -378,7 +378,7 @@ gpu::TexturePointer processImage(std::shared_ptr content, const std:: auto loader = TextureUsage::getTextureLoaderForType(textureType); auto texture = loader(std::move(image), filename, compress, target, abortProcessing); - return texture; + return { texture, { imageWidth, imageHeight } }; } Image processSourceImage(Image&& srcImage, bool cubemap, BackendTarget target) { diff --git a/libraries/image/src/image/TextureProcessing.h b/libraries/image/src/image/TextureProcessing.h index 326ec700a9..decb940fda 100644 --- a/libraries/image/src/image/TextureProcessing.h +++ b/libraries/image/src/image/TextureProcessing.h @@ -121,9 +121,9 @@ gpu::TexturePointer processCubeTextureColorFromImage(Image&& srcImage, const std const QStringList getSupportedFormats(); -gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, ColorChannel sourceChannel, - int maxNumPixels, TextureUsage::Type textureType, - bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing = false); +std::pair processImage(std::shared_ptr content, const std::string& url, ColorChannel sourceChannel, + int maxNumPixels, TextureUsage::Type textureType, + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing = false); void convertToTextureWithMips(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic& abortProcessing = false, int face = -1); void convertToTexture(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic& abortProcessing = false, int face = -1, int mipLevel = 0); diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 64c3abb0d8..e4e36cad48 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -266,23 +266,24 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash()(extra)).staticCast(); } -gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { - std::weak_ptr weakPointer; +std::pair TextureCache::getTextureByHash(const std::string& hash) { + std::pair weakPointer; { std::unique_lock lock(_texturesByHashesMutex); weakPointer = _texturesByHashes[hash]; } - return weakPointer.lock(); + return { weakPointer.first.lock(), weakPointer.second }; } -gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) { - gpu::TexturePointer result; +std::pair TextureCache::cacheTextureByHash(const std::string& hash, const std::pair& textureAndSize) { + std::pair result; { std::unique_lock lock(_texturesByHashesMutex); - result = _texturesByHashes[hash].lock(); - if (!result) { - _texturesByHashes[hash] = texture; - result = texture; + auto& value = _texturesByHashes[hash]; + result = { value.first.lock(), value.second }; + if (!result.first) { + _texturesByHashes[hash] = textureAndSize; + result = textureAndSize; } } return result; @@ -616,7 +617,7 @@ void NetworkTexture::makeLocalRequest() { ktxDescriptor = std::make_shared(ktxFile->toDescriptor()); } - gpu::TexturePointer texture; + std::pair textureAndSize; if (ktxDescriptor) { std::string hash; // Create bare ktx in memory @@ -634,18 +635,18 @@ void NetworkTexture::makeLocalRequest() { } auto textureCache = DependencyManager::get(); - texture = textureCache->getTextureByHash(hash); - if (!texture) { - texture = gpu::Texture::build(*ktxDescriptor); - if (texture) { - texture->setKtxBacking(path.toStdString()); - texture->setSource(path.toStdString()); - texture = textureCache->cacheTextureByHash(hash, texture); + textureAndSize = textureCache->getTextureByHash(hash); + if (!textureAndSize.first) { + textureAndSize = gpu::Texture::build(*ktxDescriptor); + if (textureAndSize.first) { + textureAndSize.first->setKtxBacking(path.toStdString()); + textureAndSize.first->setSource(path.toStdString()); + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); } } } - if (!texture) { + if (!textureAndSize.first) { qCDebug(networking).noquote() << "Failed load local KTX from" << path; QMetaObject::invokeMethod(this, "setImage", Q_ARG(gpu::TexturePointer, nullptr), @@ -655,11 +656,11 @@ void NetworkTexture::makeLocalRequest() { } _ktxResourceState = PENDING_MIP_REQUEST; - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + _lowestKnownPopulatedMip = textureAndSize.first->minAvailableMipLevel(); QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); } @@ -968,22 +969,22 @@ void NetworkTexture::handleFinishedInitialLoad() { auto textureCache = DependencyManager::get(); - gpu::TexturePointer texture = textureCache->getTextureByHash(hash); + std::pair textureAndSize = textureCache->getTextureByHash(hash); - if (!texture) { + if (!textureAndSize.first) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); - if (texture->source().empty()) { - texture->setSource(url.toString().toStdString()); + textureAndSize = gpu::Texture::unserialize(ktxFile); + if (textureAndSize.first) { + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); + if (textureAndSize.first->source().empty()) { + textureAndSize.first->setSource(url.toString().toStdString()); } } } } - if (!texture) { + if (!textureAndSize.first) { auto memKtx = ktx::KTX::createBare(*header, keyValues); if (!memKtx) { qWarning() << " Ktx could not be created, bailing"; @@ -1010,9 +1011,9 @@ void NetworkTexture::handleFinishedInitialLoad() { auto newKtxDescriptor = memKtx->toDescriptor(); - texture = gpu::Texture::build(newKtxDescriptor); - texture->setKtxBacking(file); - texture->setSource(filename); + textureAndSize = gpu::Texture::build(newKtxDescriptor); + textureAndSize.first->setKtxBacking(file); + textureAndSize.first->setSource(filename); auto& images = originalKtxDescriptor->images; size_t imageSizeRemaining = ktxHighMipData.size(); @@ -1025,7 +1026,7 @@ void NetworkTexture::handleFinishedInitialLoad() { break; } ktxData -= image._imageSize; - texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); + textureAndSize.first->assignStoredMip(static_cast(level), image._imageSize, ktxData); ktxData -= ktx::IMAGE_SIZE_WIDTH; imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); } @@ -1033,13 +1034,13 @@ void NetworkTexture::handleFinishedInitialLoad() { // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner - texture = textureCache->cacheTextureByHash(filename, texture); + textureAndSize = textureCache->cacheTextureByHash(filename, textureAndSize); } QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); }); @@ -1229,15 +1230,15 @@ void ImageReader::read() { auto textureCache = DependencyManager::get(); if (textureCache) { // If we already have a live texture with the same hash, use it - auto texture = textureCache->getTextureByHash(hash); + auto textureAndSize = textureCache->getTextureByHash(hash); // If there is no live texture, check if there's an existing KTX file - if (!texture) { + if (!textureAndSize.first) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString()); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); + textureAndSize = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString()); + if (textureAndSize.first) { + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); } else { qCWarning(materialnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating..."; } @@ -1246,17 +1247,17 @@ void ImageReader::read() { // If we found the texture either because it's in use or via KTX deserialization, // set the image and return immediately. - if (texture) { + if (textureAndSize.first) { QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); return; } } // Proccess new texture - gpu::TexturePointer texture; + std::pair textureAndSize; { PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0); @@ -1269,23 +1270,23 @@ void ImageReader::read() { constexpr bool shouldCompress = false; #endif auto target = getBackendTarget(); - texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); + textureAndSize = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); - if (!texture) { + if (!textureAndSize.first) { QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), + Q_ARG(gpu::TexturePointer, textureAndSize.first), Q_ARG(int, 0), Q_ARG(int, 0)); return; } - texture->setSourceHash(hash); - texture->setFallbackTexture(networkTexture->getFallbackTexture()); + textureAndSize.first->setSourceHash(hash); + textureAndSize.first->setFallbackTexture(networkTexture->getFallbackTexture()); } // Save the image into a KTXFile - if (texture && textureCache) { - auto memKtx = gpu::Texture::serialize(*texture); + if (textureAndSize.first && textureCache) { + auto memKtx = gpu::Texture::serialize(*textureAndSize.first, textureAndSize.second); // Move the texture into a memory mapped file if (memKtx) { @@ -1294,20 +1295,20 @@ void ImageReader::read() { auto& ktxCache = textureCache->_ktxCache; auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length)); if (file) { - texture->setKtxBacking(file); + textureAndSize.first->setKtxBacking(file); } } // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner - texture = textureCache->cacheTextureByHash(hash, texture); + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); } QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); } NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) { diff --git a/libraries/material-networking/src/material-networking/TextureCache.h b/libraries/material-networking/src/material-networking/TextureCache.h index 754263566b..aab458e184 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.h +++ b/libraries/material-networking/src/material-networking/TextureCache.h @@ -183,8 +183,8 @@ public: const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, image::ColorChannel sourceChannel = image::ColorChannel::NONE); - gpu::TexturePointer getTextureByHash(const std::string& hash); - gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); + std::pair getTextureByHash(const std::string& hash); + std::pair cacheTextureByHash(const std::string& hash, const std::pair& textureAndSize); NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl); const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height); @@ -226,7 +226,7 @@ private: std::shared_ptr _ktxCache { std::make_shared(KTX_DIRNAME, KTX_EXT) }; // Map from image hashes to texture weak pointers - std::unordered_map> _texturesByHashes; + std::unordered_map, glm::ivec2>> _texturesByHashes; std::mutex _texturesByHashesMutex; gpu::TexturePointer _permutationNormalTexture; From 6f7bfbc053b35cee123004541d6c56fab8afde4d Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Sun, 21 Mar 2021 03:01:28 -0400 Subject: [PATCH 78/86] Update Pal.qml --- interface/resources/qml/hifi/Pal.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index d35ae5ea90..15425ec15c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -644,7 +644,7 @@ Rectangle { width: 32; height: 32; onClicked: { - Users[styleData.role](model.sessionId, Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP); // Ban params add up to 7 from Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP + Users[styleData.role](model.sessionId, Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP); UserActivityLogger["palAction"](styleData.role, model.sessionId); if (styleData.role === "kick") { nearbyUserModelData.splice(model.userIndex, 1); From 12d5687c6a6c7cccde6d06e4dca3d8010b7e054a Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Sun, 21 Mar 2021 13:13:24 -0400 Subject: [PATCH 79/86] Update libraries/script-engine/src/UsersScriptingInterface.h Co-authored-by: David Rowe --- libraries/script-engine/src/UsersScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index a02553eca8..0b3e9dc683 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -127,7 +127,7 @@ public slots: *

This function only works if you're an administrator of the domain you're in.

* @function Users.kick * @param {Uuid} sessionID - The session ID of the user to kick and ban. - * @param {number} - Preferred ban flags. Bans a user by username (if available) and machine fingerprint by default. + * @param {BanFlags} - Preferred ban flags. Bans a user by username (if available) and machine fingerprint by default. */ void kick(const QUuid& nodeID, unsigned int banFlags = ModerationFlags::getDefaultBanFlags()); From cfac34bf7e7bc60d23b8ece6569875976802f313 Mon Sep 17 00:00:00 2001 From: Kalila L Date: Sun, 21 Mar 2021 13:20:05 -0400 Subject: [PATCH 80/86] CR. --- libraries/script-engine/src/UsersScriptingInterface.h | 8 ++++---- libraries/shared/src/ModerationFlags.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 0b3e9dc683..79c0f4b61d 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -33,10 +33,10 @@ * false. Read-only. * @property {boolean} requestsDomainListData - true if the client requests extra data from the mixers (such as * positional data of an avatar they've ignored). Read-only. - * @property {BanFlags} NO_BAN - Do not ban person. Read-only. - * @property {BanFlags} BAN_BY_USERNAME - Ban person by username. Read-only. - * @property {BanFlags} BAN_BY_FINGERPRINT - Ban person by fingerprint. Read-only. - * @property {BanFlags} BAN_BY_IP - Ban person by IP address. Read-only. + * @property {BanFlags} NO_BAN - Do not ban user. Read-only. + * @property {BanFlags} BAN_BY_USERNAME - Ban user by username. Read-only. + * @property {BanFlags} BAN_BY_FINGERPRINT - Ban user by fingerprint. Read-only. + * @property {BanFlags} BAN_BY_IP - Ban user by IP address. Read-only. */ class UsersScriptingInterface : public QObject, public Dependency { Q_OBJECT diff --git a/libraries/shared/src/ModerationFlags.h b/libraries/shared/src/ModerationFlags.h index 9bdd4bfbea..a8390873d7 100644 --- a/libraries/shared/src/ModerationFlags.h +++ b/libraries/shared/src/ModerationFlags.h @@ -42,4 +42,4 @@ public: static constexpr unsigned int getDefaultBanFlags() { return (BanFlags::BAN_BY_USERNAME | BanFlags::BAN_BY_FINGERPRINT); }; }; -#endif // vircadia_ModerationFlags_h \ No newline at end of file +#endif // vircadia_ModerationFlags_h From e5f108b0225f0219c95d9d3c0230f702da127fbc Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 21 Mar 2021 14:51:21 -0700 Subject: [PATCH 81/86] bump ktx version to force clear cache --- .../material-networking/src/material-networking/KTXCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/material-networking/src/material-networking/KTXCache.cpp b/libraries/material-networking/src/material-networking/KTXCache.cpp index edb09addb8..5a5a94dcc9 100644 --- a/libraries/material-networking/src/material-networking/KTXCache.cpp +++ b/libraries/material-networking/src/material-networking/KTXCache.cpp @@ -19,7 +19,7 @@ using FilePointer = cache::FilePointer; // Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible, // this value should be incremented. This will force the KTX cache to be wiped -const int KTXCache::CURRENT_VERSION = 0x01; +const int KTXCache::CURRENT_VERSION = 0x02; const int KTXCache::INVALID_VERSION = 0x00; const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version"; From 6b82c7fe907bef76117037d2a9333bbe8bf67652 Mon Sep 17 00:00:00 2001 From: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com> Date: Mon, 22 Mar 2021 18:37:29 -0700 Subject: [PATCH 82/86] Update RenderableImageEntityItem.cpp --- libraries/entities-renderer/src/RenderableImageEntityItem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 4d372b17e5..c0bbc0603a 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -71,6 +71,8 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint } else { naturalDimensions.y = height / width; } + // Unlike Models (where the Renderer also doubles as the EntityItem), Images need to + // convey this information back to the game object from the Renderer entity->setNaturalDimension(naturalDimensions); } } From 62a9299b1e14ad1c31eee874b010ada5e3457ae0 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Fri, 26 Mar 2021 06:50:13 -0400 Subject: [PATCH 83/86] Activate stale bot This bot will help triage issues (or so I think) in accordance with the configuration file defined here. --- .github/stale.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..c4919d8aa6 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 10 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: question / review +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had recent activity. + It will be closed if no further activity occurs. + Thank you for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false From 5eadda5be28e3a75f6fa80c8cfa0ac6b18cc1b41 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Fri, 26 Mar 2021 15:40:00 -0400 Subject: [PATCH 84/86] Update stale.yml --- .github/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index c4919d8aa6..2cb1242bd4 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,13 +1,13 @@ # Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed -daysUntilClose: 10 +daysUntilClose: 21 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale -staleLabel: question / review +staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. From 326e23a1da29e08b945e4059b666a8427afbe3f7 Mon Sep 17 00:00:00 2001 From: Kalila <69767640+digisomni@users.noreply.github.com> Date: Sat, 27 Mar 2021 00:40:48 -0400 Subject: [PATCH 85/86] Update stale.yml --- .github/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 2cb1242bd4..d5c79b8504 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ # Number of days of inactivity before an issue becomes stale -daysUntilStale: 60 +daysUntilStale: 120 # Number of days of inactivity before a stale issue is closed -daysUntilClose: 21 +daysUntilClose: 60 # Issues with these labels will never be considered stale exemptLabels: - pinned From 6a7f4a042610cdac7f295f155128f0b079ff9e45 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 29 Mar 2021 21:14:00 +1300 Subject: [PATCH 86/86] Redisplay "continue without avatar entities" when re-enter address --- libraries/networking/src/AddressManager.cpp | 19 +++++++++++++++++++ libraries/networking/src/AddressManager.h | 8 ++++---- libraries/networking/src/DomainHandler.h | 4 ++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 76587118f1..f0fa1365c4 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -415,6 +415,8 @@ void AddressManager::handleLookupString(const QString& lookupString, bool fromSu QString sanitizedString = lookupString.trimmed(); if (!sanitizedString.isEmpty()) { + resetConfirmConnectWithoutAvatarEntities(); + // make this a valid hifi URL and handle it off to handleUrl handleUrl(sanitizedString, fromSuggestions ? Suggestions : UserInput); } @@ -874,6 +876,11 @@ bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) return emitHostChanged; } +void AddressManager::goToEntry(LookupTrigger trigger) { + resetConfirmConnectWithoutAvatarEntities(); + handleUrl(DEFAULT_VIRCADIA_ADDRESS, trigger); +} + void AddressManager::goToUser(const QString& username, bool shouldMatchOrientation) { QString formattedUsername = QUrl::toPercentEncoding(username); @@ -890,6 +897,11 @@ void AddressManager::goToUser(const QString& username, bool shouldMatchOrientati QByteArray(), nullptr, requestParams); } +void AddressManager::goToLastAddress() { + resetConfirmConnectWithoutAvatarEntities(); + handleUrl(_lastVisitedURL, LookupTrigger::AttemptedRefresh); +} + bool AddressManager::canGoBack() const { return (_backStack.size() > 0); } @@ -1024,3 +1036,10 @@ QString AddressManager::getPlaceName() const { } return QString(); } + +void AddressManager::resetConfirmConnectWithoutAvatarEntities() { + DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); + if (!domainHandler.isConnected()) { + domainHandler.resetConfirmConnectWithoutAvatarEntities(); + } +} diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index daca6d3392..f491e59b97 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -301,9 +301,7 @@ public slots: * @param {location.LookupTrigger} trigger=StartupFromSettings - The reason for the function call. Helps ensure that user's * location history is correctly maintained. */ - void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { - handleUrl(DEFAULT_VIRCADIA_ADDRESS, trigger); - } + void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings); /**jsdoc * Takes you to the specified user's location. @@ -318,7 +316,7 @@ public slots: * Takes you to the last address tried. This will be the last URL tried from location.handleLookupString. * @function location.goToLastAddress */ - void goToLastAddress() { handleUrl(_lastVisitedURL, LookupTrigger::AttemptedRefresh); } + void goToLastAddress(); /**jsdoc * Checks if going back to the previous location is possible. @@ -527,6 +525,8 @@ private: void addCurrentAddressToHistory(LookupTrigger trigger); + void resetConfirmConnectWithoutAvatarEntities(); + QUrl _domainURL; QUrl _lastVisitedURL; diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 923b5913e7..fd188ecec0 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -163,6 +163,10 @@ public: bool checkInPacketTimeout(); void clearPendingCheckins() { _checkInPacketsSinceLastReply = 0; } + void resetConfirmConnectWithoutAvatarEntities() { + _haveAskedConnectWithoutAvatarEntities = false; + } + /**jsdoc *

The reasons that you may be refused connection to a domain are defined by numeric values:

*